cclib 1.8.1__tar.gz → 2.0a1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. {cclib-1.8.1/cclib.egg-info → cclib-2.0a1}/PKG-INFO +2 -2
  2. {cclib-1.8.1 → cclib-2.0a1}/README.md +1 -1
  3. {cclib-1.8.1 → cclib-2.0a1}/cclib/__init__.py +19 -7
  4. cclib-2.0a1/cclib/_version.py +1 -0
  5. cclib-2.0a1/cclib/attribute_parsers/__init__.py +26 -0
  6. cclib-2.0a1/cclib/attribute_parsers/aooverlaps.py +57 -0
  7. cclib-2.0a1/cclib/attribute_parsers/atombasis.py +176 -0
  8. cclib-2.0a1/cclib/attribute_parsers/atomcharges.py +270 -0
  9. cclib-2.0a1/cclib/attribute_parsers/atomcoords.py +94 -0
  10. cclib-2.0a1/cclib/attribute_parsers/atommasses.py +66 -0
  11. cclib-2.0a1/cclib/attribute_parsers/atomnos.py +91 -0
  12. cclib-2.0a1/cclib/attribute_parsers/base_parser.py +20 -0
  13. cclib-2.0a1/cclib/attribute_parsers/charge.py +70 -0
  14. cclib-2.0a1/cclib/attribute_parsers/coreelectrons.py +77 -0
  15. {cclib-1.8.1/cclib/parser → cclib-2.0a1/cclib/attribute_parsers}/data.py +45 -91
  16. cclib-2.0a1/cclib/attribute_parsers/gbasis.py +299 -0
  17. cclib-2.0a1/cclib/attribute_parsers/mocoeffs.py +73 -0
  18. cclib-2.0a1/cclib/attribute_parsers/moenergies.py +65 -0
  19. cclib-2.0a1/cclib/attribute_parsers/mosyms.py +206 -0
  20. cclib-2.0a1/cclib/attribute_parsers/mult.py +67 -0
  21. cclib-2.0a1/cclib/attribute_parsers/natom.py +57 -0
  22. cclib-2.0a1/cclib/attribute_parsers/nbasis.py +78 -0
  23. cclib-2.0a1/cclib/attribute_parsers/nmo.py +53 -0
  24. cclib-2.0a1/cclib/attribute_parsers/scfenergies.py +56 -0
  25. cclib-2.0a1/cclib/attribute_parsers/scftargets.py +72 -0
  26. cclib-2.0a1/cclib/attribute_parsers/scfvalues.py +70 -0
  27. cclib-2.0a1/cclib/attributes/__init__.py +6 -0
  28. cclib-2.0a1/cclib/attributes/attribute.py +811 -0
  29. cclib-2.0a1/cclib/collection/__init__.py +8 -0
  30. cclib-2.0a1/cclib/collection/collection.py +41 -0
  31. cclib-2.0a1/cclib/combinator/__init__.py +5 -0
  32. cclib-2.0a1/cclib/combinator/combinator.py +41 -0
  33. cclib-2.0a1/cclib/driver/__init__.py +8 -0
  34. cclib-2.0a1/cclib/driver/ccdriver.py +526 -0
  35. cclib-2.0a1/cclib/file_handler/__init__.py +8 -0
  36. cclib-1.8.1/cclib/parser/logfilewrapper.py → cclib-2.0a1/cclib/file_handler/file_handler.py +121 -4
  37. cclib-2.0a1/cclib/file_handler/utils.py +270 -0
  38. cclib-2.0a1/cclib/io/__init__.py +25 -0
  39. cclib-2.0a1/cclib/io/ccio.py +339 -0
  40. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/cjsonwriter.py +3 -3
  41. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/moldenwriter.py +3 -6
  42. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/bader.py +1 -4
  43. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/ddec.py +5 -12
  44. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/hirshfeld.py +1 -0
  45. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/volume.py +2 -1
  46. cclib-2.0a1/cclib/parser/__init__.py +18 -0
  47. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/daltonparser.py +0 -1
  48. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/fchkparser.py +0 -1
  49. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/gamessdatparser.py +0 -1
  50. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/gamessparser.py +0 -1
  51. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/gamessukparser.py +0 -1
  52. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/gaussianparser.py +9 -6
  53. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/jaguarparser.py +0 -1
  54. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/molcasparser.py +9 -9
  55. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/molproparser.py +0 -1
  56. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/nboparser.py +0 -1
  57. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/nwchemparser.py +3 -4
  58. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/orcaparser.py +37 -18
  59. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/psi4parser.py +3 -8
  60. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/qchemparser.py +9 -13
  61. cclib-2.0a1/cclib/parser/utils.py +270 -0
  62. {cclib-1.8.1 → cclib-2.0a1}/cclib/scripts/ccget.py +0 -1
  63. cclib-2.0a1/cclib/tree/__init__.py +5 -0
  64. cclib-2.0a1/cclib/tree/tree.py +87 -0
  65. {cclib-1.8.1 → cclib-2.0a1/cclib.egg-info}/PKG-INFO +2 -2
  66. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/SOURCES.txt +36 -2
  67. cclib-1.8.1/cclib/_version.py +0 -1
  68. cclib-1.8.1/cclib/io/__init__.py +0 -21
  69. cclib-1.8.1/cclib/io/ccio.py +0 -444
  70. cclib-1.8.1/cclib/parser/__init__.py +0 -37
  71. {cclib-1.8.1 → cclib-2.0a1}/ANNOUNCE +0 -0
  72. {cclib-1.8.1 → cclib-2.0a1}/LICENSE +0 -0
  73. {cclib-1.8.1 → cclib-2.0a1}/MANIFEST.in +0 -0
  74. {cclib-1.8.1 → cclib-2.0a1}/THANKS +0 -0
  75. {cclib-1.8.1/cclib/parser → cclib-2.0a1/cclib/attribute_parsers}/utils.py +0 -0
  76. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/__init__.py +0 -0
  77. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2ase.py +0 -0
  78. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2biopython.py +0 -0
  79. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2horton.py +0 -0
  80. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2openbabel.py +0 -0
  81. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2psi4.py +0 -0
  82. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2pyquante.py +0 -0
  83. {cclib-1.8.1 → cclib-2.0a1}/cclib/bridge/cclib2pyscf.py +0 -0
  84. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/cjsonreader.py +0 -0
  85. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/cmlwriter.py +0 -0
  86. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/filereader.py +0 -0
  87. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/filewriter.py +0 -0
  88. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/wfxwriter.py +0 -0
  89. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/xyzreader.py +0 -0
  90. {cclib-1.8.1 → cclib-2.0a1}/cclib/io/xyzwriter.py +0 -0
  91. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/__init__.py +0 -0
  92. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/bickelhaupt.py +0 -0
  93. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/calculationmethod.py +0 -0
  94. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/cda.py +0 -0
  95. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/cm5.py +0 -0
  96. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/cspa.py +0 -0
  97. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/density.py +0 -0
  98. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/electrons.py +0 -0
  99. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/fragments.py +0 -0
  100. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/lpa.py +0 -0
  101. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/mbo.py +0 -0
  102. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/moments.py +0 -0
  103. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/mpa.py +0 -0
  104. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/nuclear.py +0 -0
  105. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/opa.py +0 -0
  106. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/orbitals.py +0 -0
  107. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/population.py +0 -0
  108. {cclib-1.8.1 → cclib-2.0a1}/cclib/method/stockholder.py +0 -0
  109. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/adfparser.py +0 -0
  110. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/logfileparser.py +0 -0
  111. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/mopacparser.py +0 -0
  112. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/psi3parser.py +0 -0
  113. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/turbomoleparser.py +0 -0
  114. {cclib-1.8.1 → cclib-2.0a1}/cclib/parser/xtbparser.py +0 -0
  115. {cclib-1.8.1 → cclib-2.0a1}/cclib/progress/__init__.py +0 -0
  116. {cclib-1.8.1 → cclib-2.0a1}/cclib/progress/textprogress.py +0 -0
  117. {cclib-1.8.1 → cclib-2.0a1}/cclib/py.typed +0 -0
  118. {cclib-1.8.1 → cclib-2.0a1}/cclib/scripts/__init__.py +0 -0
  119. {cclib-1.8.1 → cclib-2.0a1}/cclib/scripts/ccframe.py +0 -0
  120. {cclib-1.8.1 → cclib-2.0a1}/cclib/scripts/ccwrite.py +0 -0
  121. {cclib-1.8.1 → cclib-2.0a1}/cclib/scripts/cda.py +0 -0
  122. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/dependency_links.txt +0 -0
  123. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/entry_points.txt +0 -0
  124. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/not-zip-safe +0 -0
  125. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/requires.txt +0 -0
  126. {cclib-1.8.1 → cclib-2.0a1}/cclib.egg-info/top_level.txt +0 -0
  127. {cclib-1.8.1 → cclib-2.0a1}/pyproject.toml +0 -0
  128. {cclib-1.8.1 → cclib-2.0a1}/setup.cfg +0 -0
  129. {cclib-1.8.1 → cclib-2.0a1}/tools/versioningit_override.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cclib
