# Copyright (C) 2011 Atsushi Togo
# All rights reserved.
#
# This file is part of phonopy.
#
# 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 phonopy project nor the names of its
# 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 HOLDER 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.
import numpy as np
[docs]class Atoms:
"""
Atoms class compatible with the ASE Atoms class
Only stuff needed by muesr is implemented.
>>> a = 4.05 # Gold lattice constant
>>> b = a / 2
>>> fcc = Atoms('Au',
... cell=[(0, b, b), (b, 0, b), (b, b, 0)],
... pbc=True)
"""
def __init__(self,
symbols=None,
positions=None,
numbers=None,
masses=None,
magmoms=None,
scaled_positions=None,
cell=None,
pbc=None):
# cell
if cell is None:
raise ValueError('Only periodic system allowed. Please specify unit cell.')
else:
self.cell = np.array(cell, dtype=float)
# position
self.scaled_positions = None
if (not self.cell is None) and (not positions is None):
self.set_positions(positions)
if (not scaled_positions is None):
self.set_scaled_positions(scaled_positions)
# Atom symbols
self.symbols = symbols
# Atomic numbers
if numbers is None:
self.numbers = None
else:
self.numbers = np.array(numbers, dtype=int)
# masses
self.set_masses(masses)
# (initial) magnetic moments
self.set_magnetic_moments(magmoms)
# number --> symbol
if not self.numbers is None:
self.numbers_to_symbols()
# symbol --> number
elif not self.symbols is None:
self.symbols_to_numbers()
# symbol --> mass
if self.symbols and (self.masses is None):
self.symbols_to_masses()
# Add an atom
# return atom index
[docs] def extend(self,
symbol=None,
position=None,
number=None,
mass=None,
magmom=None,
scaled_position=None):
"""
Extends the current atomic structure by one atom.
"""
# position
if (not self.cell is None) and (not position is None):
# append scaled position to positions
self.scaled_positions = np.concatenate(self.scaled_positions, np.dot(position,
np.linalg.inv(self.cell)))
if (not scaled_position is None):
#print ((self.scaled_positions, np.array([scaled_position])))
self.scaled_positions = np.concatenate((self.scaled_positions, np.array([scaled_position])))
if symbol is None and number is None:
raise ValueError
# Atom symbols
if symbol is None:
symbol = atom_data[number][1]
self.symbols.append(symbol)
# Atomic numbers
if number is None:
number = symbol_map[symbol]
self.numbers = np.append(self.numbers, number)
# (initial) magnetic moments
if not (self.magmoms is None):
if magmom is None:
magmom = np.array([0,0,0])
self.magmoms = np.concatenate((self.magmoms, [magmom]))
# symbol --> mass
if mass is None:
mass = atom_data[number][3]
self.masses = np.append(self.masses,mass)
return -1 # stands for last item!
[docs] def del_atom(self, i):
"""
Removes one atom
"""
if isinstance(i, int):
natoms = len(self)
if i < -natoms or i >= natoms:
raise IndexError('Index out of range.')
if not self.magmoms is None:
self.magmoms = np.delete(self.magmoms, i, 0)
self.symbols.pop(i) # = np.delete(self.symbols, i)
self.numbers = np.delete(self.numbers, i)
self.scaled_positions = np.delete(self.scaled_positions, i,0) # axis is needed for scaled pos
self.masses = np.delete(self.masses, i)
[docs] def edit_atom(self, i, symbol = None, number = None, magmom = None, mass = None):
if isinstance(i, int):
natoms = len(self)
if i < -natoms or i >= natoms:
raise IndexError('Index out of range.')
if symbol is None and number is None:
raise ValueError
# Atom symbols
if symbol is None:
symbol = atom_data[number][1]
self.symbols[i] = symbol
# Atomic numbers
if number is None:
number = symbol_map[symbol]
self.numbers[i] = number
# (initial) magnetic moments
if not (self.magmoms is None):
if magmom is None:
magmom = np.array([0,0,0])
self.magmoms[i] = magmom
# symbol --> mass
if mass is None:
mass = atom_data[number][3]
self.masses[i] = mass
[docs] def set_cell(self, cell):
"""
Set lattice vectors parameters.
:param cell: numpy array of the form
| [[v1(1), v1(2), v1(3)],
| v2(1), v2(2), v2(3)],
| v3(1), v3(2), v3(3) ]]
where v1, v2, v3 are the lattice vectors.
"""
self.cell = np.array(cell, dtype=float)
[docs] def get_cell(self):
return self.cell.copy()
[docs] def set_positions(self, cart_positions):
self.scaled_positions = np.dot(cart_positions,
np.linalg.inv(self.cell))
[docs] def get_positions(self):
return np.dot(self.scaled_positions, self.cell)
[docs] def set_scaled_positions(self, scaled_positions):
self.scaled_positions = np.array(scaled_positions, dtype=float)
[docs] def get_scaled_positions(self):
return self.scaled_positions.copy()
[docs] def set_masses(self, masses):
if masses is None:
self.masses = None
else:
self.masses = np.array(masses, dtype=float)
[docs] def get_masses(self):
return self.masses.copy()
[docs] def set_magnetic_moments(self, magmoms=None):
if magmoms is None:
self.magmoms = None
else:
self.magmoms = np.array(magmoms, dtype=float)
[docs] def get_magnetic_moments(self):
"""
Get magnetic moments if set. None if nothing set.
"""
if self.magmoms is None:
return None
else:
return self.magmoms.copy()
[docs] def set_chemical_symbols(self, symbols):
self.symbols = symbols
[docs] def get_chemical_symbols(self):
return self.symbols[:]
[docs] def get_number_of_atoms(self):
return len(self.scaled_positions)
[docs] def get_atomic_numbers(self):
return self.numbers.copy()
[docs] def numbers_to_symbols(self):
self.symbols = [atom_data[n][1] for n in self.numbers]
[docs] def symbols_to_numbers(self):
self.numbers = np.array([symbol_map[s.title()]
for s in self.symbols])
[docs] def symbols_to_masses(self):
self.masses = np.array([atom_data[symbol_map[s.title()]][3]
for s in self.symbols])
[docs] def get_volume(self):
return np.linalg.det(self.cell)
def __len__(self):
return len(self.scaled_positions)
def __getitem__(self, i):
"""
Return an atom
"""
if isinstance(i, int):
natoms = len(self)
if i < -natoms or i >= natoms:
raise IndexError('Index out of range.')
mom = np.array([0,0,0])
if not self.magmoms is None:
mom = self.magmoms[i]
return (self.symbols[i], self.numbers[i], self.scaled_positions[i], mom)
atom_data = [
[ 0, "mu", "mu", 0.2], # 0
[ 1, "H", "Hydrogen", 1.00794], # 1
[ 2, "He", "Helium", 4.002602], # 2
[ 3, "Li", "Lithium", 6.941], # 3
[ 4, "Be", "Beryllium", 9.012182], # 4
[ 5, "B", "Boron", 10.811], # 5
[ 6, "C", "Carbon", 12.0107], # 6
[ 7, "N", "Nitrogen", 14.0067], # 7
[ 8, "O", "Oxygen", 15.9994], # 8
[ 9, "F", "Fluorine", 18.9984032], # 9
[ 10, "Ne", "Neon", 20.1797], # 10
[ 11, "Na", "Sodium", 22.98976928], # 11
[ 12, "Mg", "Magnesium", 24.3050], # 12
[ 13, "Al", "Aluminium", 26.9815386], # 13
[ 14, "Si", "Silicon", 28.0855], # 14
[ 15, "P", "Phosphorus", 30.973762], # 15
[ 16, "S", "Sulfur", 32.065], # 16
[ 17, "Cl", "Chlorine", 35.453], # 17
[ 18, "Ar", "Argon", 39.948], # 18
[ 19, "K", "Potassium", 39.0983], # 19
[ 20, "Ca", "Calcium", 40.078], # 20
[ 21, "Sc", "Scandium", 44.955912], # 21
[ 22, "Ti", "Titanium", 47.867], # 22
[ 23, "V", "Vanadium", 50.9415], # 23
[ 24, "Cr", "Chromium", 51.9961], # 24
[ 25, "Mn", "Manganese", 54.938045], # 25
[ 26, "Fe", "Iron", 55.845], # 26
[ 27, "Co", "Cobalt", 58.933195], # 27
[ 28, "Ni", "Nickel", 58.6934], # 28
[ 29, "Cu", "Copper", 63.546], # 29
[ 30, "Zn", "Zinc", 65.38], # 30
[ 31, "Ga", "Gallium", 69.723], # 31
[ 32, "Ge", "Germanium", 72.64], # 32
[ 33, "As", "Arsenic", 74.92160], # 33
[ 34, "Se", "Selenium", 78.96], # 34
[ 35, "Br", "Bromine", 79.904], # 35
[ 36, "Kr", "Krypton", 83.798], # 36
[ 37, "Rb", "Rubidium", 85.4678], # 37
[ 38, "Sr", "Strontium", 87.62], # 38
[ 39, "Y", "Yttrium", 88.90585], # 39
[ 40, "Zr", "Zirconium", 91.224], # 40
[ 41, "Nb", "Niobium", 92.90638], # 41
[ 42, "Mo", "Molybdenum", 95.96], # 42
[ 43, "Tc", "Technetium", 0], # 43
[ 44, "Ru", "Ruthenium", 101.07], # 44
[ 45, "Rh", "Rhodium", 102.90550], # 45
[ 46, "Pd", "Palladium", 106.42], # 46
[ 47, "Ag", "Silver", 107.8682], # 47
[ 48, "Cd", "Cadmium", 112.411], # 48
[ 49, "In", "Indium", 114.818], # 49
[ 50, "Sn", "Tin", 118.710], # 50
[ 51, "Sb", "Antimony", 121.760], # 51
[ 52, "Te", "Tellurium", 127.60], # 52
[ 53, "I", "Iodine", 126.90447], # 53
[ 54, "Xe", "Xenon", 131.293], # 54
[ 55, "Cs", "Caesium", 132.9054519], # 55
[ 56, "Ba", "Barium", 137.327], # 56
[ 57, "La", "Lanthanum", 138.90547], # 57
[ 58, "Ce", "Cerium", 140.116], # 58
[ 59, "Pr", "Praseodymium", 140.90765], # 59
[ 60, "Nd", "Neodymium", 144.242], # 60
[ 61, "Pm", "Promethium", 0], # 61
[ 62, "Sm", "Samarium", 150.36], # 62
[ 63, "Eu", "Europium", 151.964], # 63
[ 64, "Gd", "Gadolinium", 157.25], # 64
[ 65, "Tb", "Terbium", 158.92535], # 65
[ 66, "Dy", "Dysprosium", 162.500], # 66
[ 67, "Ho", "Holmium", 164.93032], # 67
[ 68, "Er", "Erbium", 167.259], # 68
[ 69, "Tm", "Thulium", 168.93421], # 69
[ 70, "Yb", "Ytterbium", 173.054], # 70
[ 71, "Lu", "Lutetium", 174.9668], # 71
[ 72, "Hf", "Hafnium", 178.49], # 72
[ 73, "Ta", "Tantalum", 180.94788], # 73
[ 74, "W", "Tungsten", 183.84], # 74
[ 75, "Re", "Rhenium", 186.207], # 75
[ 76, "Os", "Osmium", 190.23], # 76
[ 77, "Ir", "Iridium", 192.217], # 77
[ 78, "Pt", "Platinum", 195.084], # 78
[ 79, "Au", "Gold", 196.966569], # 79
[ 80, "Hg", "Mercury", 200.59], # 80
[ 81, "Tl", "Thallium", 204.3833], # 81
[ 82, "Pb", "Lead", 207.2], # 82
[ 83, "Bi", "Bismuth", 208.98040], # 83
[ 84, "Po", "Polonium", 0], # 84
[ 85, "At", "Astatine", 0], # 85
[ 86, "Rn", "Radon", 0], # 86
[ 87, "Fr", "Francium", 0], # 87
[ 88, "Ra", "Radium", 0], # 88
[ 89, "Ac", "Actinium", 0], # 89
[ 90, "Th", "Thorium", 232.03806], # 90
[ 91, "Pa", "Protactinium", 231.03588], # 91
[ 92, "U", "Uranium", 238.02891], # 92
[ 93, "Np", "Neptunium", 0], # 93
[ 94, "Pu", "Plutonium", 0], # 94
[ 95, "Am", "Americium", 0], # 95
[ 96, "Cm", "Curium", 0], # 96
[ 97, "Bk", "Berkelium", 0], # 97
[ 98, "Cf", "Californium", 0], # 98
[ 99, "Es", "Einsteinium", 0], # 99
[100, "Fm", "Fermium", 0], # 100
[101, "Md", "Mendelevium", 0], # 101
[102, "No", "Nobelium", 0], # 102
[103, "Lr", "Lawrencium", 0], # 103
[104, "Rf", "Rutherfordium", 0], # 104
[105, "Db", "Dubnium", 0], # 105
[106, "Sg", "Seaborgium", 0], # 106
[107, "Bh", "Bohrium", 0], # 107
[108, "Hs", "Hassium", 0], # 108
[109, "Mt", "Meitnerium", 0], # 109
[110, "Ds", "Darmstadtium", 0], # 110
[111, "Rg", "Roentgenium", 0], # 111
[112, "Cn", "Copernicium", 0], # 112
[113, "Uut", "Ununtrium", 0], # 113
[114, "Uuq", "Ununquadium", 0], # 114
[115, "Uup", "Ununpentium", 0], # 115
[116, "Uuh", "Ununhexium", 0], # 116
[117, "Uus", "Ununseptium", 0], # 117
[118, "Uuo", "Ununoctium", 0], # 118
]
symbol_map = {
"H":1,
"He":2,
"Li":3,
"Be":4,
"B":5,
"C":6,
"N":7,
"O":8,
"F":9,
"Ne":10,
"Na":11,
"Mg":12,
"Al":13,
"Si":14,
"P":15,
"S":16,
"Cl":17,
"Ar":18,
"K":19,
"Ca":20,
"Sc":21,
"Ti":22,
"V":23,
"Cr":24,
"Mn":25,
"Fe":26,
"Co":27,
"Ni":28,
"Cu":29,
"Zn":30,
"Ga":31,
"Ge":32,
"As":33,
"Se":34,
"Br":35,
"Kr":36,
"Rb":37,
"Sr":38,
"Y":39,
"Zr":40,
"Nb":41,
"Mo":42,
"Tc":43,
"Ru":44,
"Rh":45,
"Pd":46,
"Ag":47,
"Cd":48,
"In":49,
"Sn":50,
"Sb":51,
"Te":52,
"I":53,
"Xe":54,
"Cs":55,
"Ba":56,
"La":57,
"Ce":58,
"Pr":59,
"Nd":60,
"Pm":61,
"Sm":62,
"Eu":63,
"Gd":64,
"Tb":65,
"Dy":66,
"Ho":67,
"Er":68,
"Tm":69,
"Yb":70,
"Lu":71,
"Hf":72,
"Ta":73,
"W":74,
"Re":75,
"Os":76,
"Ir":77,
"Pt":78,
"Au":79,
"Hg":80,
"Tl":81,
"Pb":82,
"Bi":83,
"Po":84,
"At":85,
"Rn":86,
"Fr":87,
"Ra":88,
"Ac":89,
"Th":90,
"Pa":91,
"U":92,
"Np":93,
"Pu":94,
"Am":95,
"Cm":96,
"Bk":97,
"Cf":98,
"Es":99,
"Fm":100,
"Md":101,
"No":102,
"Lr":103,
"Rf":104,
"Db":105,
"Sg":106,
"Bh":107,
"Hs":108,
"Mt":109,
"Ds":110,
"Rg":111,
"Cn":112,
"Uut":113,
"Uuq":114,
"Uup":115,
"Uuh":116,
"Uus":117,
"Uuo":118,
"mu":0,
}