#!/usr/bin/env python
# -*- coding: utf-8 -*-
from .base import listOfStr2listOfFloats, listOfStr2listOfInts
[docs]class LammpsDataFile(object):
"""
Class supporting LAMMPS data file.
:param filename: data file to read initially
:type filename: str
:param atom_style: supported: *atomic*, *charge*, *molecular* and *full*
:type atom_style: str
:return: Lammps data file object
:rtype: .LammpsDataFile
"""
BlockKeywords = ['Masses', 'Atoms', 'Velocities', 'Bonds', 'Angles', 'Dihedrals', 'Impropers', 'Coeffs']
def __init__(self, filename=None, atom_style='full'):
self.atom_style = atom_style
#: *Header* section data -> :class:`.LammpsDataHeader`
self.header = LammpsDataHeader()
#: *Masses* section data -> :class:`.LammpsDataMasses`
self.masses = LammpsDataMasses()
#: *Coefficients* section data -> :class:`.LammpsDataCoeffs`
self.coeffs = LammpsDataCoeffs()
self.BlockKeywords = LammpsDataFile.BlockKeywords + self.coeffs.coeffsSections
#: *Atoms* section data -> :class:`.LammpsDataAtoms`
self.atoms = LammpsDataAtoms(self.atom_style)
#: *Velocities* section data -> :class:`.LammpsDataVelocities`
self.velos = LammpsDataVelocities()
#: *Bonds* section data -> :class:`.LammpsDataBonds`
self.bonds = LammpsDataBonds()
#: *Bonds* section data -> :class:`.LammpsDataBonds`
self.bonds = LammpsDataBonds()
#: *Angles* section data -> :class:`.LammpsDataAngles`
self.angles = LammpsDataAngles()
#: *Dihedrals* section data -> :class:`.LammpsDataDihedrals`
self.dihedrals = LammpsDataDihedrals()
#: *Impropers* section data -> :class:`.LammpsDataImpropers`
self.impropers = LammpsDataImpropers()
self.dataObjs = [self.header, self.masses, self.coeffs, self.atoms, self.velos, self.bonds, self.angles, self.dihedrals, self.impropers]
if filename:
self.filename = filename
self._parse_file()
def _parse_file(self):
section = 'Header'
f = open(self.filename, 'r')
# read (1st) comment
line = f.readline().strip()
self.header['description'] = line
i='.'
#print '*: '+line
for line in f:
line = line.strip()
kw = line.split('#')[0].strip()
if line == '':
continue
if kw in self.BlockKeywords:
#print '\n*: switch block ' + section + ' -> ' + line
section = line.split('#')[0].strip()
continue
if section == 'Header':
self.header.parseLine(line)
elif section == 'Masses':
self.masses.parseLine(line)
elif section in self.coeffs.coeffsSections:
self.coeffs.parseLine(section, line)
elif section == 'Atoms':
self.atoms.parseLine(line)
elif section == 'Velocities':
self.velos.parseLine(line)
elif section == 'Bonds':
self.bonds.parseLine(line)
elif section == 'Angles':
self.angles.parseLine(line)
elif section == 'Dihedrals':
self.dihedrals.parseLine(line)
elif section == 'Impropers':
self.impropers.parseLine(line)
else:
pass
#print i,
#print '\n<oef>'
# def __str__(self):
# out = str(self.header)
# out += str(self.masses)
# out += str(self.coeffs)
# out += str(self.atoms)
# out += str(self.velos)
# out += str(self.bonds)
# out += str(self.angles)
# out += str(self.dihedrals)
# out += str(self.impropers)
# return out
def __str__(self):
out=''
for dataObj in self.dataObjs:
if len(dataObj) > 0:
out += str(dataObj)
return out
# def write(self, filename):
# """
# Write a LAMMPS data file.
#
# :param filename: LAMMPS data file name
# """
# myf = open(filename, 'w')
# myf.write(str(self.header))
# myf.write( str(self.masses))
# myf.write(str(self.coeffs))
# myf.write(str(self.atoms))
# myf.write(str(self.velos))
# myf.write(str(self.bonds))
# myf.write(str(self.angles))
# myf.write(str(self.dihedrals))
# myf.write(str(self.impropers))
# myf.close()
[docs] def write(self, filename):
"""
Write a LAMMPS data file.
:param filename: LAMMPS data file name
"""
myf = open(filename, 'w')
for dataObj in self.dataObjs:
if len(dataObj) > 0:
myf.write(str(dataObj))
myf.close()
def xyz_print(self,title=''):
print len(self.atoms)
print self.header['description']
print self.atoms._xyz_str
@property
def Lx(self):
""" Box length in *X* dim. """
return self.header['xhi'] - self.header['xlo']
@property
def Ly(self):
""" Box length in *Y* dim. """
return self.header['yhi'] - self.header['ylo']
@property
def Lz(self):
""" Box length in *Z* dim. """
return self.header['zhi'] - self.header['zlo']
@property
def Vol(self):
""" Box volume """
return self.Lx * self.Ly * self.Lz
def move(self, vector):
self.header.move(vector)
self.atoms.move(vector)
def scaleCharges(self, factor):
self.atoms.scaleCharges( factor )
def origin2zero(self):
vector = [ -1.0 * self.header['xlo'],
-1.0 * self.header['ylo'],
-1.0 * self.header['zlo']]
self.move( vector )
def checkHeader(self):
print self.header['atoms'], len(self.atoms)
print self.header['bonds'], len(self.bonds)
print self.header['angles'], len(self.angles)
print self.header['dihedrals'], len(self.dihedrals)
print self.header['impropers'], len(self.impropers)
#------------------------------------------------------------------------------------------------
[docs]class LammpsDataAtoms(dict):
"""
Class for parsing, storage & administration of the **Atoms** section in the LAMMPS data file.
Each individual atom is stored in an object, like :class:`.LammpsDataAtomAtomic`, which type
depends on the *atom_style*, and is derived from :class:`.LammpsDataAtomTemplate`.
:param atom_style: supported: *atomic*, *charge*, *molecular* and *full*
:type atom_style: str
"""
_registeredStyles = {}
@classmethod
def _register(cls, registerCls):
"""
Decorator to register Entry type classes.
:param registerCls: class to register
:type registerCls: class
:return: class to register
:rtype: class
"""
style_name = registerCls.atom_style
cls._registeredStyles[style_name] = registerCls
return registerCls
def __init__(self, atom_style):
#self._atoms = {}
super(LammpsDataAtoms, self).__init__()
self.atom_class = self._registeredStyles[atom_style]
[docs] def parseLine(self, line):
"""
Creates a new atom by parsing a string.
:param line: data line of the *Atoms* section
:type line: str
"""
id = line.split()[0]
self[int(id)] = self.atom_class(line)
def __str__(self):
out = '\nAtoms\n\n'
for atom in self.values():
out += str(atom)
return out
@property
def ids(self):
""" Returns a list of all atom IDs. """
return self.keys()
@property
def _xyz_str(self):
out = ''
for atom in self.values():
out += atom._xyz_str
return out
@property
def totalCharge(self):
charge=0.0
for atom in self.values():
charge += atom['q']
return charge
def move(self, vector):
for atom in self.values():
atom.move( vector )
def scaleCharges(self, factor):
for atom in self.values():
atom.scaleCharge(factor)
[docs]class LammpsDataAtomTemplate(object):
"""
Storage class template for an atom.
"""
atom_style = 'template'
props_int = ['id', 'atom-type']
props_float = ['x', 'y', 'z']
line_format = '%d %d %.6f %.6f %.6f\n'
def __init__(self, line=None):
self.props_all = self.props_int + self.props_float
self._props = {}
for prop in self.props_int:
self._props[prop] = 0
for prop in self.props_float:
self._props[prop] = 0.0
if line:
self.parseLine(line)
def parseLine(self, line):
words = line.strip().split()
i = 0
for prop in self.props_int:
self._props[prop] = int(words[i])
i += 1
for prop in self.props_float:
self._props[prop] = float(words[i])
i += 1
def _list(self):
lout = []
for prop in self.props_all:
lout.append( self._props[prop] )
return lout
def __str__(self):
return str(self.line_format % tuple(self._list()))
def __setitem__(self, prop, value):
self._props[prop] = value
def __getitem__(self, prop):
return self._props[prop]
def __getattr__(self, name):
if name in type(self).props_int + type(self).props_float:
return self._props[name]
raise AttributeError
def __setattr__(self, key, value):
if key in type(self).props_int + type(self).props_float:
self._props[key] = value
else:
super(LammpsDataAtomTemplate, self).__setattr__(key, value)
@property
def _xyz_str(self):
tmp = [ self['atom-type'], self['x'], self['y'], self['z'] ]
return str('%d %.6f %.6f %.6f\n' % tuple(tmp))
@property
def xyz(self):
return [ self['x'], self['y'], self['z'] ]
def move(self, vector):
self['x'] = self['x'] + vector[0]
self['y'] = self['y'] + vector[1]
self['z'] = self['z'] + vector[2]
def scaleCharge(self, factor):
self['q'] = self['q'] * factor
[docs]@LammpsDataAtoms._register
class LammpsDataAtomAtomic(LammpsDataAtomTemplate):
"""
Basis: :class:`LammpsDataAtomTemplate`
Storage class for an atom of style *atomic*.
Supported properties: *id*, *atom-type*, *x*, *y*, *z*
"""
atom_style = 'atomic'
props_int = ['id', 'atom-type']
props_float = ['x', 'y', 'z']
line_format = '%d %d %.16e %.16e %.16e\n'
@LammpsDataAtoms._register
class LammpsDataAtomCharge(LammpsDataAtomTemplate):
atom_style = 'charge'
props_int = ['id', 'atom-type']
props_float = ['q', 'x', 'y', 'z']
line_format = '%d %d %.16e %.16e %.16e %.16e\n'
@LammpsDataAtoms._register
class LammpsDataAtomMolecular(LammpsDataAtomTemplate):
atom_style = 'molecular'
props_int = ['id', 'molecule-id', 'atom-type']
props_float = ['x', 'y', 'z']
line_format = '%d %d %d %.16e %.16e %.16e\n'
@LammpsDataAtoms._register
class LammpsDataAtomFull(LammpsDataAtomTemplate):
atom_style = 'full'
props_int = ['id', 'molecule-id', 'atom-type']
props_float = ['q', 'x', 'y', 'z']
line_format = '%d %d %d %.16e %.16e %.16e %.16e\n'
#------------------------------------------------------------------------------------------------
[docs]class LammpsDataVelocities(dict):
"""
Class for parsing, storage & administration of the **Velocities** section in the LAMMPS data file.
"""
def parseLine(self, line):
words = line.strip().split()
self[int(words[0])] = listOfStr2listOfFloats(words[1:4])
def __str__(self):
out = '\nVelocities\n\n'
for aid, vv in self.items():
out += str("{0:d} {1:.16f} {2:.16f} {3:.16f}\n".format( aid, vv[0], vv[1], vv[2] ))
return out
#------------------------------------------------------------------------------------------------
class LammpsDataBonds(dict):
natoms = 2 # number of atoms involved in
name = 'Bonds' # name of the section
def parseLine(self, line):
words = line.strip().split()
self[int(words[0])] = listOfStr2listOfInts(words[1:self.natoms+2])
def __str__(self):
out = '\n' + self.name + '\n\n'
for id, vv in self.items():
out += str("{0:d} ".format( id ))
out += ' '.join(map(str, vv)) + '\n'
return out
class LammpsDataAngles(LammpsDataBonds):
natoms = 3
name = 'Angles'
class LammpsDataDihedrals(LammpsDataBonds):
natoms = 4
name = 'Dihedrals'
class LammpsDataImpropers(LammpsDataBonds):
natoms = 4
name = 'Impropers'
#------------------------------------------------------------------------------------------------
[docs]class LammpsDataCoeffs(object):
"""
Class for storage & administration of **the Coefficients sections** in the LAMMPS data file.
Each individual section is stored in a :class:`.LammpsDataCoeffsSection` object.
"""
#: List of supported Coefficients section names
#:
#:.. note:: This is the only position where a section name needs to be declared!
#:
coeffsSections = ['Pair Coeffs', 'PairIJ Coeffs', 'Bond Coeffs', 'Angle Coeffs', 'Dihedral Coeffs', 'Improper Coeffs',
'BondBond Coeffs','BondAngle Coeffs','MiddleBondTorsion Coeffs', 'EndBondTorsion Coeffs',
'AngleTorsion Coeffs', 'AngleAngleTorsion Coeffs','BondBond13 Coeffs','AngleAngle Coeffs' ]
def __init__(self):
self._coeffsSections = {}
for coeffsSection in self.coeffsSections:
self._coeffsSections[coeffsSection] = LammpsDataCoeffsSection(coeffsSection)
def parseLine(self, section, line):
self._coeffsSections[section].parseLine(line)
def __str__(self):
out = ''
for coeffsType in self.coeffsSections:
section = self._coeffsSections[coeffsType]
if len(section) > 0:
out += str(section)
return out
def __len__(self):
for coeffsType in self.coeffsSections:
section = self._coeffsSections[coeffsType]
if len(section) > 0:
return 1
return 0
[docs]class LammpsDataCoeffsSection(object):
"""
Class for parsing, storage & administration of **a Coefficients section** in the LAMMPS data file.
:param coeffsType: Coeffs section name (see :attr:`.LammpsDataCoeffs.coeffsSections` )
:type coeffsType: str
"""
def __init__(self, coeffsType):
self.type = coeffsType
# key = atom type ID
# value = [coeff1, coeff2, ...]
self._coeffs = {}
def __setitem__(self, key, value):
self._coeffs[key] = value
def __getitem__(self, key):
return self._coeffs[key]
def __len__(self):
return len(self._coeffs)
def parseLine(self, line):
words = line.strip().split()
self._coeffs[int(words[0])] = words[1:]
def __str__(self):
out = str("\n{0:s}\n\n".format(self.type))
for cid, coeffs in self._coeffs.items():
out += str("{0:d}".format(cid))
for coef in coeffs:
out += str(" {0:s}".format(coef))
out += '\n'
return out
[docs]class LammpsDataMasses(dict):
"""
Class for parsing, storage & administration of the **Masses** sections in the LAMMPS data file.
"""
def __setitem__(self, key, value):
super(LammpsDataMasses, self).__setitem__(key, float(value))
def parseLine(self, line):
words = line.strip().split()
self[int(words[0])] = float(words[1])
def __str__(self):
out = '\nMasses\n\n'
for aid, mass in self.items():
out += str("{0:d} {1:.6f}\n".format(aid, mass))
return out