3
- Version: 1.8.1
3
+ Version: 2.0a1
4
4
  Summary: parsers and algorithms for computational chemistry
5
5
  Author-email: cclib development team <cclib@googlegroups.com>
6
6
  Maintainer-email: cclib development team <cclib@googlegroups.com>
@@ -79,7 +79,7 @@ Requires-Dist: cclib[bridges]; extra == "all"
79
79
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8280878.svg)](https://doi.org/10.5281/zenodo.8280878)
80
80
  [![PyPI version](http://img.shields.io/pypi/v/cclib.svg?style=flat)](https://pypi.python.org/pypi/cclib)
81
81
  [![GitHub release](https://img.shields.io/github/release/cclib/cclib.svg?style=flat)](https://github.com/cclib/cclib/releases)
82
- [![build status](https://github.com/cclib/cclib/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/cclib/cclib/actions/workflows/cclib_pytest.yml)
82
+ [![build status](https://github.com/cclib/cclib/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/cclib/cclib/actions/workflows/ci.yml)
83
83
  [![license](http://img.shields.io/badge/license-BSD-blue.svg?style=flat)](https://github.com/cclib/cclib/blob/master/LICENSE)
84
84
 
85
85
  <img src="./logo.png" alt="cclib logo" width="100" />
@@ -3,7 +3,7 @@
3
3
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.8280878.svg)](https://doi.org/10.5281/zenodo.8280878)
4
4
  [![PyPI version](http://img.shields.io/pypi/v/cclib.svg?style=flat)](https://pypi.python.org/pypi/cclib)
5
5
  [![GitHub release](https://img.shields.io/github/release/cclib/cclib.svg?style=flat)](https://github.com/cclib/cclib/releases)
6
- [![build status](https://github.com/cclib/cclib/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/cclib/cclib/actions/workflows/cclib_pytest.yml)
6
+ [![build status](https://github.com/cclib/cclib/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/cclib/cclib/actions/workflows/ci.yml)
7
7
  [![license](http://img.shields.io/badge/license-BSD-blue.svg?style=flat)](https://github.com/cclib/cclib/blob/master/LICENSE)
8
8
 
9
9
  <img src="./logo.png" alt="cclib logo" width="100" />
@@ -20,17 +20,29 @@ as well as example methods that take parsed data as input.
20
20
  from cclib._version import __version__
21
21
 
22
22
  # isort: off
23
- from cclib import parser, progress, method, bridge, io
23
+ # from cclib import parser, progress, method, bridge
24
+ from cclib import (
25
+ io,
26
+ file_handler,
27
+ collection,
28
+ combinator,
29
+ driver,
30
+ attributes,
31
+ attribute_parsers,
32
+ tree,
33
+ )
24
34
  # isort: on
25
35
 
26
36
  # The test module can be imported if it was installed with cclib.
27
- try:
28
- from cclib import test
29
- except ImportError:
30
- pass
37
+ # todo try:
38
+ # todo from cclib import test
39
+ # todo except ImportError:
40
+ # todo pass
31
41
 
32
42
  # The objects below constitute our public API. These names will not change
33
43
  # over time. Names in the sub-modules will typically also be backwards
34
44
  # compatible, but may sometimes change when code is moved around.
35
- ccopen = io.ccopen
36
- ccwrite = io.ccwrite
45
+ # ccopen = io.ccopen
46
+ # ccwrite = io.ccwrite
47
+
48
+ ccDriver = driver.ccDriver
@@ -0,0 +1 @@
1
+ __version__ = "2.0a1"
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2024, the cclib development team
2
+ #
3
+ # This file is part of cclib (http://cclib.github.io) and is distributed under
4
+ # the terms of the BSD 3-Clause License.
5
+ from cclib.attribute_parsers import utils
6
+ from cclib.attribute_parsers.aooverlaps import aooverlaps
7
+ from cclib.attribute_parsers.atombasis import atombasis
8
+ from cclib.attribute_parsers.atomcharges import atomcharges
9
+ from cclib.attribute_parsers.atomcoords import atomcoords
10
+ from cclib.attribute_parsers.atommasses import atommasses
11
+ from cclib.attribute_parsers.atomnos import atomnos
12
+ from cclib.attribute_parsers.base_parser import base_parser
13
+ from cclib.attribute_parsers.charge import charge
14
+ from cclib.attribute_parsers.coreelectrons import coreelectrons
15
+ from cclib.attribute_parsers.data import ccData
16
+ from cclib.attribute_parsers.gbasis import gbasis
17
+ from cclib.attribute_parsers.mocoeffs import mocoeffs
18
+ from cclib.attribute_parsers.moenergies import moenergies
19
+ from cclib.attribute_parsers.mosyms import mosyms
20
+ from cclib.attribute_parsers.mult import mult
21
+ from cclib.attribute_parsers.natom import natom
22
+ from cclib.attribute_parsers.nbasis import nbasis
23
+ from cclib.attribute_parsers.nmo import nmo
24
+ from cclib.attribute_parsers.scfenergies import scfenergies
25
+ from cclib.attribute_parsers.scftargets import scftargets
26
+ from cclib.attribute_parsers.scfvalues import scfvalues
@@ -0,0 +1,57 @@
1
+ # Copyright (c) 2024, the cclib development team
2
+ #
3
+ # This file is part of cclib (http://cclib.github.io) and is distributed under
4
+ # the terms of the BSD 3-Clause License.
5
+ from typing import Optional
6
+
7
+ from cclib.attribute_parsers import utils
8
+ from cclib.attribute_parsers.base_parser import base_parser
9
+
10
+ import numpy as np
11
+
12
+
13
+ class aooverlaps(base_parser):
14
+ """
15
+ Docstring? Units?
16
+ """
17
+
18
+ known_codes = ["gaussian"]
19
+
20
+ @staticmethod
21
+ def gaussian(file_handler, ccdata) -> Optional[dict]:
22
+ # Molecular orbital overlap matrix.
23
+ # Has to deal with lines such as:
24
+ # *** Overlap ***
25
+ # ****** Overlap ******
26
+ # Note that Gaussian sometimes drops basis functions,
27
+ # causing the overlap matrix as parsed below to not be
28
+ line = file_handler.last_line
29
+ if line[1:4] == "***" and (line[5:12] == "Overlap" or line[8:15] == "Overlap"):
30
+ # Ensure that this is the main calc and not a fragment
31
+ # if ccdata.counterpoise != 0:
32
+ # return
33
+ constructed_aooverlaps = np.zeros((ccdata.nbasis, ccdata.nbasis), "d")
34
+ # Overlap integrals for basis fn#1 are in aooverlaps[0]
35
+ base = 0
36
+ colmNames = file_handler.virtual_next()
37
+ while base < ccdata.nbasis:
38
+ for i in range(ccdata.nbasis - base): # Fewer lines this time
39
+ line = file_handler.virtual_next()
40
+ parts = line.split()
41
+ for j in range(len(parts) - 1): # Some lines are longer than others
42
+ k = float(parts[j + 1].replace("D", "E"))
43
+ constructed_aooverlaps[base + j, i + base] = k
44
+ constructed_aooverlaps[i + base, base + j] = k
45
+ base += 5
46
+ colmNames = file_handler.virtual_next()
47
+ return {aooverlaps.__name__: constructed_aooverlaps}
48
+
49
+ @staticmethod
50
+ def parse(file_handler, program: str, ccdata) -> Optional[dict]:
51
+ constructed_data = None
52
+ if program in aooverlaps.known_codes:
53
+ file_handler.virtual_set()
54
+ program_parser = getattr(aooverlaps, program)
55
+ constructed_data = program_parser(file_handler, ccdata)
56
+ file_handler.virtual_reset()
57
+ return constructed_data
@@ -0,0 +1,176 @@
1
+ # Copyright (c) 2024, the cclib development team
2
+ #
3
+ # This file is part of cclib (http://cclib.github.io) and is distributed under
4
+ # the terms of the BSD 3-Clause License.
5
+ import re
6
+ from typing import Optional
7
+
8
+ from cclib.attribute_parsers import utils
9
+ from cclib.attribute_parsers.base_parser import base_parser
10
+
11
+ import numpy as np
12
+
13
+
14
+ class atombasis(base_parser):
15
+ """
16
+ Docstring? Units?
17
+ """
18
+
19
+ known_codes = ["gaussian", "psi4", "qchem"]
20
+
21
+ @staticmethod
22
+ def gaussian(file_handler, ccdata) -> Optional[dict]:
23
+ # ccdata is "const" here and we don't need to modify it yet. The driver will set the attr
24
+ dependency_list = ["nmo", "nbasis"]
25
+ line = file_handler.last_line
26
+ constructed_data = None
27
+ if (
28
+ line[5:35] == "Molecular Orbital Coefficients"
29
+ or line[5:41] == "Alpha Molecular Orbital Coefficients"
30
+ or line[5:40] == "Beta Molecular Orbital Coefficients"
31
+ ):
32
+ constructed_atombasis = []
33
+ if not base_parser.check_dependencies(dependency_list, ccdata, "atombasis"):
34
+ return None
35
+ beta = False
36
+ if line[5:40] == "Beta Molecular Orbital Coefficients":
37
+ beta = True
38
+ base = 0
39
+ curr_atombasis = []
40
+ for base in range(0, ccdata.nmo, 5):
41
+ colNames = file_handler.virtual_next()
42
+ symmetries = file_handler.virtual_next()
43
+ eigenvalues = file_handler.virtual_next()
44
+ for i in range(ccdata.nbasis):
45
+ line = file_handler.virtual_next()
46
+ if i == 0:
47
+ # Find location of the start of the basis function name
48
+ start_of_basis_fn_name = line.find(line.split()[3]) - 1
49
+ if base == 0 and not beta: # Just do this the first time 'round
50
+ parts = line[:start_of_basis_fn_name].split()
51
+ if len(parts) > 1: # New atom
52
+ if i > 0:
53
+ constructed_atombasis.append(curr_atombasis)
54
+ curr_atombasis = []
55
+ curr_atombasis.append(i)
56
+ if base == 0 and not beta: # Just do this the first time 'round
57
+ constructed_atombasis.append(curr_atombasis)
58
+ constructed_data = {atombasis.__name__: constructed_atombasis}
59
+ return constructed_data
60
+
61
+ # Natural orbital coefficients (nocoeffs) and occupation numbers (nooccnos),
62
+ # which are respectively define the eigenvectors and eigenvalues of the
63
+ # diagonalized one-electron density matrix. These orbitals are formed after
64
+ # configuration interaction (CI) calculations, but not only. Similarly to mocoeffs,
65
+ # we can parse and check aonames and atombasis here.
66
+ #
67
+ # Natural Orbital Coefficients:
68
+ # 1 2 3 4 5
69
+ # Eigenvalues -- 2.01580 2.00363 2.00000 2.00000 1.00000
70
+ # 1 1 O 1S 0.00000 -0.15731 -0.28062 0.97330 0.00000
71
+ # 2 2S 0.00000 0.75440 0.57746 0.07245 0.00000
72
+ # ...
73
+ #
74
+ def natural_orbital_single_spin_parsing(fh):
75
+ coeffs = np.zeros((ccdata.nmo, ccdata.nbasis), "d")
76
+ this_atombasis = []
77
+ for base in range(0, ccdata.nmo, 5):
78
+ colmNames = fh.virtual_next()
79
+ eigenvalues = fh.virtual_next()
80
+ for i in range(ccdata.nbasis):
81
+ line = fh.virtual_next()
82
+ # Just do this the first time 'round.
83
+ if base == 0:
84
+ parts = line[:12].split()
85
+ # New atom.
86
+ if len(parts) > 1:
87
+ if i > 0:
88
+ this_atombasis.append(basisonatom)
89
+ basisonatom = []
90
+ orbital = line[11:20].strip()
91
+ basisonatom.append(i)
92
+ part = line[21:].replace("D", "E").rstrip()
93
+ temp = []
94
+ for j in range(0, len(part), 10):
95
+ temp.append(float(part[j : j + 10]))
96
+ # Do the last update of atombasis.
97
+ if base == 0:
98
+ this_atombasis.append(basisonatom)
99
+ return this_atombasis
100
+
101
+ if line[5:33] == "Natural Orbital Coefficients":
102
+ parsed_atombasis = natural_orbital_single_spin_parsing(file_handler)
103
+ return {atombasis.__name__: parsed_atombasis}
104
+
105
+ if line[5:39] == "Alpha Natural Orbital Coefficients":
106
+ parsed_atombasis = natural_orbital_single_spin_parsing(file_handler)
107
+ print(atombasis.__name__)
108
+ return {atombasis.__name__: parsed_atombasis}
109
+ if line[5:38] == "Beta Natural Orbital Coefficients":
110
+ parsed_atombasis = natural_orbital_single_spin_parsing(file_handler)
111
+ return {atombasis.__name__: parsed_atombasis}
112
+
113
+ return None
114
+
115
+ @staticmethod
116
+ def psi4(file_handler, ccdata) -> Optional[dict]:
117
+ dependency_list = ["nmo", "nbasis"]
118
+ if getattr(ccdata, "atombasis") == None:
119
+ line = file_handler.last_line
120
+ if line.strip() == "-Contraction Scheme:":
121
+ file_handler.skip_lines(["headers", "d"], virtual=True)
122
+ line = file_handler.virtual_next()
123
+ constructed_atombasis = []
124
+ atombasis_pos = 0
125
+ while line.strip():
126
+ ao_count = 0
127
+ shells = line.split("//")[1].split()
128
+ for s in shells:
129
+ count, basistype = s
130
+ multiplier = 3 * (basistype == "p") or 1
131
+ ao_count += multiplier * int(count)
132
+ if len(constructed_atombasis) > 0:
133
+ atombasis_pos = constructed_atombasis[-1][-1] + 1
134
+ constructed_atombasis.append(
135
+ list(range(atombasis_pos, atombasis_pos + ao_count))
136
+ )
137
+ line = file_handler.virtual_next()
138
+ constructed_data = {atombasis.__name__: constructed_atombasis}
139
+ return constructed_data
140
+ return None
141
+
142
+ @staticmethod
143
+ def qchem(file_handler, ccdata) -> Optional[dict]:
144
+ # This block comes from `print_orbitals = true/{int}`. Less
145
+ # precision than `scf_final_print >= 2` for `mocoeffs`, but
146
+ # important for `aonames` and `atombasis`.
147
+ alpha_mo_coefficient_headers = []
148
+ qchem_re_atomindex = re.compile(r"(\d+)_")
149
+
150
+ dependency_list = ["aonames", "atomnos", "nbasis", "natom"]
151
+ line = file_handler.last_line
152
+ if any(header in line for header in alpha_mo_coefficient_headers):
153
+ if base_parser.check_dependencies(dependency_list, ccdata, "atombasis"):
154
+ constructed_atombasis = []
155
+ for n in range(ccdata.natom):
156
+ constructed_atombasis.append([])
157
+ # Go back through `aonames` to create `atombasis`.
158
+ assert len(ccdata.aonames) == ccdata.nbasis
159
+ for aoindex, aoname in enumerate(ccdata.aonames):
160
+ atomindex = int(qchem_re_atomindex.search(aoname).groups()[0]) - 1
161
+ constructed_atombasis.atombasis[atomindex].append(aoindex)
162
+ assert len(self.atombasis) == len(self.atomnos)
163
+ if not hasattr(ccdata, "atombasis"):
164
+ constructed_data = {atombasis.__name__: constructed_atombasis}
165
+ return constructed_data
166
+ return None
167
+
168
+ @staticmethod
169
+ def parse(file_handler, program: str, ccdata) -> Optional[dict]:
170
+ constructed_data = None
171
+ if program in atombasis.known_codes:
172
+ file_handler.virtual_set()
173
+ program_parser = getattr(atombasis, program)
174
+ constructed_data = program_parser(file_handler, ccdata)
175
+ file_handler.virtual_reset()
176
+ return constructed_data
@@ -0,0 +1,270 @@
1
+ # Copyright (c) 2024, the cclib development team
2
+ #
3
+ # This file is part of cclib (http://cclib.github.io) and is distributed under
4
+ # the terms of the BSD 3-Clause License.
5
+ from typing import Optional
6
+
7
+ from cclib.attribute_parsers import utils
8
+ from cclib.attribute_parsers.base_parser import base_parser
9
+
10
+ import numpy as np
11
+
12
+
13
+ def orca_parse_charge_section(file_handler, chargestype):
14
+ """Parse a charge section
15
+
16
+ Parameters
17
+ ----------
18
+ file_handler :
19
+ generates lines
20
+ chargestype : str
21
+ what type of charge we're dealing with, must be one of
22
+ 'mulliken', 'lowdin', 'chelpg' or 'hirshfeld'
23
+ """
24
+ atomcharges = dict()
25
+ atomspins = dict()
26
+ line = file_handler.last_line
27
+ has_spins = "AND SPIN POPULATIONS" in line
28
+
29
+ file_handler.skip_lines(["dashes"], virtual=True)
30
+
31
+ # depending on chargestype, decide when to stop parsing lines
32
+ # start, stop - indices for slicing lines and grabbing values
33
+ # should_stop: when to stop parsing
34
+ if chargestype == "mulliken":
35
+ should_stop = lambda x: x.startswith("Sum of atomic charges")
36
+ start, stop = 8, 20
37
+ elif chargestype == "lowdin":
38
+ should_stop = lambda x: not bool(x.strip())
39
+ start, stop = 8, 20
40
+ elif chargestype == "chelpg":
41
+ should_stop = lambda x: x.startswith("---")
42
+ start, stop = 11, 26
43
+ elif chargestype == "hirshfeld":
44
+ should_stop = lambda x: not bool(x.strip())
45
+ start, stop = 9, 18
46
+ file_handler.skip_lines(
47
+ ["d", "b", "Total integrated alpha density", "Total integrated beta density", "header"],
48
+ virtual=True,
49
+ )
50
+ else:
51
+ raise RuntimeError(f"unknown chargestype: {chargestype}")
52
+
53
+ charges = []
54
+ spins = []
55
+
56
+ line = file_handler.virtual_next()
57
+ while not should_stop(line):
58
+ # Don't add point charges or embedding potentials.
59
+ if "Q :" not in line:
60
+ charges.append(float(line[start:stop]))
61
+ if has_spins:
62
+ spins.append(float(line[stop:]))
63
+ line = file_handler.virtual_next()
64
+
65
+ atomcharges[chargestype] = charges
66
+ if has_spins:
67
+ atomspins[chargestype] = spins
68
+ return atomcharges, atomspins
69
+
70
+
71
+ def qchem_parse_charge_section(file_handler, chargetype):
72
+ """Parse the population analysis charge block."""
73
+ atomcharges = dict()
74
+ atomspins = dict()
75
+ charges = []
76
+ spins = []
77
+ line = file_handler.last_line
78
+ file_handler.skip_lines(["blank"], virtual=True)
79
+ line = file_handler.virtual_next()
80
+ has_spins = False
81
+ if "Spin" in line:
82
+ has_spins = True
83
+ file_handler.skip_lines(["dashes"], virtual=True)
84
+ line = file_handler.virtual_next()
85
+
86
+ while list(set(line.strip())) != ["-"]:
87
+ elements = line.split()
88
+ charge = utils.float(elements[2])
89
+ charges.append(charge)
90
+ if has_spins:
91
+ spin = utils.float(elements[3])
92
+ spins.append(spin)
93
+ line = file_handler.virtual_next()
94
+
95
+ atomcharges[chargetype] = charges
96
+ if has_spins:
97
+ atomspins[chargestype] = spins
98
+ return atomcharges, atomspins
99
+
100
+
101
+ class atomcharges(base_parser):
102
+ """
103
+ Docstring? Units?
104
+ """
105
+
106
+ known_codes = ["ORCA", "NBO", "qchem"]
107
+
108
+ @staticmethod
109
+ def ORCA(file_handler, ccdata) -> Optional[dict]:
110
+ # ccdata is "const" here and we don't need to modify it yet. The driver will set the attr
111
+ line = file_handler.last_line
112
+ constructed_charge_data = None
113
+ constructed_spin_data = None
114
+
115
+ # ORCA will print atomic charges along with the spin populations,
116
+ # so care must be taken about choosing the proper column.
117
+ # Population analyses are performed usually only at the end
118
+ # of a geometry optimization or other run, so we want to
119
+ # leave just the final atom charges.
120
+ # Here is an example for Mulliken charges:
121
+ # --------------------------------------------
122
+ # MULLIKEN ATOMIC CHARGES AND SPIN POPULATIONS
123
+ # --------------------------------------------
124
+ # 0 H : 0.126447 0.002622
125
+ # 1 C : -0.613018 -0.029484
126
+ # 2 H : 0.189146 0.015452
127
+ # 3 H : 0.320041 0.037434
128
+ # ...
129
+ # Sum of atomic charges : -0.0000000
130
+ # Sum of atomic spin populations: 1.0000000
131
+ if line[:23] == "MULLIKEN ATOMIC CHARGES":
132
+ constructed_charge_data, constructed_spin_data = orca_parse_charge_section(
133
+ file_handler, "mulliken"
134
+ )
135
+ # Things are the same for Lowdin populations, except that the sums
136
+ # are not printed (there is a blank line at the end).
137
+ if line[:22] == "LOEWDIN ATOMIC CHARGES":
138
+ constructed_charge_data, constructed_spin_data = orca_parse_charge_section(
139
+ file_handler, "lowdin"
140
+ )
141
+ # ------------------
142
+ # HIRSHFELD ANALYSIS
143
+ # ------------------
144
+ #
145
+ # Total integrated alpha density = 142.999988722
146
+ # Total integrated beta density = 142.999988722
147
+ #
148
+ # ATOM CHARGE SPIN
149
+ # 0 H 0.157924 0.000000
150
+ # 1 O -0.209542 0.000000
151
+ # 2 C 0.030659 0.000000
152
+ # ...
153
+ # TOTAL -0.999977 0.000000
154
+ if line[:18] == "HIRSHFELD ANALYSIS":
155
+ constructed_charge_data, constructed_spin_data = orca_parse_charge_section(
156
+ file_handler, "hirshfeld"
157
+ )
158
+ # CHELPG Charges
159
+ # --------------------------------
160
+ # 0 C : 0.363939
161
+ # 1 H : 0.025695
162
+ # ...
163
+ # --------------------------------
164
+ # Total charge: -0.000000
165
+ # --------------------------------
166
+ if line.startswith("CHELPG Charges"):
167
+ constructed_charge_data, constructed_spin_data = orca_parse_charge_section(
168
+ file_handler, "chelpg"
169
+ )
170
+ # TODO handle atomspins
171
+ constructed_data = dict()
172
+ if constructed_charge_data:
173
+ if ccdata.atomcharges:
174
+ constructed_data["atomcharges"] = {**ccdata.atomcharges, **constructed_charge_data}
175
+ else:
176
+ constructed_data["atomcharges"] = {**constructed_charge_data}
177
+ if constructed_spin_data:
178
+ if ccdata.atomspins:
179
+ constructed_data["atomspins"] = {**ccdata.atomspins, **constructed_spin_data}
180
+ else:
181
+ constructed_data["atomspins"] = {**constructed_spin_data}
182
+ if constructed_data:
183
+ return constructed_data
184
+ return None
185
+
186
+ @staticmethod
187
+ def NBO(file_handler, ccdata) -> Optional[dict]:
188
+ atomcharges = dict()
189
+ # ccdata is "const" here and we don't need to modify it yet. The driver will set the attr
190
+ charges = None
191
+ line = file_handler.last_line
192
+ if " Atom No Charge" in line:
193
+ parsed_charges = []
194
+ line = file_handler.virtual_next()
195
+ line = file_handler.virtual_next()
196
+ while "==============" not in line:
197
+ population_analysis = line.split()
198
+ atom = population_analysis[0]
199
+ no = int(population_analysis[1])
200
+ natural_charge = float(population_analysis[2])
201
+ core = float(population_analysis[3])
202
+ valence = float(population_analysis[4])
203
+ rydberg = float(population_analysis[5])
204
+ total = float(population_analysis[6])
205
+ parsed_charges.append(natural_charge)
206
+ line = file_handler.virtual_next()
207
+ atomcharges["nbo"] = parsed_charges
208
+ constructed_data = dict()
209
+ if atomcharges != dict():
210
+ if ccdata.atomcharges:
211
+ constructed_data["atomcharges"] = {**ccdata.atomcharges, **atomcharges}
212
+ else:
213
+ constructed_data["atomcharges"] = {**atomcharges}
214
+ return constructed_data
215
+ return None
216
+
217
+ @staticmethod
218
+ def qchem(file_handler, ccdata) -> Optional[dict]:
219
+ line = file_handler.last_line
220
+ constructed_charge_data = None
221
+ constructed_spin_data = None
222
+ constructed_data = dict()
223
+ if "Ground-State Mulliken Net Atomic Charges" in line:
224
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
225
+ inputfile, "mulliken"
226
+ )
227
+ if "Hirshfeld Atomic Charges" in line:
228
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
229
+ inputfile, "hirshfeld"
230
+ )
231
+ if "Charge Model 5" in line:
232
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
233
+ inputfile, "cm5"
234
+ )
235
+ if "Ground-State ChElPG Net Atomic Charges" in line:
236
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
237
+ inputfile, "chelpg"
238
+ )
239
+ if "Merz-Kollman ESP Net Atomic Charges" in line:
240
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
241
+ inputfile, "esp"
242
+ )
243
+ if "Merz-Kollman RESP Net Atomic Charges" in line:
244
+ constructed_charge_data, constructed_spin_data = qchem_parse_charge_section(
245
+ inputfile, "resp"
246
+ )
247
+ constructed_data = dict()
248
+ if constructed_charge_data:
249
+ if ccdata.atomcharges:
250
+ constructed_data["atomcharges"] = {**ccdata.atomcharges, **constructed_charge_data}
251
+ else:
252
+ constructed_data["atomcharges"] = {**constructed_charge_data}
253
+ if constructed_spin_data:
254
+ if ccdata.atomspins:
255
+ constructed_data["atomspins"] = {**ccdata.atomspins, **constructed_spin_data}
256
+ else:
257
+ constructed_data["atomspins"] = {**constructed_spin_data}
258
+ if constructed_data:
259
+ return constructed_data
260
+ return None
261
+
262
+ @staticmethod
263
+ def parse(file_handler, program: str, ccdata) -> Optional[dict]:
264
+ constructed_data = None
265
+ if program in atomcharges.known_codes:
266
+ file_handler.virtual_set()
267
+ program_parser = getattr(atomcharges, program)
268
+ constructed_data = program_parser(file_handler, ccdata)
269
+ file_handler.virtual_reset()
270
+ return constructed_data