File: cmlwriter.py

package info (click to toggle)
cclib-data 1.6.2-2
  • links: PTS, VCS
  • area: non-free
  • in suites: bookworm, bullseye, sid
  • size: 87,912 kB
  • sloc: python: 16,440; sh: 131; makefile: 79; cpp: 31
file content (116 lines) | stat: -rw-r--r-- 3,701 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# -*- 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."""

import xml.etree.cElementTree as ET

from cclib.io import filewriter
from cclib.parser.utils import find_package

_has_openbabel = find_package("openbabel")


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)
    return


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)


del find_package