Coverage for cclib/bridge/cclib2horton.py : 73%
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) 2020, 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"""Bridge for using cclib data in horton (http://theochem.github.io/horton)."""
10import numpy
11from cclib.parser.data import ccData
12from cclib.parser.utils import find_package
14_found_iodata = find_package("iodata")
16# Detect whether iodata (part of horton 3) is present or not
17# Horton 3 is divided into smaller (sub)packages that each take different functionalities.
18if _found_iodata:
19 from iodata import IOData
20 from iodata.orbitals import MolecularOrbitals
23def check_horton():
24 if not _found_iodata:
25 raise ImportError("You must install `horton` to use this function.")
28def makehorton(data):
29 """ Create horton IOData object from ccData object """
31 check_horton()
32 attributes = {}
34 # In horton 3 IOData, inputs are type-checked -- thus the bridge also verifies the types.
35 # if coordinates are known; numpy.ndarray (size natom-by-3)
36 if hasattr(data, "atomcoords"):
37 attributes["atcoords"] = numpy.asanyarray(data.atomcoords)[-1]
38 # if atomic numbers are known; numpy.ndarray (size natom)
39 if hasattr(data, "atomnos"):
40 attributes["atnums"] = numpy.asanyarray(data.atomnos)
41 # if orbital coefficients known; iodata.orbitals.MolecularOrbitals
42 if hasattr(data, "mocoeffs"):
43 # First build a dictionary of inputs that will be passed to the constructor of
44 # horton3's MolecularOrbitals object.
45 moattr = {
46 "kind": "restricted",
47 "norba": data.nbasis,
48 "norbb": None,
49 # In horton 3, occupation in MOs are represented as 1's (occupied) and
50 # 0's (unoccupied). Beta orbitals follow directly after alpha orbitals, forming
51 # 1D array.
52 "occs": numpy.concatenate(
53 numpy.ones(data.homos[0]), numpy.zeros(data.nbasis - data.homos[0])
54 ),
55 "coeffs": data.mocoeffs[0],
56 "energies": None,
57 "irreps": None,
58 }
59 # and if unrestricted:
60 if len(mocoeffs) == 2:
61 moattr["kind"] = "unrestricted"
62 moattr["norbb"] = data.nbasis
63 moattr["coeffs"].append(data.mocoeffs[1].T)
64 moattr["occs"].append(
65 numpy.concatenate(
66 numpy.ones(data.homos[1]),
67 numpy.zeros(data.nbasis - data.homos[1]),
68 )
69 )
70 # Then construct MolecularOrbitals object
71 attributes["mo"] = MolecularOrbitals(**moattr)
72 # if multiplicity known; float / should not be set when mocoeffs present
73 # Refer to IOData code:
74 # https://github.com/theochem/iodata/blob/b36513d162f99b57264005583701c6987037839c/iodata/iodata.py#L174
75 if (
76 hasattr(data, "mult")
77 and not hasattr(data, "mocoeffs")
78 ):
79 attributes["spinpol"] = data.mult - 1 # horton has 2S+1, iodata has 2S
80 # if pseudopotentials exist; numpy.ndarray (size natom)
81 if hasattr(data, "coreelectrons"):
82 attributes["atcorenums"] = data.atomnos - numpy.asanyarray(data.coreelectrons)
83 # if mulliken charges are known; dict of numpy.ndarrays (size natom)
84 if hasattr(data, "atomcharges"):
85 attributes["atcharges"] = data.atomcharges
87 return IOData(**attributes) # Pass collected attributes into IOData constructor
90def makecclib(iodat):
91 """ Create cclib ccData object from horton IOData object """
93 check_horton()
94 attributes = {}
96 # Horton 3 IOData class uses attr and does not have __dict__.
97 # In horton 3, some attributes have a default value of None.
98 # Therefore, second hasattr statement is needed for mo attribute.
99 if hasattr(iodat, "atcoords"):
100 # cclib parses the whole history of coordinates in the list, horton keeps the last one.
101 attributes["atomcoords"] = [iodat.atcoords]
102 if hasattr(iodat, "mo") and hasattr(iodat.mo, "norba"):
103 # MO coefficient should be transposed to match the dimensions.
104 attributes["mocoeffs"] = [iodat.mo.coeffs[: iodat.mo.norba].T]
105 if iodat.mo.kind == "unrestricted":
106 attributes["mocoeffs"].append(iodat.mo.coeffs[iodat.mo.norba :].T)
107 if hasattr(iodat, "spinpol") and isinstance(iodat.spinpol, int):
108 # IOData stores 2S, ccData stores 2S+1.
109 attributes["mult"] = iodat.spinpol + 1
110 if hasattr(iodat, "atnums"):
111 attributes["atnums"] = numpy.asanyarray(iodat.atnums)
112 if hasattr(iodat, "atcorenums") and isinstance(iodat.atnums, numpy.ndarray):
113 # cclib stores num of electrons screened out by pseudopotential
114 # horton stores num of electrons after applying pseudopotential
115 attributes["coreelectrons"] = numpy.asanyarray(iodat.atnums) - numpy.asanyarray(
116 iodat.atcorenums
117 )
118 if hasattr(iodat, "atcharges"):
119 attributes["atomcharges"] = iodat.atcharges
121 return ccData(attributes)