...
 
Commits (3)
__pycache__
MANIFEST
build
dist
*.pyc
.cache
cclib.egg-info
sudo: false
dist: trusty
language: python
python:
- 2.7
- 3.2
- 3.6
addons:
apt:
packages:
- libopenbabel-dev
- swig
cache:
pip: true
before_install:
- pip install -r requirements.txt
install:
- python setup.py install
script:
- sh travis/run_travis_tests.sh
On behalf of the cclib development team, we are pleased to announce the release of cclib 1.5.3, which is now available for download from https://cclib.github.io. This is a minor update to version 1.5 that includes some new functionality and attributes, as well as bug fixes and small improvements.
cclib is an open source library, written in Python, for parsing and interpreting the results of computational chemistry packages. It currently parses output files from 13 different programs: ADF, DALTON, Firefly, GAMESS (US), GAMESS-UK, Gaussian, Jaguar, Molpro, MOPAC, NWChem, ORCA, Psi and QChem.
Among other data, cclib extracts:
* results of SCF, post-Hartree-Fock, TD-DFT and other calculations
* coordinates, energies and geometry optimization data
* information about atomic and molecular orbitals
* vibrational modes, excited states and transitions
* charges, electrostatic moments and polarizabilities
(For a complete list see https://cclib.github.io/data.html).
cclib also provides some calculation methods for interpreting the electronic properties of molecules such as:
* Mulliken and Lowdin population analyses
* Overlap population analysis
* Mayer's bond orders
(For a complete list see https://cclib.github.io/methods.html).
For information on how to use cclib, see our documentation at https://cclib.github.io.
If you need help, find a bug, want new features or have any questions, please send an email to our mailing list:
https://lists.sourceforge.net/lists/listinfo/cclib-users
If your published work uses cclib, please support its development by citing the following article:
N. M. O'Boyle, A. L. Tenderholt, K. M. Langner, cclib: a library for package-independent computational chemistry algorithms, J. Comp. Chem. 29 (5), 839-845 (2008)
You can also specifically reference this version of cclib as:
Eric Berquist, Karol M. Langner, Noel M. O'Boyle, and Adam L. Tenderholt. Release of cclib version 1.5. 2016. https://dx.doi.org/10.5281/zenodo.60670
Regards,
The cclib development team
———
Summary of changes since last version:
* New attribute transprop for electronic transitions
* Support grads attribute in Psi4 and Molpro
* Support optstatus for IRCs and in Psi4
* Updated test file versions to Gaussian16
* Many other minor improvements and bug fixes
This diff is collapsed.
== cclib installation instructions ==
=== Requirements ===
Before you install cclib, you need to make sure that you have the following:
* Python (version 3.0 and up, although 2.7 will still work)
* NumPy (at least version 1.5 is recommended).
Python is an open-source programming language available from http://www.python.org and it is included in many Linux distributions. In Debian it is installed as follows: (as root)
apt-get install python python-dev
NumPy (Numerical Python) adds a fast array facility to Python and is available from http://www.numpy.org. Windows users should use the most recent NumPy installation for the Python version they have (2.4, 2.5). Linux users are recommended to find a binary package for their distribution. In Debian it is installed as follows: (as root)
apt-get install python-numpy
Note: Numeric (the old version of Numerical Python) is not supported by the Numerical Python developers and is not supported by cclib.
To test whether Python is on the PATH, open a command prompt window and type:
python
If Python is not on the PATH and you use Windows, add the full path to the directory containing it to the end of the PATH variable under Control Panel/System/Advanced Settings/Environment Variables. If you use Linux and Python is not on the PATH, put/edit the appropriate line in your .bashrc or similar startup file.
To test, try importing NumPy at the Python prompt. You should see something similar to the following:
$ python3
Python 3.2.3 (default, Feb 27 2014, 21:31:18)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy
>>> numpy.__version__
'1.6.1'
(To exit, press CTRL+Z in Windows or CTRL+D in Linux)
=== Installing cclib ===
On Debian, Ubuntu and other derived Linux distribution, cclib can be quickly installed with the command:
aptitude install cclib
The version installed from a distribuion might not be the most recent one. To install the most recent version, first download the source code of cclib. Extract the cclib tar file or zip file at an appropriate location, which we will call INSTALLDIR. Open a command prompt and change directory to INSTALLDIR. Next, run the following commands:
python setup.py build
python setup.py install (as root)
To test, trying importing cclib at the Python prompt. You should see something similar to the following:
$ python3
Python 3.2.3 (default, Feb 27 2014, 21:31:18)
[GCC 4.6.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
Press ESC for command-line completion (twice for guesses).
History is saved to ~/.pyhistory.
>>> import cclib
>>> cclib.__version__
'1.5.3'
To run the unit tests, change directory into INSTALLDIR/test and run the following command:
python testall.py
This tests the program using the example data files included in the INSTALLDIR/data directory.
=== What next? ===
* Read the documentation at:
http://cclib.github.io
* Read the list and specifications of the extracted data at:
http://cclib.github.io/data.html
* Send any questions to the cclib-users mailing list at:
https://lists.sourceforge.net/lists/listinfo/cclib-users.
BSD 3-Clause License
Copyright (c) 2017, the cclib development team
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 holder 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.
### cclib
[![DOI](https://zenodo.org/badge/doi/10.5281/zenodo.50324.svg)](https://dx.doi.org/10.5281/zenodo.60670)
[![PyPI version](http://img.shields.io/pypi/v/cclib.svg?style=flat)](https://pypi.python.org/pypi/cclib)
[![GitHub release](https://img.shields.io/github/release/cclib/cclib.svg?style=flat)](https://github.com/cclib/cclib/releases)
[![build status](http://img.shields.io/travis/cclib/cclib/master.svg?style=flat)](https://travis-ci.org/cclib/cclib)
[![license](http://img.shields.io/badge/license-BSD-blue.svg?style=flat)](https://github.com/cclib/cclib/blob/master/LICENSE)
<img src="./logo.png" alt="cclib logo" width="100" />
cclib is a Python library that provides parsers for output files of computational chemistry packages. It also provides a platform for computational chemists to implement algorithms in a platform-independent way.
For more information, go to [https://cclib.github.io](https://cclib.github.io). There is a mailing list for questions at cclib-users@lists.sourceforge.net.
Developers that have added more than 1K SLOC:
Eric J. Berquist
Geoff Hutchison
Karol M. Langner
Noel M. O'Boyle
Sanjeed Schamnad
Casper Steinmann
Adam L. Tenderholt
We would like the thank the following who have contributed to cclib:
Nuno Bandeira -- for bug reporting
Björn Baumeier -- for bug reporting
Dermot Brougham -- for bug reporting
bwang2453 -- for patches and new features
Avril Coghlan -- for designing the cclib logo
Ramon Crehuet -- for new features
Björn Dahlgren -- for bug reporting
Yafei Dai -- for bug reporting
Abhishek Dey -- for bug reporting
Matt Ernst -- for patches
Clyde Fare -- for bug reporting and patches
Christos Garoufalis -- for bug reporting
Sagar Gaur -- for patches
glideht -- for bug reporting
Edward Holland -- for patches
Karen Hemelsoet -- for bug reporting
Ian Hovell -- for bug reporting
Julien Idé -- for bug reporting
csjacky -- for bug reporting
Russell Johnson -- for providing CCCBDB (NIST) logfiles
Jerome Kieffer -- for bug reporting
Greg Magoon -- for bug reporting and patches
Scott McKechnie -- for bug reporting
mkrompiec -- for contributing test files
mwykes -- for bug reporting and patches
Alexis Otero-Calvis -- for bug reporting
Rob Paton -- for creating and running Jaguar test jobs
Martin Peeks -- for patches
Felix Plasser -- for fixes, patches and contributing files
Martin Rahm -- for bug reporting
Marius Retegan -- for bug reporting
Tamilmani S -- for bug reporting
Melchor Sanchez -- for bug reporting
Alex Schild -- for ideas and contributing test files
Jen Schwartz -- for helping create and run Jaguar 6.0 test jobs
Tiago Silva -- for bug reporting
Pavel Solntsev -- for bug reporting
Ben Stein -- for patches
Adam Swanson -- for bug reporting
Joe Townsend -- for contributing multiple GAMESS files to test on
Chengju Wang -- for bug reporting
Andrew Warden -- for bug reporting
Samuel Wilson -- for bug reporting
Fedor Zhuravlev -- for patches
Please let us know if we have omitted someone from this list.
logo.png

16.3 KB

This diff is collapsed.
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Create a MANIFEST file for distributing soruces with distutils."""
from __future__ import print_function
import glob
import os
files = ['ANNOUNCE', 'CHANGELOG', 'INSTALL', 'LICENSE', 'README.md','THANKS',]
files += ['setup.py']
source = os.path.join('src', 'cclib')
files.append(os.path.join(source, "__init__.py"))
folders = ['bridge', 'io', 'method', 'parser', 'progress']
for folder in folders:
files.extend(glob.glob(os.path.join(source,folder,'*.py')))
for f in files:
if not os.path.isfile(f):
print("%s does not exist" % f)
print("\n".join(files), file=open("MANIFEST", "w"))
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
import urllib
from BeautifulSoup import BeautifulSoup
wiki = "http://sourceforge.net/apps/mediawiki/cclib"
# http://openbabel.sourceforge.net/w/index.php?title=Special:Export&action=submit&pages=FAQ
allpages = urllib.urlopen(wiki + "/index.php?title=Special:AllPages").read()
soup = BeautifulSoup(allpages)
hrefs = []
for anchor in soup('table')[2]('a'):
hrefs.append(anchor['href'].split("/")[-1])
print 'Pages to download:\n', ', '.join(hrefs)
params = urllib.urlencode({"action":"submit",
"pages":"\n".join(hrefs)})
query = urllib.urlopen(wiki + "/index.php/Special:Export", params)
outputfile = open("backup.xml","w")
print >> outputfile, query.read()
outputfile.close()
biopython ; python_version == '2.7' or python_version >= '3.4'
numpy
openbabel
# For some reason PyQuante can't be installed from PyPI.
https://sourceforge.net/projects/pyquante/files/PyQuante-1.6/PyQuante-1.6.5/PyQuante-1.6.5.tar.gz ; python_version <= '2.7'
# for unit tests
mock==2.0.0 ; python_version < '3.3'
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""cclib: parsers and algorithms for computational chemistry
cclib is a Python library that provides parsers for computational
chemistry log files. It also provides a platform to implement
algorithms in a package-independent manner.
"""
from __future__ import absolute_import, with_statement
import setuptools
# Chosen from http://www.python.org/pypi?:action=list_classifiers
classifiers = """Development Status :: 5 - Production/Stable
Environment :: Console
Intended Audience :: Science/Research
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Natural Language :: English
Operating System :: OS Independent
Programming Language :: Python
Topic :: Scientific/Engineering :: Chemistry
Topic :: Software Development :: Libraries :: Python Modules"""
def setup_cclib():
doclines = __doc__.split("\n")
setuptools.setup(
name="cclib",
version="1.5.3",
url="http://cclib.github.io/",
author="cclib development team",
author_email="cclib-users@lists.sourceforge.net",
maintainer="cclib development team",
maintainer_email="cclib-users@lists.sourceforge.net",
license="BSD 3-Clause License",
description=doclines[0],
long_description="\n".join(doclines[2:]),
classifiers=classifiers.split("\n"),
platforms=["Any."],
packages=setuptools.find_packages('src'),
package_dir={'': 'src'},
entry_points={
'console_scripts': [
'ccget=scripts.ccget:ccget',
'ccwrite=scripts.ccwrite:main',
'cda=scripts.cda:main'
]
}
)
if __name__ == '__main__':
setup_cclib()
# -*- coding: utf-8 -*-
#
# Copyright (c) 2018, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""A library for parsing and interpreting results from computational chemistry packages.
The goals of cclib are centered around the reuse of data obtained from various
computational chemistry programs and typically contained in output files. Specifically,
cclib extracts (parses) data from the output files generated by multiple programs
and provides a consistent interface to access them.
Currently supported programs:
ADF, Firefly, GAMESS(US), GAMESS-UK, Gaussian,
Jaguar, Molpro, MOPAC, NWChem, ORCA, Psi, Q-Chem
Another aim is to facilitate the implementation of algorithms that are not specific
to any particular computational chemistry package and to maximise interoperability
with other open source computational chemistry and cheminformatic software libraries.
To this end, cclib provides a number of bridges to help transfer data to other libraries
as well as example methods that take parsed data as input.
"""
__version__ = "1.5.3"
from cclib import parser
from cclib import progress
from cclib import method
from cclib import bridge
from cclib import io
# The test module can be imported if it was installed with cclib.
try:
from cclib import test
except ImportError:
pass
# The objects below constitute our public API. These names will not change
# over time. Names in the sub-modules will typically also be backwards
# compatible, but may sometimes change when code is moved around.
ccopen = io.ccopen
ccwrite = io.ccwrite
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Facilities for moving parsed data to other cheminformatic libraries."""
try:
import openbabel
except ImportError:
pass
else:
from cclib.bridge.cclib2openbabel import makeopenbabel
try:
import PyQuante
except ImportError:
pass
else:
from cclib.bridge.cclib2pyquante import makepyquante
try:
import Bio
except ImportError:
pass
else:
from cclib.bridge.cclib2biopython import makebiopython
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Bridge for using cclib data in biopython (http://biopython.org)."""
try:
from Bio.PDB.Atom import Atom
except ImportError:
# Fail silently for now.
pass
from cclib.parser.utils import PeriodicTable
def makebiopython(atomcoords, atomnos):
"""Create a list of BioPython Atoms.
This creates a list of BioPython Atoms suitable for use by
Bio.PDB.Superimposer, for example.
"""
pt = PeriodicTable()
bioatoms = []
for coords, atomno in zip(atomcoords, atomnos):
symbol = pt.element[atomno]
bioatoms.append(Atom(symbol, coords, 0, 0, 0, symbol, 0, symbol.upper()))
return bioatoms
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Bridge between cclib data and openbabel (http://openbabel.org)."""
try:
import openbabel as ob
except ImportError:
# Fail silently for now.
pass
from cclib.parser.data import ccData
def makecclib(mol):
"""Create cclib attributes and return a ccData from an OpenBabel molecule.
Beyond the numbers, masses and coordinates, we could also set the total charge
and multiplicity, but often these are calculated from atomic formal charges
so it is better to assume that would not be correct.
"""
attributes = {
'atomcoords': [],
'atommasses': [],
'atomnos': [],
'natom': mol.NumAtoms(),
}
for atom in ob.OBMolAtomIter(mol):
attributes['atomcoords'].append([atom.GetX(), atom.GetY(), atom.GetZ()])
attributes['atommasses'].append(atom.GetAtomicMass())
attributes['atomnos'].append(atom.GetAtomicNum())
return ccData(attributes)
def makeopenbabel(atomcoords, atomnos, charge=0, mult=1):
"""Create an Open Babel molecule."""
obmol = ob.OBMol()
for i in range(len(atomnos)):
# Note that list(atomcoords[i]) is not equivalent!!!
# For now, only take the last geometry.
# TODO: option to export last geometry or all geometries?
coords = atomcoords[-1][i].tolist()
atomno = int(atomnos[i])
obatom = ob.OBAtom()
obatom.SetAtomicNum(atomno)
obatom.SetVector(*coords)
obmol.AddAtom(obatom)
obmol.ConnectTheDots()
obmol.PerceiveBondOrders()
obmol.SetTotalSpinMultiplicity(mult)
obmol.SetTotalCharge(int(charge))
return obmol
def readfile(fname, format):
"""Read a file with OpenBabel and extract cclib attributes."""
obc = ob.OBConversion()
if obc.SetInFormat(format):
mol = ob.OBMol()
obc.ReadFile(mol, fname)
return makecclib(mol)
else:
print("Unable to load the %s reader from OpenBabel." % format)
return {}
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Bridge for using cclib data in PyQuante (http://pyquante.sourceforge.net)."""
from __future__ import print_function
import sys
try:
from PyQuante.Molecule import Molecule
except ImportError:
# Fail silently for now.
pass
def makepyquante(atomcoords, atomnos, charge=0, mult=1):
"""Create a PyQuante Molecule."""
return Molecule("notitle", list(zip(atomnos, atomcoords)), units="Angstrom",
charge=charge, multiplicity=mult)
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Contains all writers for standard chemical representations"""
from cclib.io.cjsonwriter import CJSON as CJSONWriter
from cclib.io.cmlwriter import CML
from cclib.io.xyzwriter import XYZ
from cclib.io.moldenwriter import MOLDEN
from cclib.io.wfxwriter import WFXWriter
from cclib.io.cjsonreader import CJSON as CJSONReader
# This allows users to type:
# from cclib.io import ccopen
# from cclib.io import ccread
# from cclib.io import ccwrite
# from cclib.io import URL_PATTERN
from cclib.io.ccio import ccopen
from cclib.io.ccio import ccread
from cclib.io.ccio import ccwrite
from cclib.io.ccio import URL_PATTERN
This diff is collapsed.
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
import json
from cclib.parser.data import ccData
class CJSON:
""" CJSON log file"""
def __init__(self, source, *args, **kwargs):
# Set the filename to source if it is a string.
# To Do: Add functionality to accept multiple cjson files and streams
if isinstance(source, str):
self.filename = source
else:
raise ValueError
self.datatype = {}
def read_cjson(self):
inputfile = self.filename
json_data = json.loads(open(inputfile).read())
# Actual update of attribute dictionary happens here
self.construct(json_data)
return self.datatype
def construct(self,json_data):
for Key, Value in ccData._attributes.items():
jsonKey = Value.jsonKey
attributePath = Value.attributePath.split(":")
if attributePath[0] == 'N/A':
continue
levels = len(attributePath)
if attributePath[0] in json_data:
l1_data_object = json_data[attributePath[0]]
if levels == 1:
if jsonKey in l1_data_object:
self.datatype[Key] = l1_data_object[jsonKey]
elif levels >= 2:
if attributePath[1] in l1_data_object:
l2_data_object = l1_data_object[attributePath[1]]
if jsonKey in l2_data_object:
self.datatype[Key] = l2_data_object[jsonKey]
if levels == 3 and attributePath[2] in l2_data_object:
l3_data_object = l2_data_object[attributePath[2]]
if jsonKey in l3_data_object:
self.datatype[Key] = l3_data_object[jsonKey]
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""A writer for chemical JSON (CJSON) files."""
try:
import openbabel as ob
_has_openbabel = True
except ImportError:
_has_openbabel = False
import os.path
import json
import numpy as np
from cclib.io import filewriter
from cclib.parser.data import ccData
class CJSON(filewriter.Writer):
"""A writer for chemical JSON (CJSON) files."""
def __init__(self, ccdata, terse=False, *args, **kwargs):
"""Initialize the chemical JSON writer object.
Inputs:
ccdata - An instance of ccData, parsed from a logfile.
"""
# Call the __init__ method of the superclass
super(CJSON, self).__init__(ccdata, terse=terse, *args, **kwargs)
def pathname(self, path):
"""Return filename without extension to be used as name."""
name = os.path.basename(os.path.splitext(path)[0])
return name
def generate_repr(self):
"""Generate the CJSON representation of the logfile data."""
cjson_dict = dict()
# Need to decide on a number format.
cjson_dict['chemical json'] = 0
if self.jobfilename is not None:
cjson_dict['name'] = self.pathname(self.jobfilename)
# These are properties that can be collected using Open Babel.
if _has_openbabel:
cjson_dict['smiles'] = self.pbmol.write('smiles')
cjson_dict['inchi'] = self.pbmol.write('inchi')
cjson_dict['inchikey'] = self.pbmol.write('inchikey')
cjson_dict['formula'] = self.pbmol.formula
# Incorporate Unit Cell into the chemical JSON.
# Iterate through the attribute list present in ccData. Depending on the
# availability of the attribute add it at the right 'level'.
for attributeName, Value in ccData._attributes.items():
if not hasattr(self.ccdata, attributeName):
continue
attributePath = Value.attributePath.split(":")
# Depth of the attribute in the CJSON.
levels = len(attributePath)
# The attributes which haven't been included in the CJSON format.
if attributePath[0] == 'N/A':
continue
if attributePath[0] not in cjson_dict:
cjson_dict[attributePath[0]] = dict()
l1_data_object = cjson_dict[attributePath[0]]
# 'moments' and 'atomcoords' key will contain processed data obtained from the output file.
if attributeName == 'moments' or attributeName == 'atomcoords' :
if attributeName == 'moments':
cjson_dict['properties'][ccData._attributes['moments'].jsonKey] = self._calculate_total_dipole_moment()
else:
cjson_dict['atoms']['coords'] = dict()
cjson_dict['atoms']['coords']['3d'] = self.ccdata.atomcoords[-1].flatten().tolist()
continue
if levels == 1:
self.set_JSON_attribute(l1_data_object, attributeName)
elif levels >= 2:
if attributePath[1] not in l1_data_object:
l1_data_object[attributePath[1]] = dict()
l2_data_object = l1_data_object[attributePath[1]]
if levels == 2:
self.set_JSON_attribute(l2_data_object, attributeName)
elif levels == 3:
if attributePath[2] not in l2_data_object:
l2_data_object[attributePath[2]] = dict()
l3_data_object = l2_data_object[attributePath[2]]
self.set_JSON_attribute(l3_data_object, attributeName)
# Attributes which are not directly obtained from the output files.
if hasattr(self.ccdata, 'moenergies') and hasattr(self.ccdata, 'homos'):
if 'energy' not in cjson_dict['properties']:
cjson_dict['properties']['energy'] = dict()
cjson_dict['properties']['energy']['alpha'] = dict()
cjson_dict['properties']['energy']['beta'] = dict()
homo_idx_alpha = int(self.ccdata.homos[0])
homo_idx_beta = int(self.ccdata.homos[-1])
energy_alpha_homo = self.ccdata.moenergies[0][homo_idx_alpha]
energy_alpha_lumo = self.ccdata.moenergies[0][homo_idx_alpha + 1]
energy_alpha_gap = energy_alpha_lumo - energy_alpha_homo
energy_beta_homo = self.ccdata.moenergies[-1][homo_idx_beta]
energy_beta_lumo = self.ccdata.moenergies[-1][homo_idx_beta + 1]
energy_beta_gap = energy_beta_lumo - energy_beta_homo
cjson_dict['properties']['energy']['alpha']['homo'] = energy_alpha_homo
cjson_dict['properties']['energy']['alpha']['gap'] = energy_alpha_gap
cjson_dict['properties']['energy']['beta']['homo'] = energy_beta_homo
cjson_dict['properties']['energy']['beta']['gap'] = energy_beta_gap
cjson_dict['properties']['energy']['total'] = self.ccdata.scfenergies[-1]
if hasattr(self.ccdata, 'atomnos'):
cjson_dict['atoms']['elements']['atom count'] = len(self.ccdata.atomnos)
cjson_dict['atoms']['elements']['heavy atom count'] = len([x for x in self.ccdata.atomnos if x > 1])
# Bond attributes:
if _has_openbabel and (len(self.ccdata.atomnos) > 1):
cjson_dict['bonds'] = dict()
cjson_dict['bonds']['connections'] = dict()
cjson_dict['bonds']['connections']['index'] = []
for bond in self.bond_connectivities:
cjson_dict['bonds']['connections']['index'].append(bond[0] + 1)
cjson_dict['bonds']['connections']['index'].append(bond[1] + 1)
cjson_dict['bonds']['order'] = [bond[2] for bond in self.bond_connectivities]
if _has_openbabel:
cjson_dict['properties']['molecular mass'] = self.pbmol.molwt
cjson_dict['diagram'] = self.pbmol.write(format='svg')
if self.terse:
return json.dumps(cjson_dict, cls=NumpyAwareJSONEncoder)
else:
return json.dumps(cjson_dict, cls=JSONIndentEncoder, sort_keys=True, indent=4)
def set_JSON_attribute(self, object, key):
"""
Args:
object: Python dictionary which is being appended with the key value.
key: cclib attribute name.
Returns:
None. The dictionary is modified to contain the attribute with the
cclib keyname as key
"""
if hasattr(self.ccdata, key):
object[ccData._attributes[key].jsonKey] = getattr(self.ccdata, key)
class NumpyAwareJSONEncoder(json.JSONEncoder):
"""A encoder for numpy.ndarray's obtained from the cclib attributes.
For all other types the json default encoder is called.
Do Not rename the 'default' method as it is required to be implemented
by any subclass of the json.JSONEncoder
"""
def default(self, obj):
if isinstance(obj, np.ndarray):
if obj.ndim == 1:
nan_list = obj.tolist()
return [None if np.isnan(x) else x for x in nan_list]
else:
return [self.default(obj[i]) for i in range(obj.shape[0])]
return json.JSONEncoder.default(self, obj)
class JSONIndentEncoder(json.JSONEncoder):
def __init__(self, *args, **kwargs):
super(JSONIndentEncoder, self).__init__(*args, **kwargs)
self.current_indent = 0
self.current_indent_str = ""
def encode(self, o):
# Special Processing for lists
if isinstance(o, (list, tuple)):
primitives_only = True
for item in o:
if isinstance(item, (list, tuple, dict)):
primitives_only = False
break
output = []
if primitives_only:
for item in o:
output.append(json.dumps(item, cls=NumpyAwareJSONEncoder))
return "[ " + ", ".join(output) + " ]"
else:
self.current_indent += self.indent
self.current_indent_str = "".join([" " for x in range(self.current_indent)])
for item in o:
output.append(self.current_indent_str + self.encode(item))
self.current_indent -= self.indent
self.current_indent_str = "".join([" " for x in range(self.current_indent)])
return "[\n" + ",\n".join(output) + "\n" + self.current_indent_str + "]"
elif isinstance(o, dict):
output = []
self.current_indent += self.indent
self.current_indent_str = "".join([" " for x in range(self.current_indent)])
for key, value in o.items():
output.append(self.current_indent_str + json.dumps(key, cls=NumpyAwareJSONEncoder) + ": " +
str(self.encode(value)))
self.current_indent -= self.indent
self.current_indent_str = "".join([" " for x in range(self.current_indent)])
return "{\n" + ",\n".join(output) + "\n" + self.current_indent_str + "}"
elif isinstance(o, np.generic):
return json.dumps(np.asscalar(o), cls=NumpyAwareJSONEncoder)
else:
return json.dumps(o, cls=NumpyAwareJSONEncoder)
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""A writer for chemical markup language (CML) files."""
try:
import openbabel as ob
_has_openbabel = True
except ImportError:
_has_openbabel = False
import xml.etree.cElementTree as ET
from cclib.io import filewriter
class CML(filewriter.Writer):
"""A writer for chemical markup language (CML) files."""
def __init__(self, ccdata, *args, **kwargs):
"""Initialize the CML writer object.
Inputs:
ccdata - An instance of ccData, parsed from a logfile.
"""
# Call the __init__ method of the superclass
super(CML, self).__init__(ccdata, *args, **kwargs)
def generate_repr(self):
"""Generate the CML representation of the logfile data."""
# Create the base molecule.
molecule = ET.Element('molecule')
d = {
# Write the namespace directly.
'xmlns': 'http://www.xml-cml.org/schema',
}
if self.jobfilename is not None:
d['id'] = self.jobfilename
_set_attrs(molecule, d)
# Form the listing of all the atoms present.
atomArray = ET.SubElement(molecule, 'atomArray')
if hasattr(self.ccdata, 'atomcoords') and hasattr(self.ccdata, 'atomnos'):
elements = [self.pt.element[Z] for Z in self.ccdata.atomnos]
for atomid in range(self.ccdata.natom):
atom = ET.SubElement(atomArray, 'atom')
x, y, z = self.ccdata.atomcoords[-1][atomid].tolist()
d = {
'id': 'a{}'.format(atomid + 1),
'elementType': elements[atomid],
'x3': '{:.10f}'.format(x),
'y3': '{:.10f}'.format(y),
'z3': '{:.10f}'.format(z),
}
_set_attrs(atom, d)
# Form the listing of all the bonds present.
bondArray = ET.SubElement(molecule, 'bondArray')
if _has_openbabel:
for bc in self.bond_connectivities:
bond = ET.SubElement(bondArray, 'bond')
d = {
'atomRefs2': 'a{} a{}'.format(bc[0] + 1, bc[1] + 1),
'order': str(bc[2]),
}
_set_attrs(bond, d)
_indent(molecule)
return _tostring(molecule)
def _set_attrs(element, d):
"""Set all the key-value pairs from a dictionary as element
attributes.
"""
for (k, v) in d.items():
element.set(k, v)
def _indent(elem, level=0):
"""An in-place pretty-print indenter for XML."""
i = "\n" + (level * " ")
if len(elem):
if not elem.text or not elem.text.strip():
elem.text = i + " "
if not elem.tail or not elem.tail.strip():
elem.tail = i
for elem in elem:
_indent(elem, level+1)
if not elem.tail or not elem.tail.strip():
elem.tail = i
else:
if level and (not elem.tail or not elem.tail.strip()):
elem.tail = i
def _tostring(element, xml_declaration=True, encoding='utf-8', method='xml'):
"""A reimplementation of tostring() found in ElementTree."""
class dummy:
pass
data = []
file = dummy()
file.write = data.append
ET.ElementTree(element).write(file,
xml_declaration=xml_declaration,
encoding=encoding,
method=method)
return b''.join(data).decode(encoding)
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Generic file writer and related tools"""
try:
from cclib.bridge import makeopenbabel
import openbabel as ob
import pybel as pb
_has_openbabel = True
except ImportError:
_has_openbabel = False
from math import sqrt
from collections import Iterable
from cclib.parser.utils import PeriodicTable
class MissingAttributeError(Exception):
pass
class Writer(object):
"""Abstract class for writer objects."""
required_attrs = ()
def __init__(self, ccdata, jobfilename=None, indices=None, terse=False,
*args, **kwargs):
"""Initialize the Writer object.
This should be called by a subclass in its own __init__ method.
Inputs:
ccdata - An instance of ccData, parsed from a logfile.
jobfilename - The filename of the parsed logfile.
indices - One or more indices for extracting specific geometries/etc. (zero-based)
terse - Whether to print the terse version of the output file - currently limited to cjson/json formats
"""
self.ccdata = ccdata
self.jobfilename = jobfilename
self.indices = indices
self.terse = terse
self.pt = PeriodicTable()
self._check_required_attributes()
# Open Babel isn't necessarily present.
if _has_openbabel:
# Generate the Open Babel/Pybel representation of the molecule.
# Used for calculating SMILES/InChI, formula, MW, etc.
self.obmol, self.pbmol = self._make_openbabel_from_ccdata()
self.bond_connectivities = self._make_bond_connectivity_from_openbabel(self.obmol)
self._fix_indices()
def generate_repr(self):
"""Generate the written representation of the logfile data.
This should be overriden by all the subclasses inheriting from
Writer.
"""
raise NotImplementedError(
'generate_repr is not implemented for ' + str(type(self)))
def _calculate_total_dipole_moment(self):
"""Calculate the total dipole moment."""
return sqrt(sum(self.ccdata.moments[1] ** 2))
def _check_required_attributes(self):
"""Check if required attributes are present in ccdata."""
missing = [x for x in self.required_attrs
if not hasattr(self.ccdata, x)]
if missing:
missing = ' '.join(missing)
raise MissingAttributeError(
'Could not parse required attributes to write file: ' + missing)
def _make_openbabel_from_ccdata(self):
"""Create Open Babel and Pybel molecules from ccData.
"""
obmol = makeopenbabel(self.ccdata.atomcoords,
self.ccdata.atomnos,
charge=self.ccdata.charge,
mult=self.ccdata.mult)
if self.jobfilename is not None:
obmol.SetTitle(self.jobfilename)
return (obmol, pb.Molecule(obmol))
def _make_bond_connectivity_from_openbabel(self, obmol):
"""Based upon the Open Babel/Pybel molecule, create a list of tuples
to represent bonding information, where the three integers are
the index of the starting atom, the index of the ending atom,
and the bond order.
"""
bond_connectivities = []
for obbond in ob.OBMolBondIter(obmol):
bond_connectivities.append((obbond.GetBeginAtom().GetIndex(),
obbond.GetEndAtom().GetIndex(),
obbond.GetBondOrder()))
return bond_connectivities
def _fix_indices(self):
"""Clean up the index container type and remove zero-based indices to
prevent duplicate structures and incorrect ordering when
indices are later sorted.
"""
if not self.indices:
self.indices = set()
elif not isinstance(self.indices, Iterable):
self.indices = set([self.indices])
# This is the most likely place to get the number of
# geometries from.
if hasattr(self.ccdata, 'atomcoords'):
lencoords = len(self.ccdata.atomcoords)
indices = set()
for i in self.indices:
if i < 0:
i += lencoords
indices.add(i)
self.indices = indices
return
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""A writer for MOLDEN format files."""
import os.path
import math
import decimal
from cclib.parser import utils
from cclib.io import filewriter
def round_molden(num, p=6):
"""Molden style number rounding in [Atoms] section."""
# Digit at pth position after dot.
p_digit = math.floor(abs(num) * 10 ** p) % 10
# If the 6th digit after dot is greater than 5, but is not 7,
# round the number upto 6th place.
# Else truncate at 6th digit after dot.
if p_digit > 5 and p_digit != 7:
return round(num, p)
if num >= 0:
return math.floor(num * 10 ** p) / 10 ** p
else:
return math.ceil(num * 10 ** p) / 10 ** p
class MOLDEN(filewriter.Writer):
"""A writer for MOLDEN files."""
required_attrs = ('atomcoords', 'atomnos', 'natom')
def _title(self, path):
"""Return filename without extension to be used as title."""
title = os.path.basename(os.path.splitext(path)[0])
return title
def _coords_from_ccdata(self, index):
"""Create [Atoms] section using geometry at the given index."""
elements = [self.pt.element[Z] for Z in self.ccdata.atomnos]
atomcoords = self.ccdata.atomcoords[index]
atomnos = self.ccdata.atomnos
nos = range(self.ccdata.natom)
# element_name number atomic_number x y z
atom_template = '{:2s} {:5d} {:2d} {:12.6f} {:12.6f} {:12.6f}'
lines = []
for element, no, atomno, coord in zip(elements, nos, atomnos,
atomcoords):
x, y, z = map(round_molden, coord)
lines.append(atom_template.format(element, no + 1, atomno,
x, y, z))
return lines
def _gto_from_ccdata(self):
"""Create [GTO] section using gbasis.
atom_sequence_number1 0
shell_label number_of_primitives 1.00
exponent_primitive_1 contraction_coeff_1 (contraction_coeff_1)
...
empty line
atom_sequence__number2 0
"""
gbasis = self.ccdata.gbasis
label_template = '{:s} {:5d} 1.00'
basis_template = '{:15.9e} {:15.9e}'
lines = []
for no, basis in enumerate(gbasis):
lines.append('{:3d} 0'.format(no + 1))
for prims in basis:
lines.append(label_template.format(prims[0].lower(),
len(prims[1])))
for prim in prims[1]:
lines.append(basis_template.format(prim[0], prim[1]))
lines.append('')
lines.append('')
return lines
def _scfconv_from_ccdata(self):
"""Create [SCFCONV] section using gbasis.
scf-first 1 THROUGH 12
-672.634394
...
-673.590571
-673.590571
"""
lines = ["scf-first 1 THROUGH %d" % len(self.ccdata.scfenergies)]
for scfenergy in self.ccdata.scfenergies:
lines.append('{:15.6f}'.format(scfenergy))
return lines
def _rearrange_mocoeffs(self, mocoeffs):
"""Rearrange cartesian F functions in mocoeffs.
Molden's order:
xxx, yyy, zzz, xyy, xxy, xxz, xzz, yzz, yyz, xyz
cclib's order:
XXX, YYY, ZZZ, XXY, XXZ, YYX, YYZ, ZZX, ZZY, XYZ
cclib's order can be converted by:
moving YYX two indexes ahead, and
moving YYZ two indexes back.
"""
aonames = self.ccdata.aonames
mocoeffs = mocoeffs.tolist()
pos_yyx = [key for key, val in enumerate(aonames) if '_YYX' in val]
pos_yyz = [key for key, val in enumerate(aonames) if '_YYZ' in val]
if pos_yyx:
for pos in pos_yyx:
mocoeffs.insert(pos-2, mocoeffs.pop(pos))
if pos_yyz:
for pos in pos_yyz:
mocoeffs.insert(pos+2, mocoeffs.pop(pos))
return mocoeffs
def _mo_from_ccdata(self):
"""Create [MO] section.
Sym= symmetry_label_1
Ene= mo_energy_1
Spin= (Alpha|Beta)
Occup= mo_occupation_number_1
ao_number_1 mo_coefficient_1
...
ao_number_n mo_coefficient_n
...
"""
moenergies = self.ccdata.moenergies
mocoeffs = self.ccdata.mocoeffs
homos = self.ccdata.homos
mult = self.ccdata.mult
has_syms = False
lines = []
# Sym attribute is optional in [MO] section.
if hasattr(self.ccdata, 'mosyms'):
has_syms = True
syms = self.ccdata.mosyms
spin = 'Alpha'
for i in range(mult):
for j in range(len(moenergies[i])):
if has_syms:
lines.append(' Sym= %s' % syms[i][j])
moenergy = utils.convertor(moenergies[i][j], 'eV', 'hartree')
lines.append(' Ene= {:10.4f}'.format(moenergy))
lines.append(' Spin= %s' % spin)
if j <= homos[i]:
lines.append(' Occup= {:10.6f}'.format(2.0 / mult))
else:
lines.append(' Occup= {:10.6f}'.format(0.0))
# Rearrange mocoeffs according to Molden's lexicographical order.
mocoeffs[i][j] = self._rearrange_mocoeffs(mocoeffs[i][j])
for k, mocoeff in enumerate(mocoeffs[i][j]):
lines.append('{:4d} {:10.6f}'.format(k + 1, mocoeff))
spin = 'Beta'
return lines
def generate_repr(self):
"""Generate the MOLDEN representation of the logfile data."""
molden_lines = ['[Molden Format]']
# Title of file.
if self.jobfilename is not None:
molden_lines.append('[Title]')
molden_lines.append(self._title(self.jobfilename))
# Coordinates for the Electron Density/Molecular orbitals.
# [Atoms] (Angs|AU)
unit = "Angs"
molden_lines.append('[Atoms] %s' % unit)
# Last set of coordinates for geometry optimization runs.
index = -1
molden_lines.extend(self._coords_from_ccdata(index))
# Either both [GTO] and [MO] should be present or none of them.
if hasattr(self.ccdata, 'gbasis') and hasattr(self.ccdata, 'mocoeffs')\
and hasattr(self.ccdata, 'moenergies'):
molden_lines.append('[GTO]')
molden_lines.extend(self._gto_from_ccdata())
molden_lines.append('[MO]')
molden_lines.extend(self._mo_from_ccdata())
# Omitting until issue #390 is resolved.
# https://github.com/cclib/cclib/issues/390
# if hasattr(self.ccdata, 'scfenergies'):
# if len(self.ccdata.scfenergies) > 1:
# molden_lines.append('[SCFCONV]')
# molden_lines.extend(self._scfconv_from_ccdata())
# molden_lines.append('')
return '\n'.join(molden_lines)
class MoldenReformatter(object):
"""Reformat Molden output files."""
def __init__(self, filestring):
self.filestring = filestring
def scinotation(self, num):
"""Convert Molden style number formatting to scientific notation.
0.9910616900D+02 --> 9.910617e+01
"""
num = num.replace('D', 'e')
return str('%.9e' % decimal.Decimal(num))
def reformat(self):
"""Reformat Molden output file to:
- use scientific notation,
- split sp molecular orbitals to s and p, and
- replace multiple spaces with single."""
filelines = iter(self.filestring.split("\n"))
lines = []
for line in filelines:
line = line.replace('\n', '')
# Replace multiple spaces with single spaces.
line = ' '.join(line.split())
# Check for [Title] section.
if '[title]' in line.lower():
line = next(filelines)
# Exclude SCFCONV section until issue #390 is resolved.
# https://github.com/cclib/cclib/issues/390
if '[scfconv]' in line.lower():
break
# Although Molden format specifies Sym in [MO] section,
# the Molden program does not print it.
if 'sym' in line.lower():
continue
# Convert D notation to scientific notation.
if 'D' in line:
vals = line.split()
vals = [self.scinotation(i) for i in vals]
lines.append(' '.join(vals))
# Convert sp to s and p orbitals.
elif 'sp' in line:
n_prim = int(line.split()[1])
new_s = ['s ' + str(n_prim) + ' 1.00']
new_p = ['p ' + str(n_prim) + ' 1.00']
while n_prim > 0:
n_prim -= 1
line = next(filelines).split()
new_s.append(self.scinotation(line[0]) + ' '
+ self.scinotation(line[1]))
new_p.append(self.scinotation(line[0]) + ' '
+ self.scinotation(line[2]))
lines.extend(new_s)
lines.extend(new_p)
else:
lines.append(line)
return '\n'.join(lines)
This diff is collapsed.
# -*- coding: utf-8 -*-
#
# Copyright (c) 2017, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""A writer for XYZ (Cartesian coordinate) files."""
from cclib.io import filewriter
class XYZ(filewriter.Writer):
"""A writer for XYZ (Cartesian coordinate) files."""
def __init__(self, ccdata, splitfiles=False,
firstgeom=False, lastgeom=False, allgeom=False