# -*- coding: utf-8 -*-
# elements.py

# Copyright (c) 2005, Christoph Gohlke
# Copyright (c) 2006-2008, The Regents of the University of California
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
# * Neither the name of the copyright holders nor the names of any
#   contributors may be used to endorse or promote products derived
#   from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Properties of the Chemical Elements.

:Author:
    `Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`__,
    Laboratory for Fluorescence Dynamics, University of California, Irvine

:Version: 20080123

Requirements
------------

* `Python 2.5 <http://www.python.org>`__

References
----------

(1) http://physics.nist.gov/PhysRefData/Compositions/
(2) http://physics.nist.gov/PhysRefData/IonEnergy/tblNew.html
(3) http://en.wikipedia.org/wiki/%(element.name)s

Examples
--------

>>> from elements import ELEMENTS
>>> mass = 0.0
>>> for symbol in 'HBCNOFPSKVYIU':
...     mass += ELEMENTS[symbol].mass
>>> assert(abs(mass-680.) < 1.)
>>> mass = 0.0
>>> for ele in ELEMENTS:
...     mass += ele.mass
>>> assert(abs(mass-14659.)<1.)
>>> for ele in ELEMENTS:
...     _ = ele.export('python')

"""

__docformat__ = "restructuredtext en"


class Element(object):
    """Chemical Element.

    Instance Attributes
    -------------------

    number : int
        Atomic number
    symbol : string of length 1 or 2
        Chemical symbol
    name : string
        Name in english
    group : int
        Group in the periodic table
    period : int
        Period in the periodic table
    block : int
        Block in the periodic table
    protons : int
        Number of protons
    neutron : int
        Number of neutrons in the most abundant isotope
    electrons : int
        Number of electrons
    mass : float
        Relative atomic mass
    en : float
        Electronegativity (Pauling scale)
    covrad : float
        Covalent radius in A
    atmrad :
        Atomic radius in A
    vdwrad : float
        Van der Waals radius in A
    tboil : float
        Boiling temperature in K
    tmelt : float
        Melting temperature in K
    density : dloat
        Density at 295K (g/cm^3 respectively g/L)
    phase : string
        Phase at 295K. 'solid/liquid' or 'gas'
    acidity : string
        Acidic behaviour: acidic, basic, neutral, amphoteric
    oxistates : string
        Oxidation states
    eleaffin : float
        Electron affinity in eV
    eleshells : int
        Number of electrons per shell
    eleconfig : string
        Ground state electron configuration
    ionenergy : sequence
        Ionization energies in eV
    isotopes : sequence of tuples
        Isotopes mass number, relative atomic mass, and fraction
    maxiso : int
        Index of the most abundant isotope
    puremass : float
        Relative atomic mass of the most abundant isotope
    purefrac : float
        Fraction of the most abundant isotope

    """

    def __init__(self, number, symbol, name, **kwargs):
        self.number = number
        self.symbol = symbol
        self.name = name

        self.__dict__.update(kwargs)

        # update registries
        ELEMENTS.add(self)
        GROUPS.add(self)
        PERIODS.add(self)
        BLOCKS.add(self)

        # properties the most abundant isotope
        mass = 0.0
        frac = 0.0
        maxf = 0
        for i, (n, m, f) in enumerate(self.isotopes):
            mass += m*f
            frac += f
            if f > maxf:
                maxf = f
                self.isomax = i
                self.puremass = m
                self.purefrac = f
                self.neutrons = n - number

        self.protons = number
        self.electrons = number
        self.massnumber = self.protons+self.neutrons

        assert(abs(frac-1.0) < 1e-9,
            "%s - Isotope fractions must add up to 1.0" % self.symbol)
        assert(abs(mass-self.mass) < 0.03,
            "%s - Mean of isotope masses must equal mass" % self.symbol)
        assert(self.number == self.protons,
            "%s - Atomic number must equal number of protons" % self.symbol)
        assert(self.protons == sum(self.eleshells),
            "%s - Number of protons must equal electrons" % self.symbol)

    def __str__(self):
        return self.symbol

    def add(self, **kwargs):
        """Add any properties."""
        self.__dict__.update(kwargs)

    def export(self, format='text'):
        """Return element properties in specifed format."""
        if format == 'xml':
            raise NotImplementedError()
        elif format == 'list':
            result = [self.symbol, self.name]
        elif format == 'python':
            result = """%(symbol)s = Element(""" \
    """%(number)s, '%(symbol)s', '%(name)s',
    group=%(group)s, period=%(period)s, block='%(block)s',
    mass=%(mass)s, en=%(en)s,
    covrad=%(covrad).3f, atmrad=%(atmrad).3f, vdwrad=%(vdwrad).3f,
    tboil=%(tboil).3f, tmelt=%(tmelt).3f, density=%(density).4f,
    phase='%(phase)s', acidity='%(acidity)s',
    eleaffin=%(eleaffin).8f,
    eleshells=%(eleshells)s,
    eleconfig='%(eleconfig)s',
    oxistates='%(oxistates)s',
    ionenergy=(%%s),
    isotopes=(%%s), )\n""" % self.__dict__
            ion = []
            for i, j in enumerate(self.ionenergy):
                if i and (i%4 == 0):
                    ion.append("\n" + " "*15)
                ion.append("%.4f, " % j)
            iso = []
            for i in self.isotopes:
                iso.append("(%i, %.10f, %.8f), " % (i[0], i[1], i[2]))
            result = result % ("".join(ion), "\n              ".join(iso))
        else:
            result = '\n'.join([
            "%-22s : %s" % ("Name", self.name),
            "%-22s : %s" % ("Symbol", self.symbol),
            "%-22s : %i" % ("Atomic Number", self.number),
            "%-22s : %i" % ("Group", self.group),
            "%-22s : %i" % ("Period", self.period),
            "%-22s : %s" % ("Block", self.block),
            "%-22s : %.10g" % ("Atomic Weight", self.mass),
            "%-22s : %.10g" % ("Atomic Radius", self.atmrad),
            "%-22s : %.10g" % ("Covalent Radius", self.covrad),
            "%-22s : %.10g" % ("Van der Waals Radius", self.vdwrad),
            "%-22s : %.10g" % ("Electronegativity", self.en),
            "%-22s : %s" % ("Electron Configuration", self.eleconfig),
            "%-22s : %s" % ("Electron per Shell", ', '.join(
                "%i" % i for i in self.eleshells)),
            "%-22s : %s" % ("Oxidation States", self.oxistates),
            "%-22s : %s" % ("Isotopes", ', '.join(
                "(%i, %.10g, %.10g)" % (a, b, c*100.0) for a, b, c in \
                                                               self.isotopes)),
            "%-22s : %s" % ("Ionization Potentials", ', '.join(
                "%.10g" % ion for ion in self.ionenergy)),
            ""])
        return result


class _Elements(object):
    """List of Chemical Elements."""

    def __init__(self):
        self.__list = []
        self.__dict = {}

    def add(self, element):
        """Add element."""
        self.__list.append(element)
        self.__dict[element.number] = element
        self.__dict[element.symbol] = element
        self.__dict[element.name] = element

    def __str__(self):
        return "[%s]" % ", ".join(e.symbol for e in self.__list)

    def __contains__(self, item):
        return self.__dict.__contains__(item)

    def __iter__(self):
        return iter(self.__list)

    def __len__(self):
        return len(self.__list)

    def __getitem__(self, key):
        return self.__dict[key]


class _Blocks(object):
    """List of Blocks of Chemical Elements."""

    def __init__(self):
        self.__dict = {'s': [], 'p': [], 'd': [], 'f': []}

    def add(self, element):
        """Add element."""
        self.__dict[element.block].append(element)

    def __str__(self):
        l = []
        for i in 'spdf':
            b = self.__dict[i]
            l.append("%2s [%s]" % (i, ", ".join(e.symbol for e in b)))
        return "\n".join(l)

    def __iter__(self):
        return iter(self.__dict[i] for i in 'spdf')

    def __getitem__(self, key):
        return self.__dict[key]


class _Periods(object):
    """List of Periods of Chemical Elements."""

    def __init__(self):
        self.__list = [[]]*8

    def add(self, element):
        """Add element."""
        self.__list[element.period].append(element)

    def __str__(self):
        l = []
        for i, g in enumerate(self.__list[1:]):
            l.append("%2i [%s]" % (i+1, ", ".join(e.symbol for e in g)))
        return "\n".join(l)

    def __iter__(self):
        return iter(self.__list[1:])

    def __getitem__(self, key):
        return self.__list[key]


class _Group(list):
    """Group of Chemical Elements."""

    def __init__(self, old, description):
        self.description = description
        self.oldname = old
        list.__init__(self)


class _Groups(object):
    """List of Groups of Chemical Elements."""

    __list = [[],
        _Group('IA', 'Alkali metals'),
        _Group('IIA', 'Alkaline earths'),
        _Group('IIIB', ''),
        _Group('IVB', ''),
        _Group('VB', ''),
        _Group('VIB', ''),
        _Group('VIIB', ''),
        _Group('VIIIB', ''),
        _Group('VIIIB', ''),
        _Group('VIIIB', ''),
        _Group('IB', 'Coinage Metals'),
        _Group('IIB', ''),
        _Group('IIIA', 'Boron Group'),
        _Group('IVA', 'Carbon Group'),
        _Group('VA', 'Pnictogens'),
        _Group('VIA', 'Chalcogens'),
        _Group('VIIA', 'Halogens'),
        _Group('VIIIA', 'Noble gases')]

    def add(self, element):
        """Add element."""
        self.__list[element.group].append(element)

    def __str__(self):
        l = []
        for i, g in enumerate(self.__list[1:]):
            l.append("%2i [%s]" % (i+1, ", ".join(e.symbol for e in g)))
        return "\n".join(l)

    def __iter__(self):
        return iter(self.__list[1:])

    def __getitem__(self, key):
        return self.__list[key]


ELEMENTS = _Elements()
GROUPS = _Groups()
PERIODS = _Periods()
BLOCKS = _Blocks()

H = Element(1, 'H', 'Hydrogen',
    group=1, period=1, block='s',
    mass=1.00794, en=2.2,
    covrad=0.320, atmrad=0.790, vdwrad=1.200,
    tboil=20.280, tmelt=13.810, density=0.0840,
    phase='gas', acidity='basic',
    eleaffin=0.75420375,
    eleshells=(1, ),
    eleconfig='1s',
    oxistates='1*, -1',
    ionenergy=(13.5984, ),
    isotopes=((1, 1.0078250321, 0.99988500),
              (2, 2.0141017780, 0.00011500), ), )

He = Element(2, 'He', 'Helium',
    group=18, period=1, block='s',
    mass=4.002602, en=0.0,
    covrad=0.930, atmrad=0.490, vdwrad=1.400,
    tboil=4.216, tmelt=0.950, density=0.1785,
    phase='gas', acidity='basic',
    eleaffin=0.00000000,
    eleshells=(2, ),
    eleconfig='1s^2',
    oxistates='*',
    ionenergy=(24.5874, 54.4160, ),
    isotopes=((3, 3.0160293097, 0.00000137),
              (4, 4.0026032497, 0.99999863), ), )

Li = Element(3, 'Li', 'Lithium',
    group=1, period=2, block='s',
    mass=6.941, en=0.98,
    covrad=1.230, atmrad=2.050, vdwrad=1.820,
    tboil=1615.000, tmelt=453.700, density=0.5300,
    phase='solid', acidity='acidic',
    eleaffin=0.61804900,
    eleshells=(2, 1),
    eleconfig='[He] 2s',
    oxistates='1*',
    ionenergy=(5.3917, 75.6380, 122.4510, ),
    isotopes=((6, 6.0151223000, 0.07590000),
              (7, 7.0160040000, 0.92410000), ), )

Be = Element(4, 'Be', 'Beryllium',
    group=2, period=2, block='s',
    mass=9.012182, en=1.57,
    covrad=0.900, atmrad=1.400, vdwrad=0.000,
    tboil=3243.000, tmelt=1560.000, density=1.8500,
    phase='solid', acidity='neutral',
    eleaffin=0.00000000,
    eleshells=(2, 2),
    eleconfig='[He] 2s^2',
    oxistates='2*',
    ionenergy=(9.3227, 18.2110, 153.8930, 217.7130, ),
    isotopes=((9, 9.0121821000, 1.00000000), ), )

B = Element(5, 'B', 'Boron',
    group=13, period=2, block='p',
    mass=10.811, en=2.04,
    covrad=0.820, atmrad=1.170, vdwrad=0.000,
    tboil=4275.000, tmelt=2365.000, density=2.4600,
    phase='solid', acidity='amphoteric',
    eleaffin=0.27972300,
    eleshells=(2, 3),
    eleconfig='[He] 2s^2 2p',
    oxistates='3*',
    ionenergy=(8.2980, 25.1540, 37.9300, 59.3680,
               340.2170, ),
    isotopes=((10, 10.0129370000, 0.19900000),
              (11, 11.0093055000, 0.80100000), ), )

C = Element(6, 'C', 'Carbon',
    group=14, period=2, block='p',
    mass=12.0107, en=2.55,
    covrad=0.770, atmrad=0.910, vdwrad=1.700,
    tboil=5100.000, tmelt=3825.000, density=3.5100,
    phase='solid', acidity='amphoteric',
    eleaffin=1.26211800,
    eleshells=(2, 4),
    eleconfig='[He] 2s^2 2p^2',
    oxistates='4*, 2, -4*',
    ionenergy=(11.2603, 24.3830, 47.8770, 64.4920,
               392.0770, 489.9810, ),
    isotopes=((12, 12.0000000000, 0.98930000),
              (13, 13.0033548378, 0.01070000), ), )

N = Element(7, 'N', 'Nitrogen',
    group=15, period=2, block='p',
    mass=14.0067, en=3.04,
    covrad=0.750, atmrad=0.750, vdwrad=1.550,
    tboil=77.344, tmelt=63.150, density=1.1700,
    phase='gas', acidity='amphoteric',
    eleaffin=-0.07000000,
    eleshells=(2, 5),
    eleconfig='[He] 2s^2 2p^3',
    oxistates='5, 4, 3, 2, -3*',
    ionenergy=(14.5341, 39.6010, 47.4880, 77.4720,
               97.8880, 522.0570, 667.0290, ),
    isotopes=((14, 14.0030740052, 0.99632000),
              (15, 15.0001088984, 0.00368000), ), )

O = Element(8, 'O', 'Oxygen',
    group=16, period=2, block='p',
    mass=15.9994, en=3.44,
    covrad=0.730, atmrad=0.650, vdwrad=1.520,
    tboil=90.188, tmelt=54.800, density=1.3300,
    phase='gas', acidity='amphoteric',
    eleaffin=1.46111200,
    eleshells=(2, 6),
    eleconfig='[He] 2s^2 2p^4',
    oxistates='-2*, -1',
    ionenergy=(13.6181, 35.1160, 54.9340, 54.9340,
               77.4120, 113.8960, 138.1160, 739.3150,
               871.3870, ),
    isotopes=((16, 15.9949146221, 0.99757000),
              (17, 16.9991315000, 0.00038000),
              (18, 17.9991604000, 0.00205000), ), )

F = Element(9, 'F', 'Fluorine',
    group=17, period=2, block='p',
    mass=18.9984032, en=3.98,
    covrad=0.720, atmrad=0.570, vdwrad=1.470,
    tboil=85.000, tmelt=53.550, density=1.5800,
    phase='gas', acidity='amphoteric',
    eleaffin=3.40118870,
    eleshells=(2, 7),
    eleconfig='[He] 2s^2 2p^5',
    oxistates='-1*',
    ionenergy=(17.4228, 34.9700, 62.7070, 87.1380,
               114.2400, 157.1610, 185.1820, 953.8860,
               1103.0890, ),
    isotopes=((19, 18.9984032000, 1.00000000), ), )

Ne = Element(10, 'Ne', 'Neon',
    group=18, period=2, block='p',
    mass=20.1797, en=0.0,
    covrad=0.710, atmrad=0.510, vdwrad=1.540,
    tboil=27.100, tmelt=24.550, density=0.8999,
    phase='gas', acidity='basic',
    eleaffin=0.00000000,
    eleshells=(2, 8),
    eleconfig='[He] 2s^2 2p^6',
    oxistates='*',
    ionenergy=(21.5645, 40.9620, 63.4500, 97.1100,
               126.2100, 157.9300, 207.2700, 239.0900,
               1195.7970, 1362.1640, ),
    isotopes=((20, 19.9924401759, 0.90480000),
              (21, 20.9938467400, 0.00270000),
              (22, 21.9913855100, 0.09250000), ), )

Na = Element(11, 'Na', 'Sodium',
    group=1, period=3, block='s',
    mass=22.98977, en=0.93,
    covrad=1.540, atmrad=2.230, vdwrad=2.270,
    tboil=1156.000, tmelt=371.000, density=0.9700,
    phase='solid', acidity='acidic',
    eleaffin=0.54792600,
    eleshells=(2, 8, 1),
    eleconfig='[Ne] 3s',
    oxistates='1*',
    ionenergy=(5.1391, 47.2860, 71.6400, 98.9100,
               138.3900, 172.1500, 208.4700, 264.1800,
               299.8700, 1465.0910, 1648.6590, ),
    isotopes=((23, 22.9897696700, 1.00000000), ), )

Mg = Element(12, 'Mg', 'Magnesium',
    group=2, period=3, block='s',
    mass=24.305, en=1.31,
    covrad=1.360, atmrad=1.720, vdwrad=1.730,
    tboil=1380.000, tmelt=922.000, density=1.7400,
    phase='solid', acidity='acidic',
    eleaffin=0.00000000,
    eleshells=(2, 8, 2),
    eleconfig='[Ne] 3s^2',
    oxistates='2*',
    ionenergy=(7.6462, 15.0350, 80.1430, 109.2400,
               141.2600, 186.5000, 224.9400, 265.9000,
               327.9500, 367.5300, 1761.8020, 1962.6130, ),
    isotopes=((24, 23.9850419000, 0.78990000),
              (25, 24.9858370200, 0.10000000),
              (26, 25.9825930400, 0.11010000), ), )

Al = Element(13, 'Al', 'Aluminium',
    group=13, period=3, block='p',
    mass=26.981538, en=1.61,
    covrad=1.180, atmrad=1.820, vdwrad=0.000,
    tboil=2740.000, tmelt=933.500, density=2.7000,
    phase='solid', acidity='neutral',
    eleaffin=0.43283000,
    eleshells=(2, 8, 3),
    eleconfig='[Ne] 3s^2 3p',
    oxistates='3*',
    ionenergy=(5.9858, 18.8280, 28.4470, 119.9900,
               153.7100, 190.4700, 241.4300, 284.5900,
               330.2100, 398.5700, 442.0700, 2085.9830,
               2304.0800, ),
    isotopes=((27, 26.9815384400, 1.00000000), ), )

Si = Element(14, 'Si', 'Silicon',
    group=14, period=3, block='p',
    mass=28.0855, en=1.9,
    covrad=1.110, atmrad=1.460, vdwrad=2.100,
    tboil=2630.000, tmelt=1683.000, density=2.3300,
    phase='solid', acidity='amphoteric',
    eleaffin=1.38952100,
    eleshells=(2, 8, 4),
    eleconfig='[Ne] 3s^2 3p^2',
    oxistates='4*, -4',
    ionenergy=(8.1517, 16.3450, 33.4920, 45.1410,
               166.7700, 205.0500, 246.5200, 303.1700,
               351.1000, 401.4300, 476.0600, 523.5000,
               2437.6760, 2673.1080, ),
    isotopes=((28, 27.9769265327, 0.92229700),
              (29, 28.9764947200, 0.04683200),
              (30, 29.9737702200, 0.03087100), ), )

P = Element(15, 'P', 'Phosphorus',
    group=15, period=3, block='p',
    mass=30.973761, en=2.19,
    covrad=1.060, atmrad=1.230, vdwrad=1.800,
    tboil=553.000, tmelt=317.300, density=1.8200,
    phase='solid', acidity='amphoteric',
    eleaffin=0.74650000,
    eleshells=(2, 8, 5),
    eleconfig='[Ne] 3s^2 3p^3',
    oxistates='5*, 3, -3',
    ionenergy=(10.4867, 19.7250, 30.1800, 51.3700,
               65.0230, 220.4300, 263.2200, 309.4100,
               371.7300, 424.5000, 479.5700, 560.4100,
               611.8500, 2816.9430, 3069.7620, ),
    isotopes=((31, 30.9737615100, 1.00000000), ), )

S = Element(16, 'S', 'Sulfur',
    group=16, period=3, block='p',
    mass=32.065, en=2.58,
    covrad=1.020, atmrad=1.090, vdwrad=1.800,
    tboil=717.820, tmelt=392.200, density=2.0600,
    phase='solid', acidity='amphoteric',
    eleaffin=2.07710290,
    eleshells=(2, 8, 6),
    eleconfig='[Ne] 3s^2 3p^4',
    oxistates='6*, 4, 2, -2',
    ionenergy=(10.3600, 23.3300, 34.8300, 47.3000,
               72.6800, 88.0490, 280.9300, 328.2300,
               379.1000, 447.0900, 504.7800, 564.6500,
               651.6300, 707.1400, 3223.8360, 3494.0990, ),
    isotopes=((32, 31.9720706900, 0.94930000),
              (33, 32.9714585000, 0.00760000),