Coverage for cclib/io/cmlwriter.py : 21%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2#
3# Copyright (c) 2017, the cclib development team
4#
5# This file is part of cclib (http://cclib.github.io) and is distributed under
6# the terms of the BSD 3-Clause License.
8"""A writer for chemical markup language (CML) files."""
10import xml.etree.ElementTree as ET
12from cclib.io import filewriter
13from cclib.parser.utils import find_package
15_has_openbabel = find_package("openbabel")
18class CML(filewriter.Writer):
19 """A writer for chemical markup language (CML) files."""
21 def __init__(self, ccdata, *args, **kwargs):
22 """Initialize the CML writer object.
24 Inputs:
25 ccdata - An instance of ccData, parsed from a logfile.
26 """
28 # Call the __init__ method of the superclass
29 super(CML, self).__init__(ccdata, *args, **kwargs)
31 def generate_repr(self):
32 """Generate the CML representation of the logfile data."""
34 # Create the base molecule.
35 molecule = ET.Element('molecule')
36 d = {
37 # Write the namespace directly.
38 'xmlns': 'http://www.xml-cml.org/schema',
39 }
40 if self.jobfilename is not None:
41 d['id'] = self.jobfilename
42 _set_attrs(molecule, d)
44 # Form the listing of all the atoms present.
45 atomArray = ET.SubElement(molecule, 'atomArray')
46 if hasattr(self.ccdata, 'atomcoords') and hasattr(self.ccdata, 'atomnos'):
47 elements = [self.pt.element[Z] for Z in self.ccdata.atomnos]
48 for atomid in range(self.ccdata.natom):
49 atom = ET.SubElement(atomArray, 'atom')
50 x, y, z = self.ccdata.atomcoords[-1][atomid].tolist()
51 d = {
52 'id': 'a{}'.format(atomid + 1),
53 'elementType': elements[atomid],
54 'x3': '{:.10f}'.format(x),
55 'y3': '{:.10f}'.format(y),
56 'z3': '{:.10f}'.format(z),
57 }
58 _set_attrs(atom, d)
60 # Form the listing of all the bonds present.
61 bondArray = ET.SubElement(molecule, 'bondArray')
62 if _has_openbabel:
63 for bc in self.bond_connectivities:
64 bond = ET.SubElement(bondArray, 'bond')
65 d = {
66 'atomRefs2': 'a{} a{}'.format(bc[0] + 1, bc[1] + 1),
67 'order': str(bc[2]),
68 }
69 _set_attrs(bond, d)
71 _indent(molecule)
73 return _tostring(molecule)
76def _set_attrs(element, d):
77 """Set all the key-value pairs from a dictionary as element
78 attributes.
79 """
80 for (k, v) in d.items():
81 element.set(k, v)
82 return
85def _indent(elem, level=0):
86 """An in-place pretty-print indenter for XML."""
87 i = "\n" + (level * " ")
88 if len(elem):
89 if not elem.text or not elem.text.strip():
90 elem.text = i + " "
91 if not elem.tail or not elem.tail.strip():
92 elem.tail = i
93 for elem in elem:
94 _indent(elem, level+1)
95 if not elem.tail or not elem.tail.strip():
96 elem.tail = i
97 else:
98 if level and (not elem.tail or not elem.tail.strip()):
99 elem.tail = i
102def _tostring(element, xml_declaration=True, encoding='utf-8', method='xml'):
103 """A reimplementation of tostring() found in ElementTree."""
104 class dummy:
105 pass
106 data = []
107 file = dummy()
108 file.write = data.append
109 ET.ElementTree(element).write(file,
110 xml_declaration=xml_declaration,
111 encoding=encoding,
112 method=method)
113 return b''.join(data).decode(encoding)
116del find_package