Hide keyboard shortcuts

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. 

7 

8"""Bridge for using cclib data in horton (http://theochem.github.io/horton).""" 

9 

10import numpy 

11from cclib.parser.data import ccData 

12from cclib.parser.utils import find_package 

13 

14_found_iodata = find_package("iodata") 

15 

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 

21 

22 

23def check_horton(): 

24 if not _found_iodata: 

25 raise ImportError("You must install `horton` to use this function.") 

26 

27 

28def makehorton(data): 

29 """ Create horton IOData object from ccData object """ 

30 

31 check_horton() 

32 attributes = {} 

33 

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 

86 

87 return IOData(**attributes) # Pass collected attributes into IOData constructor 

88 

89 

90def makecclib(iodat): 

91 """ Create cclib ccData object from horton IOData object """ 

92 

93 check_horton() 

94 attributes = {} 

95 

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 

120 

121 return ccData(attributes)