From ef9c592ea67cdb471563763e466e97b7f43373a7 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 14 May 2024 11:17:47 +0200 Subject: [PATCH 1/4] Add Ruff format to pre-commit --- .pre-commit-config.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 12e2786e..17e86c2b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ exclude: 'iodata/test/data/.*' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: check-added-large-files - id: check-ast @@ -26,6 +26,10 @@ repos: hooks: - id: remove-crlf - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.1 + rev: 0.28.3 hooks: - id: check-github-workflows +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.4.4 + hooks: + - id: ruff-format From de3d55cff718da61652b1df8b4e7410c03d744c1 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 14 May 2024 11:30:23 +0200 Subject: [PATCH 2/4] Add Ruff format config to pyproject.toml --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 45e12f81..fc87fa8c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,3 +48,7 @@ packages = ["iodata"] write_to = "iodata/_version.py" version_scheme = "post-release" local_scheme = "no-local-version" + +[tool.ruff] +line-length = 100 +target-version = "py311" From 11c6203b0be3507b0c0ac5add62887cb018ee66e Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 14 May 2024 11:22:29 +0200 Subject: [PATCH 3/4] Disable auto-formatting of selected code blocks --- iodata/formats/mwfn.py | 4 +++- iodata/formats/wfn.py | 3 ++- iodata/overlap_cartpure.py | 2 ++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/iodata/formats/mwfn.py b/iodata/formats/mwfn.py index e72be2fb..d3f3b987 100644 --- a/iodata/formats/mwfn.py +++ b/iodata/formats/mwfn.py @@ -49,6 +49,8 @@ # F shell: F 0, F+1, F-1, F+2, F-2, F+3, F-3 # G shell: G 0, G+1, G-1, G+2, G-2, G+3, G-3, G+4, G-4 + +# fmt: off CONVENTIONS = { (4, 'p'): HORTON2_CONVENTIONS[(4, 'p')], (3, 'p'): HORTON2_CONVENTIONS[(3, 'p')], @@ -63,7 +65,7 @@ 'xyyzz', 'xyyyz', 'xyyyy', 'xxzzz', 'xxyzz', 'xxyyz', 'xxyyy', 'xxxzz', 'xxxyz', 'xxxyy', 'xxxxz', 'xxxxy', 'xxxxx'], } - +# fmt: on def _load_helper_opener(lit: LineIterator) -> dict: """Read initial variables at the beginning of a MWFN file.""" diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 72dc672a..88e5146e 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -104,6 +104,7 @@ # 56 HXXXXX (500) +# fmt: off CONVENTIONS = { (0, 'c'): ['1'], (1, 'c'): ['x', 'y', 'z'], @@ -115,7 +116,7 @@ 'xyzzz', 'xyyzz', 'xyyyz', 'xyyyy', 'xxzzz', 'xxyzz', 'xxyyz', 'xxyyy', 'xxxzz', 'xxxyz', 'xxxyy', 'xxxxz', 'xxxxy', 'xxxxx'], } - +# fmt: on # Definition of primitives in the WFN format. This is the order of the primitive # types as documented by aimall, used in the field TYPE ASSIGNMENTS. diff --git a/iodata/overlap_cartpure.py b/iodata/overlap_cartpure.py index e1317061..fbbc6e24 100644 --- a/iodata/overlap_cartpure.py +++ b/iodata/overlap_cartpure.py @@ -30,6 +30,7 @@ __all__ = ["tfs"] +# fmt: off tf0 = np.array([ [1.0], ]) @@ -203,5 +204,6 @@ 0, 1.96875, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.64725984928774935, 0, 0, 0, 0, 0, 0, 0], ]) +# fmt: on tfs = [tf0, tf1, tf2, tf3, tf4, tf5, tf6, tf7] From d73423863285c6cd53dc4b779d008c3cd457a881 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 14 May 2024 11:44:30 +0200 Subject: [PATCH 4/4] Apply Ruff format --- doc/conf.py | 122 ++-- doc/gen_formats.py | 8 +- doc/gen_formats_tab.py | 13 +- doc/gen_inputs.py | 7 +- iodata/__init__.py | 1 - iodata/__main__.py | 60 +- iodata/api.py | 36 +- iodata/attrutils.py | 8 +- iodata/basis.py | 82 ++- iodata/docstrings.py | 122 +++- iodata/formats/charmm.py | 29 +- iodata/formats/chgcar.py | 24 +- iodata/formats/cp2klog.py | 179 +++-- iodata/formats/cube.py | 63 +- iodata/formats/extxyz.py | 116 +-- iodata/formats/fchk.py | 430 ++++++----- iodata/formats/fcidump.py | 65 +- iodata/formats/gamess.py | 11 +- iodata/formats/gaussianinput.py | 18 +- iodata/formats/gaussianlog.py | 33 +- iodata/formats/gromacs.py | 38 +- iodata/formats/json.py | 37 +- iodata/formats/locpot.py | 7 +- iodata/formats/mol2.py | 59 +- iodata/formats/molden.py | 422 ++++++----- iodata/formats/molekel.py | 216 +++--- iodata/formats/mwfn.py | 122 ++-- iodata/formats/orcalog.py | 27 +- iodata/formats/pdb.py | 57 +- iodata/formats/poscar.py | 31 +- iodata/formats/qchemlog.py | 321 +++++---- iodata/formats/sdf.py | 43 +- iodata/formats/wfn.py | 205 +++--- iodata/formats/wfx.py | 256 ++++--- iodata/formats/xyz.py | 52 +- iodata/inputs/common.py | 6 +- iodata/inputs/gaussian.py | 21 +- iodata/inputs/orca.py | 12 +- iodata/iodata.py | 66 +- iodata/orbitals.py | 73 +- iodata/overlap.py | 54 +- iodata/periodic.py | 3 +- iodata/test/common.py | 30 +- iodata/test/test_attrutils.py | 9 +- iodata/test/test_basis.py | 257 ++++--- iodata/test/test_charmm.py | 20 +- iodata/test/test_chgcar.py | 18 +- iodata/test/test_cli.py | 38 +- iodata/test/test_cp2klog.py | 44 +- iodata/test/test_cube.py | 56 +- iodata/test/test_extxyz.py | 39 +- iodata/test/test_fchk.py | 531 +++++++------- iodata/test/test_fcidump.py | 78 +- iodata/test/test_gamess.py | 14 +- iodata/test/test_gaussianinput.py | 25 +- iodata/test/test_gaussianlog.py | 20 +- iodata/test/test_gromacs.py | 18 +- iodata/test/test_inputs.py | 54 +- iodata/test/test_iodata.py | 45 +- iodata/test/test_json.py | 14 +- iodata/test/test_locpot.py | 12 +- iodata/test/test_mol2.py | 31 +- iodata/test/test_molden.py | 195 +++-- iodata/test/test_molekel.py | 41 +- iodata/test/test_mwfn.py | 240 +++++-- iodata/test/test_orbitals.py | 1 - iodata/test/test_orcalog.py | 19 +- iodata/test/test_overlap.py | 27 +- iodata/test/test_pdb.py | 110 +-- iodata/test/test_poscar.py | 38 +- iodata/test/test_qchemlog.py | 1099 ++++++++++++++++++++++------- iodata/test/test_sdf.py | 16 +- iodata/test/test_wfn.py | 210 +++--- iodata/test/test_wfx.py | 802 ++++++++++++++------- iodata/test/test_xyz.py | 42 +- iodata/utils.py | 77 +- tools/harmonics.py | 36 +- 77 files changed, 4723 insertions(+), 3138 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 0c7f2e38..40d043b6 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -19,7 +19,6 @@ # pylint: disable=unused-argument,redefined-builtin """Sphinxdoc configuration file.""" - import os import subprocess @@ -30,10 +29,10 @@ # An in-place build can also work, provided the necessary environment variables # are set accordingly. -on_rtd = os.environ.get('READTHEDOCS', None) == 'True' +on_rtd = os.environ.get("READTHEDOCS", None) == "True" if on_rtd: - subprocess.run(['python', '-m', 'pip', 'install', '..'], check=True) - subprocess.run(['./gen_docs.sh'], shell=True, check=True) + subprocess.run(["python", "-m", "pip", "install", ".."], check=True) + subprocess.run(["./gen_docs.sh"], shell=True, check=True) # Custom sidebar templates, must be a dictionary that maps document names # to template names. @@ -41,12 +40,12 @@ # This is required for the alabaster theme # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars html_sidebars = { - '**': [ - 'about.html', - 'navigation.html', - 'relations.html', # needs 'show_related': True theme option to display - 'searchbox.html', - 'donate.html', + "**": [ + "about.html", + "navigation.html", + "relations.html", # needs 'show_related': True theme option to display + "searchbox.html", + "donate.html", ] } @@ -62,42 +61,42 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', - 'sphinx.ext.coverage', - 'sphinx.ext.mathjax', - 'sphinx.ext.viewcode', - 'sphinx.ext.napoleon', - 'sphinx_autodoc_typehints', + "sphinx.ext.autodoc", + "sphinx.ext.doctest", + "sphinx.ext.intersphinx", + "sphinx.ext.coverage", + "sphinx.ext.mathjax", + "sphinx.ext.viewcode", + "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = 'IOData' -copyright = '2019, The IODATA Development Team' -author = 'The IODATA Development Team' +project = "IOData" +copyright = "2019, The IODATA Development Team" +author = "The IODATA Development Team" # The version info for the project yo're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '' +version = "" # The full version, including alpha/beta/rc tags. -release = '' +release = "" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -109,10 +108,10 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -122,7 +121,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' +html_theme = "sphinx_rtd_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -133,14 +132,14 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] -html_style = 'css/override.css' +html_static_path = ["_static"] +html_style = "css/override.css" # -- Options for HTMLHelp output ------------------------------------------ # Output file base name for HTML help builder. -htmlhelp_basename = 'IODatadoc' +htmlhelp_basename = "IODatadoc" # -- Options for LaTeX output --------------------------------------------- @@ -148,15 +147,12 @@ # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # # 'preamble': '', - # Latex figure (float) alignment # # 'figure_align': 'htbp', @@ -166,18 +162,14 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'IOData.tex', 'IOData Documentation', - 'The HORTON Developers', 'manual'), + (master_doc, "IOData.tex", "IOData Documentation", "The HORTON Developers", "manual"), ] # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). -man_pages = [ - (master_doc, 'iodata', 'IOData Documentation', - [author], 1) -] +man_pages = [(master_doc, "iodata", "IOData Documentation", [author], 1)] # -- Options for Texinfo output ------------------------------------------- @@ -185,9 +177,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'IOData', 'IOData Documentation', - author, 'IOData', 'One line description of project.', - 'Miscellaneous'), + ( + master_doc, + "IOData", + "IOData Documentation", + author, + "IOData", + "One line description of project.", + "Miscellaneous", + ), ] # -- Options for Epub output ---------------------------------------------- @@ -208,20 +206,20 @@ # epub_uid = '' # A list of files that should not be packed into the epub file. -epub_exclude_files = ['search.html'] +epub_exclude_files = ["search.html"] # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {'https://docs.python.org/': None} +intersphinx_mapping = {"https://docs.python.org/": None} # -- Configuration for autodoc extensions --------------------------------- autodoc_default_options = { - 'undoc-members': True, - 'show-inheritance': True, - 'members': None, - 'inherited-members': True, - 'ignore-module-all': True, + "undoc-members": True, + "show-inheritance": True, + "members": None, + "inherited-members": True, + "ignore-module-all": True, } @@ -243,22 +241,24 @@ def setup(app): # -- Configuration of mathjax extension ----------------------------------- mathjax_config = { - 'extensions': ['fast-preview.js'], + "extensions": ["fast-preview.js"], # TeX: { # Macros: { # RR: '{\\bf R}', # bold: ['{\\bf #1}', 1] # } # } - 'TeX': { - 'Macros': { - 'ket': ["{\\left\\vert { #1 } \\right\\rangle}", 1], - 'bra': ["{\\left\\langle { #1} \\right\\vert}", 1], - 'braket': ["{\\left\\langle {#1} \\mid { #2} \\right\\rangle}", 2], - 'ketbra': ["{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}", - 2], - 'ev': ["{\\left\\langle {#2} \\vert {#1} \\vert {#2} \\right\\rangle}", 2], - 'mel': ["{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}", 3] + "TeX": { + "Macros": { + "ket": ["{\\left\\vert { #1 } \\right\\rangle}", 1], + "bra": ["{\\left\\langle { #1} \\right\\vert}", 1], + "braket": ["{\\left\\langle {#1} \\mid { #2} \\right\\rangle}", 2], + "ketbra": [ + "{\\left\\vert { #1 } \\right\\rangle\\left\\langle { #2} \\right\\vert}", + 2, + ], + "ev": ["{\\left\\langle {#2} \\vert {#1} \\vert {#2} \\right\\rangle}", 2], + "mel": ["{\\left\\langle{ #1 }\\right\\vert{ #2 }\\left\\vert{#3}\\right\\rangle}", 3], } }, } diff --git a/doc/gen_formats.py b/doc/gen_formats.py index 530e7362..3addbd98 100755 --- a/doc/gen_formats.py +++ b/doc/gen_formats.py @@ -40,7 +40,7 @@ def _format_words(words): - return ', '.join('``{}``'.format(word) for word in words) + return ", ".join("``{}``".format(word) for word in words) def _print_section(title, linechar): @@ -61,11 +61,11 @@ def main(): break if skip: continue - lines = module.__doc__.split('\n') + lines = module.__doc__.split("\n") # add labels for cross-referencing format (e.g. in formats table) print(f".. _format_{modname}:") print() - _print_section('{} (``{}``)'.format(lines[0][:-1], modname), "=") + _print_section("{} (``{}``)".format(lines[0][:-1], modname), "=") print() for line in lines[2:]: print(line) @@ -95,5 +95,5 @@ def main(): print() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/doc/gen_formats_tab.py b/doc/gen_formats_tab.py index e33d03af..5147f44a 100755 --- a/doc/gen_formats_tab.py +++ b/doc/gen_formats_tab.py @@ -20,7 +20,6 @@ # pylint: disable=unused-argument,redefined-builtin """Generate formats.rst.""" - from collections import defaultdict import inspect @@ -86,13 +85,12 @@ def generate_table_rst(): table with rows of each property and columns of each format """ - fmt_names, has_load, guaranteed, ifpresent, has_dump, required, optional = \ + fmt_names, has_load, guaranteed, ifpresent, has_dump, required, optional = ( _generate_all_format_parser() + ) # Sort rows by number of times the attribute is used in decreasing order. - rows = sorted( - attr_name for attr_name in dir(iodata.IOData) - if not attr_name.startswith('_')) + rows = sorted(attr_name for attr_name in dir(iodata.IOData) if not attr_name.startswith("_")) # Order columns based on number of guaranteed and ifpresent entries for each format. # Also keep track of which format has a load_one and dump_one function. @@ -106,10 +104,9 @@ def generate_table_rst(): # Construct header with cross-referencing columns. header = ["Attribute"] for fmt_name in cols: - col_name = f':ref:`{fmt_name} `' + col_name = f":ref:`{fmt_name} `" col_name += ": {}{}".format( - "L" if fmt_name in has_load else "", - "D" if fmt_name in has_dump else "" + "L" if fmt_name in has_load else "", "D" if fmt_name in has_dump else "" ) header.append(col_name) table = [header] diff --git a/doc/gen_inputs.py b/doc/gen_inputs.py index e8a3ccbe..0459e5e4 100755 --- a/doc/gen_inputs.py +++ b/doc/gen_inputs.py @@ -20,7 +20,6 @@ # pylint: disable=unused-argument,redefined-builtin """Generate formats.rst.""" - from gen_formats import _format_words, _print_section from iodata.api import INPUT_MODULES @@ -54,11 +53,11 @@ def main(): for modname, module in sorted(INPUT_MODULES.items()): if not hasattr(module, "write_input"): continue - lines = module.__doc__.split('\n') + lines = module.__doc__.split("\n") # add labels for cross-referencing format (e.g. in formats table) print(f".. _input_{modname}:") print() - _print_section('{} (``{}``)'.format(lines[0][:-1], modname), "=") + _print_section("{} (``{}``)".format(lines[0][:-1], modname), "=") print() for line in lines[2:]: print(line) @@ -82,5 +81,5 @@ def main(): print() -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/iodata/__init__.py b/iodata/__init__.py index ef8713f2..98658e0d 100644 --- a/iodata/__init__.py +++ b/iodata/__init__.py @@ -18,7 +18,6 @@ # -- """Input and Output Module.""" - try: from ._version import __version__ except ImportError: diff --git a/iodata/__main__.py b/iodata/__main__.py index 96dcf077..fbda54f7 100755 --- a/iodata/__main__.py +++ b/iodata/__main__.py @@ -19,7 +19,6 @@ # -- """CLI for file conversion.""" - import argparse import numpy as np @@ -28,7 +27,7 @@ try: from iodata.version import __version__ except ImportError: - __version__ = '0.0.0.post0' + __version__ = "0.0.0.post0" __all__ = [] @@ -49,36 +48,49 @@ dump_many {dump_many} """.format( - load_one=' '.join(name for name, module in sorted(FORMAT_MODULES.items()) - if hasattr(module, 'load_one')), - dump_one=' '.join(name for name, module in sorted(FORMAT_MODULES.items()) - if hasattr(module, 'dump_one')), - load_many=' '.join(name for name, module in sorted(FORMAT_MODULES.items()) - if hasattr(module, 'load_many')), - dump_many=' '.join(name for name, module in sorted(FORMAT_MODULES.items()) - if hasattr(module, 'dump_many')), + load_one=" ".join( + name for name, module in sorted(FORMAT_MODULES.items()) if hasattr(module, "load_one") + ), + dump_one=" ".join( + name for name, module in sorted(FORMAT_MODULES.items()) if hasattr(module, "dump_one") + ), + load_many=" ".join( + name for name, module in sorted(FORMAT_MODULES.items()) if hasattr(module, "load_many") + ), + dump_many=" ".join( + name for name, module in sorted(FORMAT_MODULES.items()) if hasattr(module, "dump_many") + ), ) def parse_args(): """Use argparse to to parse command-line arguments.""" parser = argparse.ArgumentParser( - prog='iodata-convert', formatter_class=argparse.RawTextHelpFormatter, - description=DESCRIPTION) + prog="iodata-convert", + formatter_class=argparse.RawTextHelpFormatter, + description=DESCRIPTION, + ) parser.add_argument( - '-V', '--version', action='version', - version="%(prog)s (IOData version {})".format(__version__)) + "-V", + "--version", + action="version", + version="%(prog)s (IOData version {})".format(__version__), + ) parser.add_argument( - '-i', '--infmt', - help='Select the input format, overrides automatic detection.') + "-i", "--infmt", help="Select the input format, overrides automatic detection." + ) parser.add_argument( - '-o', '--outfmt', - help='Select the output format, overrides automatic detection.') + "-o", "--outfmt", help="Select the output format, overrides automatic detection." + ) parser.add_argument( - '-m', '--many', default=False, action='store_true', - help='Convert many frames, e.g. for trajectories.') - parser.add_argument('input', help='The input file.') - parser.add_argument('output', help='The output file.') + "-m", + "--many", + default=False, + action="store_true", + help="Convert many frames, e.g. for trajectories.", + ) + parser.add_argument("input", help="The input file.") + parser.add_argument("output", help="The output file.") return parser.parse_args() @@ -108,11 +120,11 @@ def convert(infn, outfn, many, infmt, outfmt): def main(): """Convert files between two formats using command-line arguments.""" # All, except underflows, is *not* fine. - np.seterr(divide='raise', over='raise', invalid='raise') + np.seterr(divide="raise", over="raise", invalid="raise") args = parse_args() convert(args.input, args.output, args.many, args.infmt, args.outfmt) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/iodata/api.py b/iodata/api.py index d34b794c..71d614ba 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -18,7 +18,6 @@ # -- """Functions to be used by end users.""" - import os from typing import Iterator from types import ModuleType @@ -30,16 +29,16 @@ from .utils import LineIterator -__all__ = ['load_one', 'load_many', 'dump_one', 'dump_many', 'write_input'] +__all__ = ["load_one", "load_many", "dump_one", "dump_many", "write_input"] def _find_format_modules(): """Return all file-format modules found with importlib.""" result = {} - for module_info in iter_modules(import_module('iodata.formats').__path__): + for module_info in iter_modules(import_module("iodata.formats").__path__): if not module_info.ispkg: - format_module = import_module('iodata.formats.' + module_info.name) - if hasattr(format_module, 'PATTERNS'): + format_module = import_module("iodata.formats." + module_info.name) + if hasattr(format_module, "PATTERNS"): result[module_info.name] = format_module return result @@ -74,16 +73,17 @@ def _select_format_module(filename: str, attrname: str, fmt: str = None) -> Modu return format_module else: return FORMAT_MODULES[fmt] - raise ValueError('Could not find file format with feature {} for file {}'.format( - attrname, filename)) + raise ValueError( + "Could not find file format with feature {} for file {}".format(attrname, filename) + ) def _find_input_modules(): """Return all input modules found with importlib.""" result = {} - for module_info in iter_modules(import_module('iodata.inputs').__path__): + for module_info in iter_modules(import_module("iodata.inputs").__path__): if not module_info.ispkg: - input_module = import_module('iodata.inputs.' + module_info.name) + input_module = import_module("iodata.inputs." + module_info.name) if hasattr(input_module, "write_input"): result[module_info.name] = input_module return result @@ -107,8 +107,8 @@ def _select_input_module(fmt: str) -> ModuleType: """ if fmt in INPUT_MODULES: - if not hasattr(INPUT_MODULES[fmt], 'write_input'): - raise ValueError(f'{fmt} input module does not have write_input!') + if not hasattr(INPUT_MODULES[fmt], "write_input"): + raise ValueError(f"{fmt} input module does not have write_input!") return INPUT_MODULES[fmt] raise ValueError(f"Could not find input format {fmt}!") @@ -136,7 +136,7 @@ def load_one(filename: str, fmt: str = None, **kwargs) -> IOData: The instance of IOData with data loaded from the input files. """ - format_module = _select_format_module(filename, 'load_one', fmt) + format_module = _select_format_module(filename, "load_one", fmt) lit = LineIterator(filename) try: iodata = IOData(**format_module.load_one(lit, **kwargs)) @@ -168,7 +168,7 @@ def load_many(filename: str, fmt: str = None, **kwargs) -> Iterator[IOData]: An instance of IOData with data for one frame loaded for the file. """ - format_module = _select_format_module(filename, 'load_many', fmt) + format_module = _select_format_module(filename, "load_many", fmt) lit = LineIterator(filename) for data in format_module.load_many(lit, **kwargs): try: @@ -197,8 +197,8 @@ def dump_one(iodata: IOData, filename: str, fmt: str = None, **kwargs): Keyword arguments are passed on to the format-specific dump_one function. """ - format_module = _select_format_module(filename, 'dump_one', fmt) - with open(filename, 'w') as f: + format_module = _select_format_module(filename, "dump_one", fmt) + with open(filename, "w") as f: format_module.dump_one(f, iodata, **kwargs) @@ -221,8 +221,8 @@ def dump_many(iodatas: Iterator[IOData], filename: str, fmt: str = None, **kwarg Keyword arguments are passed on to the format-specific dump_many function. """ - format_module = _select_format_module(filename, 'dump_many', fmt) - with open(filename, 'w') as f: + format_module = _select_format_module(filename, "dump_many", fmt) + with open(filename, "w") as f: format_module.dump_many(f, iodatas, **kwargs) @@ -244,5 +244,5 @@ def write_input(iodata: IOData, filename: str, fmt: str, template: str = None, * """ input_module = _select_input_module(fmt) - with open(filename, 'w') as f: + with open(filename, "w") as f: input_module.write_input(f, iodata, template=template, **kwargs) diff --git a/iodata/attrutils.py b/iodata/attrutils.py index 41466ad3..b3f0b3fd 100644 --- a/iodata/attrutils.py +++ b/iodata/attrutils.py @@ -18,7 +18,6 @@ # -- """Utilities for building attr classes.""" - import numpy as np @@ -27,10 +26,12 @@ def convert_array_to(dtype): """Return a function to convert arrays to the given type.""" + def converter(array): if array is None: return None return np.array(array, copy=False, dtype=dtype) + return converter @@ -73,6 +74,7 @@ def validate_shape(*shape_requirements: tuple): the expected size of the array being checked along axis ``i``. """ + def validator(obj, attribute, value): # Build the expected shape, with the rules from the docstring. expected_shape = [] @@ -85,9 +87,7 @@ def validator(obj, attribute, value): other_name, other_axis = item other = getattr(obj, other_name) if other is None: - raise TypeError( - "Other attribute '{}' is not set.".format(other_name) - ) + raise TypeError("Other attribute '{}' is not set.".format(other_name)) if other_axis == 0: expected_shape.append(len(other)) else: diff --git a/iodata/basis.py b/iodata/basis.py index 92f4c90e..117efa2c 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -34,20 +34,30 @@ from .attrutils import validate_shape -__all__ = ['angmom_sti', 'angmom_its', 'Shell', 'MolecularBasis', - 'convert_convention_shell', 'convert_conventions', - 'iter_cart_alphabet', 'HORTON2_CONVENTIONS', 'CCA_CONVENTIONS'] +__all__ = [ + "angmom_sti", + "angmom_its", + "Shell", + "MolecularBasis", + "convert_convention_shell", + "convert_conventions", + "iter_cart_alphabet", + "HORTON2_CONVENTIONS", + "CCA_CONVENTIONS", +] -ANGMOM_CHARS = 'spdfghiklmnoqrtuvwxyzabce' +ANGMOM_CHARS = "spdfghiklmnoqrtuvwxyzabce" def _alsolist(f): """Wrap a function to accepts also list as first argument and then return list.""" + @wraps(f) def wrapper(firsts, *args, **kwargs): if isinstance(firsts, (Integral, str)): return f(firsts, *args, **kwargs) return [f(first, *args, **kwargs) for first in firsts] + return wrapper @@ -91,8 +101,7 @@ def angmom_its(angmom: Union[int, List[int]]) -> Union[str, List[str]]: return ANGMOM_CHARS[angmom] -@attr.s(auto_attribs=True, slots=True, - on_setattr=[attr.setters.validate, attr.setters.convert]) +@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) class Shell: """A shell in a molecular basis representing (generalized) contractions with the same exponents. @@ -129,12 +138,12 @@ def nbasis(self) -> int: # noqa: D401 """Number of basis functions (e.g. 3 for a P shell and 4 for an SP shell).""" result = 0 for angmom, kind in zip(self.angmoms, self.kinds): - if kind == 'c': # Cartesian + if kind == "c": # Cartesian result += ((angmom + 1) * (angmom + 2)) // 2 - elif kind == 'p' and angmom >= 2: + elif kind == "p" and angmom >= 2: result += 2 * angmom + 1 else: - raise TypeError('Unknown shell kind \'{}\'; expected \'c\' or \'p\'.'.format(kind)) + raise TypeError("Unknown shell kind '{}'; expected 'c' or 'p'.".format(kind)) return result @property @@ -148,8 +157,7 @@ def ncon(self) -> int: # noqa: D401 return len(self.angmoms) -@attr.s(auto_attribs=True, slots=True, - on_setattr=[attr.setters.validate, attr.setters.convert]) +@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) class MolecularBasis: """A complete molecular orbital or density basis set. @@ -212,14 +220,16 @@ def get_segmented(self): shells = [] for shell in self.shells: for angmom, kind, coeffs in zip(shell.angmoms, shell.kinds, shell.coeffs.T): - shells.append(Shell(shell.icenter, [angmom], [kind], - shell.exponents, coeffs.reshape(-1, 1))) + shells.append( + Shell(shell.icenter, [angmom], [kind], shell.exponents, coeffs.reshape(-1, 1)) + ) # pylint: disable=no-member return attr.evolve(self, shells=shells) -def convert_convention_shell(conv1: List[str], conv2: List[str], reverse=False) \ - -> Tuple[np.ndarray, np.ndarray]: +def convert_convention_shell( + conv1: List[str], conv2: List[str], reverse=False +) -> Tuple[np.ndarray, np.ndarray]: """Return a permutation vector and sign changes to convert from 1 to 2. The transformation from convention 1 to convention 2 can be done applying @@ -253,20 +263,22 @@ def convert_convention_shell(conv1: List[str], conv2: List[str], reverse=False) """ if len(conv1) != len(conv2): - raise TypeError('conv1 and conv2 must contain the same number of elements.') + raise TypeError("conv1 and conv2 must contain the same number of elements.") # Get signs from both - signs1 = [1 - 2 * el1.startswith('-') for el1 in conv1] - signs2 = [1 - 2 * el2.startswith('-') for el2 in conv2] + signs1 = [1 - 2 * el1.startswith("-") for el1 in conv1] + signs2 = [1 - 2 * el2.startswith("-") for el2 in conv2] # Strip signs from both - conv1 = [el1.lstrip('-') for el1 in conv1] - conv2 = [el2.lstrip('-') for el2 in conv2] + conv1 = [el1.lstrip("-") for el1 in conv1] + conv2 = [el2.lstrip("-") for el2 in conv2] if len(conv1) != len(set(conv1)): - raise TypeError('Argument conv1 contains duplicates.') + raise TypeError("Argument conv1 contains duplicates.") if len(conv2) != len(set(conv2)): - raise TypeError('Argument conv2 contains duplicates.') + raise TypeError("Argument conv2 contains duplicates.") if set(conv1) != set(conv2): - raise TypeError('Without the minus signs, conv1 and conv2 must contain ' - 'the same elements. Got {} and {}.'.format(conv1, conv2)) + raise TypeError( + "Without the minus signs, conv1 and conv2 must contain " + "the same elements. Got {} and {}.".format(conv1, conv2) + ) # Get the permutation if reverse: permutation = [conv2.index(el1) for el1 in conv1] @@ -277,8 +289,9 @@ def convert_convention_shell(conv1: List[str], conv2: List[str], reverse=False) return permutation, signs -def convert_conventions(molbasis: MolecularBasis, new_conventions: Dict[str, List[str]], - reverse=False) -> Tuple[np.ndarray, np.ndarray]: +def convert_conventions( + molbasis: MolecularBasis, new_conventions: Dict[str, List[str]], reverse=False +) -> Tuple[np.ndarray, np.ndarray]: """Return a permutation vector and sign changes to convert from 1 to 2. The transformation from molbasis.convention to the new convention can be done @@ -376,20 +389,21 @@ def get_default_conventions() -> Tuple[Dict, Dict]: Architecture (CCA). """ - horton2 = {(0, 'c'): ['1']} + horton2 = {(0, "c"): ["1"]} cca = horton2.copy() for angmom in range(1, 25): - conv_cart = list('x' * nx + 'y' * ny + 'z' * nz - for nx, ny, nz in iter_cart_alphabet(angmom)) - key = (angmom, 'c') + conv_cart = list( + "x" * nx + "y" * ny + "z" * nz for nx, ny, nz in iter_cart_alphabet(angmom) + ) + key = (angmom, "c") horton2[key] = conv_cart cca[key] = conv_cart if angmom > 1: - conv_pure = ['c0'] + conv_pure = ["c0"] for absm in range(1, angmom + 1): - conv_pure.append('c{}'.format(absm)) - conv_pure.append('s{}'.format(absm)) - key = (angmom, 'p') + conv_pure.append("c{}".format(absm)) + conv_pure.append("s{}".format(absm)) + key = (angmom, "p") horton2[key] = conv_pure cca[key] = conv_pure[:1:-2] + conv_pure[:1] + conv_pure[1::2] return horton2, cca diff --git a/iodata/docstrings.py b/iodata/docstrings.py index 820ba067..e972c0fb 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -19,31 +19,43 @@ # pylint: disable=dangerous-default-value """Docstring decorators for file format implementations.""" - from typing import List, Dict -__all__ = ['document_load_one', 'document_load_many', 'document_dump_one', 'document_dump_many', - 'document_write_input'] +__all__ = [ + "document_load_one", + "document_load_many", + "document_dump_one", + "document_dump_many", + "document_write_input", +] -def _document_load(template: str, fmt: str, guaranteed: List[str], ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def _document_load( + template: str, + fmt: str, + guaranteed: List[str], + ifpresent: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): ifpresent = ifpresent or [] def decorator(func): if ifpresent: - ifpresent_sentence = ( - " The following may be loaded if present in the file: {}.".format( - ', '.join("``{}``".format(word) for word in ifpresent))) + ifpresent_sentence = " The following may be loaded if present in the file: {}.".format( + ", ".join("``{}``".format(word) for word in ifpresent) + ) else: ifpresent_sentence = "" func.__doc__ = template.format( fmt=fmt, - guaranteed=', '.join("``{}``".format(word) for word in guaranteed), + guaranteed=", ".join("``{}``".format(word) for word in guaranteed), ifpresent=ifpresent_sentence, - kwdocs="\n".join("{}\n {}".format(name, docu.replace("\n", " ")) - for name, docu in sorted(kwdocs.items())), + kwdocs="\n".join( + "{}\n {}".format(name, docu.replace("\n", " ")) + for name, docu in sorted(kwdocs.items()) + ), notes=(notes or ""), ) func.fmt = fmt @@ -52,6 +64,7 @@ def decorator(func): func.kwdocs = kwdocs func.notes = notes return func + return decorator @@ -77,8 +90,13 @@ def decorator(func): """ -def document_load_one(fmt: str, guaranteed: List[str], ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def document_load_one( + fmt: str, + guaranteed: List[str], + ifpresent: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): """Decorate a load_one function to generate a docstring. Parameters @@ -127,8 +145,13 @@ def document_load_one(fmt: str, guaranteed: List[str], ifpresent: List[str] = No """ -def document_load_many(fmt: str, guaranteed: List[str], ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def document_load_many( + fmt: str, + guaranteed: List[str], + ifpresent: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): """Decorate a load_many function to generate a docstring. Parameters @@ -155,8 +178,14 @@ def document_load_many(fmt: str, guaranteed: List[str], ifpresent: List[str] = N return _document_load(LOAD_MANY_DOC_TEMPLATE, fmt, guaranteed, ifpresent, kwdocs, notes) -def _document_dump(template: str, fmt: str, required: List[str], optional: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def _document_dump( + template: str, + fmt: str, + required: List[str], + optional: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): optional = optional or [] def decorator(func): @@ -164,15 +193,17 @@ def decorator(func): optional_sentence = ( " If the following attributes are present, they are also dumped " "into the file: {}." - ).format(', '.join("``{}``".format(word) for word in optional)) + ).format(", ".join("``{}``".format(word) for word in optional)) else: optional_sentence = "" func.__doc__ = template.format( fmt=fmt, - required=', '.join("``{}``".format(word) for word in required), + required=", ".join("``{}``".format(word) for word in required), optional=optional_sentence, - kwdocs="\n".join("{}\n {}".format(name, docu.replace("\n", " ")) - for name, docu in sorted(kwdocs.items())), + kwdocs="\n".join( + "{}\n {}".format(name, docu.replace("\n", " ")) + for name, docu in sorted(kwdocs.items()) + ), notes=(notes or ""), ) func.fmt = fmt @@ -181,6 +212,7 @@ def decorator(func): func.kwdocs = kwdocs func.notes = notes return func + return decorator @@ -203,8 +235,13 @@ def decorator(func): """ -def document_dump_one(fmt: str, required: List[str], optional: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def document_dump_one( + fmt: str, + required: List[str], + optional: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): """Decorate a dump_one function to generate a docstring. Parameters @@ -250,8 +287,13 @@ def document_dump_one(fmt: str, required: List[str], optional: List[str] = None, """ -def document_dump_many(fmt: str, required: List[str], optional: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def document_dump_many( + fmt: str, + required: List[str], + optional: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): """Decorate a dump_many function to generate a docstring. Parameters @@ -278,8 +320,14 @@ def document_dump_many(fmt: str, required: List[str], optional: List[str] = None return _document_dump(DUMP_MANY_DOC_TEMPLATE, fmt, required, optional, kwdocs, notes) -def _document_write(template: str, fmt: str, required: List[str], optional: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def _document_write( + template: str, + fmt: str, + required: List[str], + optional: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): optional = optional or [] def decorator(func): @@ -288,15 +336,17 @@ def decorator(func): " If the following attributes are present, they are also written " "into the file: {}. If these attributes are not assigned, " "internal default values are used." - ).format(', '.join("``{}``".format(word) for word in optional)) + ).format(", ".join("``{}``".format(word) for word in optional)) else: optional_sentence = "" func.__doc__ = template.format( fmt=fmt, - required=', '.join("``{}``".format(word) for word in required), + required=", ".join("``{}``".format(word) for word in required), optional=optional_sentence, - kwdocs="\n".join("{}\n {}".format(name, docu.replace("\n", " ")) - for name, docu in sorted(kwdocs.items())), + kwdocs="\n".join( + "{}\n {}".format(name, docu.replace("\n", " ")) + for name, docu in sorted(kwdocs.items()) + ), notes=(notes or ""), ) func.fmt = fmt @@ -305,6 +355,7 @@ def decorator(func): func.kwdocs = kwdocs func.notes = notes return func + return decorator @@ -329,8 +380,13 @@ def decorator(func): """ -def document_write_input(fmt: str, required: List[str], optional: List[str] = None, - kwdocs: Dict[str, str] = {}, notes: str = None): +def document_write_input( + fmt: str, + required: List[str], + optional: List[str] = None, + kwdocs: Dict[str, str] = {}, + notes: str = None, +): """Decorate a write_input function to generate a docstring. Parameters diff --git a/iodata/formats/charmm.py b/iodata/formats/charmm.py index 95f120fd..8ec24d23 100644 --- a/iodata/formats/charmm.py +++ b/iodata/formats/charmm.py @@ -29,7 +29,6 @@ """ - from typing import Tuple import numpy as np @@ -41,14 +40,14 @@ __all__ = [] -PATTERNS = ['*.crd'] +PATTERNS = ["*.crd"] -@document_load_one('CRD', ['atcoords', 'atffparams', 'atmasses', 'extra'], ['title']) +@document_load_one("CRD", ["atcoords", "atffparams", "atmasses", "extra"], ["title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # Read title section - title = '' + title = "" while True: try: line = next(lit) @@ -69,21 +68,17 @@ def load_one(lit: LineIterator) -> dict: segid = np.array(data[4]) resid = np.array(data[5]) atmasses = np.array(data[6]) - atffparams = { - 'attypes': attypes, - 'resnames': resnames, - 'resnums': resnums - } + atffparams = {"attypes": attypes, "resnames": resnames, "resnums": resnums} extra = { - 'segid': segid, - 'resid': resid, + "segid": segid, + "resid": resid, } result = { - 'atcoords': atcoords, - 'atffparams': atffparams, - 'atmasses': atmasses, - 'extra': extra, - 'title': title, + "atcoords": atcoords, + "atffparams": atffparams, + "atmasses": atmasses, + "extra": extra, + "title": title, } return result @@ -93,7 +88,7 @@ def _helper_read_crd(lit: LineIterator) -> Tuple: # Read the line for number of atoms. natom = next(lit) if natom is None or not natom.strip().isdigit(): - lit.error('The number of atoms must be an integer.') + lit.error("The number of atoms must be an integer.") natom = int(natom) # Read the atom lines resnums = [] diff --git a/iodata/formats/chgcar.py b/iodata/formats/chgcar.py index bdc74610..0e66ce1c 100644 --- a/iodata/formats/chgcar.py +++ b/iodata/formats/chgcar.py @@ -25,7 +25,6 @@ different conversions to atomic units. """ - from typing import Tuple import numpy as np @@ -38,7 +37,7 @@ __all__ = [] -PATTERNS = ['CHGCAR*', 'AECCAR*'] +PATTERNS = ["CHGCAR*", "AECCAR*"] def _load_vasp_header(lit: LineIterator) -> Tuple[str, np.ndarray, np.ndarray, np.ndarray]: @@ -81,10 +80,10 @@ def _load_vasp_header(lit: LineIterator) -> Tuple[str, np.ndarray, np.ndarray, n line = next(lit) # the 7th line can optionally indicate selective dynamics - if line[0].lower() in ['s']: + if line[0].lower() in ["s"]: line = next(lit) # parse direct/cartesian switch - cartesian = line[0].lower() in ['c', 'k'] + cartesian = line[0].lower() in ["c", "k"] # read the coordinates atcoords = [] @@ -133,22 +132,21 @@ def _load_vasp_grid(lit: LineIterator) -> dict: words = next(lit).split() cube_data[i0, i1, i2] = float(words.pop(0)) - cube = Cube(origin=np.zeros(3), axes=cellvecs / shape.reshape(-1, 1), - data=cube_data) + cube = Cube(origin=np.zeros(3), axes=cellvecs / shape.reshape(-1, 1), data=cube_data) return { - 'title': title, - 'atcoords': atcoords, - 'atnums': atnums, - 'cellvecs': cellvecs, - 'cube': cube, + "title": title, + "atcoords": atcoords, + "atnums": atnums, + "cellvecs": cellvecs, + "cube": cube, } -@document_load_one("VASP 5 CHGCAR", ['atcoords', 'atnums', 'cellvecs', 'cube', 'title']) +@document_load_one("VASP 5 CHGCAR", ["atcoords", "atnums", "cellvecs", "cube", "title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" result = _load_vasp_grid(lit) # renormalize electron density - result['cube'].data[:] /= volume(result['cellvecs']) + result["cube"].data[:] /= volume(result["cellvecs"]) return result diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index bf803baf..527f91bd 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -18,7 +18,6 @@ # -- """CP2K ATOM output file format.""" - from typing import Dict, Union, List, Tuple import numpy as np @@ -33,19 +32,20 @@ __all__ = [] -PATTERNS = ['*.cp2k.out'] +PATTERNS = ["*.cp2k.out"] CONVENTIONS = { - (0, 'c'): HORTON2_CONVENTIONS[(0, 'c')], - (1, 'c'): HORTON2_CONVENTIONS[(1, 'c')], - (2, 'p'): HORTON2_CONVENTIONS[(2, 'p')], - (3, 'p'): HORTON2_CONVENTIONS[(3, 'p')], + (0, "c"): HORTON2_CONVENTIONS[(0, "c")], + (1, "c"): HORTON2_CONVENTIONS[(1, "c")], + (2, "p"): HORTON2_CONVENTIONS[(2, "p")], + (3, "p"): HORTON2_CONVENTIONS[(3, "p")], } -def _get_cp2k_norm_corrections(l: int, alphas: Union[float, np.ndarray]) \ - -> Union[float, np.ndarray]: +def _get_cp2k_norm_corrections( + l: int, alphas: Union[float, np.ndarray] +) -> Union[float, np.ndarray]: """Compute the corrections for the normalization of the basis functions. This correction is needed because the CP2K atom code works with a different @@ -71,7 +71,7 @@ def _get_cp2k_norm_corrections(l: int, alphas: Union[float, np.ndarray]) \ expzet = 0.25 * (2 * l + 3) prefac = np.sqrt(np.sqrt(np.pi) / 2.0 ** (l + 2) * factorialk(2 * l + 1, 2)) zeta = 2.0 * alphas - return zeta ** expzet / prefac + return zeta**expzet / prefac def _read_cp2k_contracted_obasis(lit: LineIterator) -> MolecularBasis: @@ -91,32 +91,33 @@ def _read_cp2k_contracted_obasis(lit: LineIterator) -> MolecularBasis: shells = [] while True: line = next(lit) - if line[3:12] != 'Functions': + if line[3:12] != "Functions": break angmom = angmom_sti(line[1:2]) exponents = [] coeffs = [] for line in lit: - if line[3:12] == 'Functions' or line.startswith(' *******************'): + if line[3:12] == "Functions" or line.startswith(" *******************"): break values = [float(w) for w in line.split()] # one exponent per line exponents.append(values[0]) # many contraction coefficients per line, all corresponding to the # same primitive, so rows in coeffs - coeffs.append( - np.array(values[1:]) / _get_cp2k_norm_corrections(angmom, values[0])) + coeffs.append(np.array(values[1:]) / _get_cp2k_norm_corrections(angmom, values[0])) # Push back the last line for the next iteration lit.back(line) # Build the shell exponents = np.array(exponents) coeffs = np.array(coeffs) - kind = 'c' if angmom < 2 else 'p' - shells.append(Shell(0, np.array([angmom] * coeffs.shape[1]), - [kind] * coeffs.shape[1], - exponents, coeffs)) + kind = "c" if angmom < 2 else "p" + shells.append( + Shell( + 0, np.array([angmom] * coeffs.shape[1]), [kind] * coeffs.shape[1], exponents, coeffs + ) + ) - return MolecularBasis(shells, CONVENTIONS, 'L2') + return MolecularBasis(shells, CONVENTIONS, "L2") def _read_cp2k_uncontracted_obasis(lit: LineIterator) -> MolecularBasis: @@ -139,7 +140,7 @@ def _read_cp2k_uncontracted_obasis(lit: LineIterator) -> MolecularBasis: next(lit) while True: line = next(lit) - if line[3:13] != 'Exponents:': + if line[3:13] != "Exponents:": break angmom = angmom_sti(line[1:2]) exponents = [] @@ -154,13 +155,13 @@ def _read_cp2k_uncontracted_obasis(lit: LineIterator) -> MolecularBasis: coeffs.append(1.0 / _get_cp2k_norm_corrections(angmom, exponent)) line = next(lit) # Build the shell - kind = 'c' if angmom < 2 else 'p' + kind = "c" if angmom < 2 else "p" for exponent, coeff in zip(exponents, coeffs): - shells.append(Shell( - 0, np.array([angmom]), [kind], - np.array([exponent]), np.array([[coeff]]))) + shells.append( + Shell(0, np.array([angmom]), [kind], np.array([exponent]), np.array([[coeff]])) + ) - return MolecularBasis(shells, CONVENTIONS, 'L2') + return MolecularBasis(shells, CONVENTIONS, "L2") # pylint: disable=inconsistent-return-statements @@ -181,17 +182,20 @@ def _read_cp2k_obasis(lit: LineIterator) -> dict: """ next(lit) # Skip empty line line = next(lit) # Check for contracted versus uncontracted - if line == (' ********************** Contracted Gaussian Type Orbitals ' - '**********************\n'): + if line == ( + " ********************** Contracted Gaussian Type Orbitals " "**********************\n" + ): return _read_cp2k_contracted_obasis(lit) - if line == (' ********************* Uncontracted Gaussian Type Orbitals ' - '*********************\n'): + if line == ( + " ********************* Uncontracted Gaussian Type Orbitals " "*********************\n" + ): return _read_cp2k_uncontracted_obasis(lit) - lit.error('Could not find basis set in CP2K ATOM output.') + lit.error("Could not find basis set in CP2K ATOM output.") -def _read_cp2k_occupations_energies(lit: LineIterator, restricted: bool) \ - -> List[Tuple[int, int, float, float]]: +def _read_cp2k_occupations_energies( + lit: LineIterator, restricted: bool +) -> List[Tuple[int, int, float, float]]: """Read orbital occupation numbers and energies from a CP2K ATOM file object. Parameters @@ -224,15 +228,16 @@ def _read_cp2k_occupations_energies(lit: LineIterator, restricted: bool) \ l = int(words[2 - restricted]) occ = float(words[3 - restricted]) ener = float(words[4 - restricted]) - if restricted or words[1] == 'alpha': + if restricted or words[1] == "alpha": oe_alpha.append((l, s, occ, ener)) else: oe_beta.append((l, s, occ, ener)) return oe_alpha, oe_beta -def _read_cp2k_orbital_coeffs(lit: LineIterator, oe: List[Tuple[int, int, float, float]]) \ - -> Dict[Tuple[int, int], np.ndarray]: +def _read_cp2k_orbital_coeffs( + lit: LineIterator, oe: List[Tuple[int, int, float, float]] +) -> Dict[Tuple[int, int], np.ndarray]: """Read the expansion coefficients of the orbital from an open CP2K ATOM output. Parameters @@ -289,13 +294,15 @@ def _get_norb_nel(oe: List[Tuple[int, int, float, float]]) -> Tuple[int, float]: return norb, nel -def _fill_orbitals(orb_coeffs: np.ndarray, - orb_energies: np.ndarray, - orb_occupations: np.ndarray, - oe: List[Tuple[int, int, float, float]], - coeffs: Dict[Tuple[int, int], np.ndarray], - obasis: MolecularBasis, - restricted: bool): +def _fill_orbitals( + orb_coeffs: np.ndarray, + orb_energies: np.ndarray, + orb_occupations: np.ndarray, + oe: List[Tuple[int, int, float, float]], + coeffs: Dict[Tuple[int, int], np.ndarray], + obasis: MolecularBasis, + restricted: bool, +): """Fill in orbital coefficients, energies and occupation numbers. The data is entered int ``orb_coeffs``, ``orb_energies``, and ``orb_occupations``. @@ -363,48 +370,51 @@ def _fill_orbitals(orb_coeffs: np.ndarray, # pylint: disable=too-many-branches,too-many-statements @document_load_one( "CP2K ATOM outupt", - ['atcoords', 'atcorenums', 'atnums', 'energy', 'mo', 'obasis'], - [], {}, LOAD_ONE_NOTES) + ["atcoords", "atcorenums", "atnums", "energy", "mo", "obasis"], + [], + {}, + LOAD_ONE_NOTES, +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # Find the element number atnum = None for line in lit: - if line.startswith(' Atomic Energy Calculation'): + if line.startswith(" Atomic Energy Calculation"): atnum = int(line[-5:-1]) break # Go to the all-electron basis set and read it. for line in lit: - if line.startswith(' All Electron Basis'): + if line.startswith(" All Electron Basis"): break ae_obasis = _read_cp2k_obasis(lit) # Go to the pseudo basis set and read it. for line in lit: - if line.startswith(' Pseudopotential Basis'): + if line.startswith(" Pseudopotential Basis"): break pp_obasis = _read_cp2k_obasis(lit) # Search for (un)restricted restricted = None for line in lit: - if line.startswith(' METHOD |'): - if 'U' in line: + if line.startswith(" METHOD |"): + if "U" in line: restricted = False break - if 'R' in line: + if "R" in line: restricted = True break # Search for the core charge (pseudo number) atcorenum = None for line in lit: - if line.startswith(' Core Charge'): + if line.startswith(" Core Charge"): atcorenum = float(line[70:]) assert atcorenum == int(atcorenum) break - if line.startswith(' Electronic structure'): + if line.startswith(" Electronic structure"): atcorenum = float(atnum) break @@ -416,28 +426,30 @@ def load_one(lit: LineIterator) -> dict: # Search for energy for line in lit: - if line.startswith(' Energy components [Hartree] Total Energy ::'): + if line.startswith(" Energy components [Hartree] Total Energy ::"): energy = float(line[60:]) break # Read orbital energies and occupations for line in lit: - if line.startswith(' Orbital energies'): + if line.startswith(" Orbital energies"): break next(lit) oe_alpha, oe_beta = _read_cp2k_occupations_energies(lit, restricted) # Read orbital expansion coefficients line = next(lit) - if line not in [" Atomic orbital expansion coefficients [Alpha]\n", - " Atomic orbital expansion coefficients []\n"]: - lit.error('Could not find orbital coefficients in CP2K ATOM output.') + if line not in [ + " Atomic orbital expansion coefficients [Alpha]\n", + " Atomic orbital expansion coefficients []\n", + ]: + lit.error("Could not find orbital coefficients in CP2K ATOM output.") coeffs_alpha = _read_cp2k_orbital_coeffs(lit, oe_alpha) if not restricted: line = next(lit) if line != " Atomic orbital expansion coefficients [Beta]\n": - lit.error('Could not find beta orbital coefficient in CP2K ATOM output.') + lit.error("Could not find beta orbital coefficient in CP2K ATOM output.") coeffs_beta = _read_cp2k_orbital_coeffs(lit, oe_beta) # Turn orbital data into a MolecularOrbitals object. @@ -447,11 +459,18 @@ def load_one(lit: LineIterator) -> dict: orb_alpha_coeffs = np.zeros([obasis.nbasis, norb]) orb_alpha_energies = np.zeros(norb) orb_alpha_occs = np.zeros(norb) - _fill_orbitals(orb_alpha_coeffs, orb_alpha_energies, orb_alpha_occs, - oe_alpha, coeffs_alpha, obasis, restricted) + _fill_orbitals( + orb_alpha_coeffs, + orb_alpha_energies, + orb_alpha_occs, + oe_alpha, + coeffs_alpha, + obasis, + restricted, + ) mo = MolecularOrbitals( - 'restricted', norb, norb, 2 * orb_alpha_occs, orb_alpha_coeffs, - orb_alpha_energies) + "restricted", norb, norb, 2 * orb_alpha_occs, orb_alpha_coeffs, orb_alpha_energies + ) else: norb_alpha = _get_norb_nel(oe_alpha)[0] norb_beta = _get_norb_nel(oe_beta)[0] @@ -462,24 +481,40 @@ def load_one(lit: LineIterator) -> dict: orb_beta_coeffs = np.zeros([obasis.nbasis, norb_beta]) orb_beta_energies = np.zeros(norb_beta) orb_beta_occs = np.zeros(norb_beta) - _fill_orbitals(orb_alpha_coeffs, orb_alpha_energies, orb_alpha_occs, - oe_alpha, coeffs_alpha, obasis, restricted) - _fill_orbitals(orb_beta_coeffs, orb_beta_energies, orb_beta_occs, - oe_beta, coeffs_beta, obasis, restricted) + _fill_orbitals( + orb_alpha_coeffs, + orb_alpha_energies, + orb_alpha_occs, + oe_alpha, + coeffs_alpha, + obasis, + restricted, + ) + _fill_orbitals( + orb_beta_coeffs, + orb_beta_energies, + orb_beta_occs, + oe_beta, + coeffs_beta, + obasis, + restricted, + ) mo = MolecularOrbitals( - 'unrestricted', norb_alpha, norb_beta, + "unrestricted", + norb_alpha, + norb_beta, np.concatenate((orb_alpha_occs, orb_beta_occs), axis=0), np.concatenate((orb_alpha_coeffs, orb_beta_coeffs), axis=1), np.concatenate((orb_alpha_energies, orb_beta_energies), axis=0), ) result = { - 'obasis': obasis, - 'mo': mo, - 'atcoords': np.zeros((1, 3), float), - 'atnums': np.array([atnum]), - 'energy': energy, - 'atcorenums': np.array([atcorenum]), + "obasis": obasis, + "mo": mo, + "atcoords": np.zeros((1, 3), float), + "atnums": np.array([atnum]), + "energy": energy, + "atcorenums": np.array([atcorenum]), } return result diff --git a/iodata/formats/cube.py b/iodata/formats/cube.py index e247686c..a66373f6 100644 --- a/iodata/formats/cube.py +++ b/iodata/formats/cube.py @@ -26,7 +26,6 @@ as the effective core charges. """ - from typing import TextIO, Dict, Tuple import numpy as np @@ -39,11 +38,12 @@ __all__ = [] -PATTERNS = ['*.cube', '*.cub'] +PATTERNS = ["*.cube", "*.cub"] -def _read_cube_header(lit: LineIterator) \ - -> Tuple[str, np.ndarray, np.ndarray, np.ndarray, Dict[str, np.ndarray], np.ndarray]: +def _read_cube_header( + lit: LineIterator, +) -> Tuple[str, np.ndarray, np.ndarray, np.ndarray, Dict[str, np.ndarray], np.ndarray]: """Load header data from a CUBE file object. Parameters @@ -68,7 +68,7 @@ def read_grid_line(line: str) -> Tuple[int, np.ndarray]: words = line.split() return ( int(words[0]), - np.array([float(words[1]), float(words[2]), float(words[3])], float) + np.array([float(words[1]), float(words[2]), float(words[3])], float), # all coordinates in a cube file are in atomic units ) @@ -82,14 +82,15 @@ def read_grid_line(line: str) -> Tuple[int, np.ndarray]: axes = np.array([axis0, axis1, axis2]) cellvecs = axes * shape.reshape(-1, 1) - cube = {"origin": origin, 'axes': axes, 'shape': shape} + cube = {"origin": origin, "axes": axes, "shape": shape} def read_atom_line(line: str) -> Tuple[int, float, np.ndarray]: """Read an atomic number and coordinate from the cube file.""" words = line.split() return ( - int(words[0]), float(words[1]), - np.array([float(words[2]), float(words[3]), float(words[4])], float) + int(words[0]), + float(words[1]), + np.array([float(words[2]), float(words[3]), float(words[4])], float), # all coordinates in a cube file are in atomic units ) @@ -120,8 +121,8 @@ def _read_cube_data(lit: LineIterator, cube: Dict[str, np.ndarray]): The cube data array. """ - cube['data'] = np.zeros(tuple(cube['shape']), float) - tmp = cube['data'].ravel() + cube["data"] = np.zeros(tuple(cube["shape"]), float) + tmp = cube["data"].ravel() counter = 0 words = [] while counter < tmp.size: @@ -131,56 +132,62 @@ def _read_cube_data(lit: LineIterator, cube: Dict[str, np.ndarray]): counter += 1 -@document_load_one("Gaussian Cube", ['atcoords', 'atcorenums', 'atnums', 'cellvecs', 'cube']) +@document_load_one("Gaussian Cube", ["atcoords", "atcorenums", "atnums", "cellvecs", "cube"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" title, atcoords, atnums, cellvecs, cube, atcorenums = _read_cube_header(lit) _read_cube_data(lit, cube) del cube["shape"] return { - 'title': title, - 'atcoords': atcoords, - 'atnums': atnums, - 'cellvecs': cellvecs, - 'cube': Cube(**cube), - 'atcorenums': atcorenums, + "title": title, + "atcoords": atcoords, + "atnums": atnums, + "cellvecs": cellvecs, + "cube": Cube(**cube), + "atcorenums": atcorenums, } -def _write_cube_header(f: TextIO, title: str, atcoords: np.ndarray, atnums: np.ndarray, - cube: Dict[str, np.ndarray], atcorenums: np.ndarray): +def _write_cube_header( + f: TextIO, + title: str, + atcoords: np.ndarray, + atnums: np.ndarray, + cube: Dict[str, np.ndarray], + atcorenums: np.ndarray, +): print(title, file=f) - print('OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z', file=f) + print("OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z", file=f) natom = len(atnums) x, y, z = cube.origin - print(f'{natom:5d} {x: 11.6f} {y: 11.6f} {z: 11.6f}', file=f) + print(f"{natom:5d} {x: 11.6f} {y: 11.6f} {z: 11.6f}", file=f) for i in range(3): x, y, z = cube.axes[i] - print(f'{cube.shape[i]:5d} {x: 11.6f} {y: 11.6f} {z: 11.6f}', file=f) + print(f"{cube.shape[i]:5d} {x: 11.6f} {y: 11.6f} {z: 11.6f}", file=f) for i in range(natom): q = atcorenums[i] x, y, z = atcoords[i] - print(f'{atnums[i]:5d} {q: 11.6f} {x: 11.6f} {y: 11.6f} {z: 11.6f}', file=f) + print(f"{atnums[i]:5d} {q: 11.6f} {x: 11.6f} {y: 11.6f} {z: 11.6f}", file=f) def _write_cube_data(f: TextIO, cube_data: np.ndarray, block_size: int): counter = 0 for value in cube_data.flat: - f.write(f' {value: 12.5E}') + f.write(f" {value: 12.5E}") # go to next line after adding 6 values on a line if counter % 6 == 5: - f.write('\n') + f.write("\n") # go to next line after reaching the block_size & reset counter if block_size % 6 != 0 and counter % block_size == block_size - 1: - f.write('\n') + f.write("\n") counter = 0 continue counter += 1 -@document_dump_one("Gaussian Cube", ['atcoords', 'atnums', 'cube'], ['title', 'atcorenums']) +@document_dump_one("Gaussian Cube", ["atcoords", "atnums", "cube"], ["title", "atcorenums"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" - title = data.title or 'Created with IOData' + title = data.title or "Created with IOData" _write_cube_header(f, title, data.atcoords, data.atnums, data.cube, data.atcorenums) _write_cube_data(f, data.cube.data, data.cube.shape[2]) diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index 1db534ae..b5b28706 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -42,7 +42,7 @@ __all__ = [] -PATTERNS = ['*.extxyz'] +PATTERNS = ["*.extxyz"] def _convert_title_value(value: str): @@ -70,8 +70,9 @@ def _convert_title_value(value: str): converted_value = np.array(list_of_splits, dtype=float) except ValueError: try: - converted_value = np.array([strtobool(split) for split in list_of_splits], - dtype=bool) + converted_value = np.array( + [strtobool(split) for split in list_of_splits], dtype=bool + ) except ValueError: converted_value = np.array(list_of_splits, dtype=str) return converted_value @@ -81,47 +82,71 @@ def _parse_properties(properties: str): """Parse the properties into atom_columns.""" atom_columns = [] # Maps the dtype to the atom_columns dtype, load_word and dump_word - dtype_map = {"S": (np.dtype('U25'), str, "{:10s}".format), - "R": (float, float, "{:15.10f}".format), - "I": (int, int, "{:10d}".format), - "L": (bool, strtobool, lambda boolean: "T" if boolean else "F")} + dtype_map = { + "S": (np.dtype("U25"), str, "{:10s}".format), + "R": (float, float, "{:15.10f}".format), + "I": (int, int, "{:10d}".format), + "L": (bool, strtobool, lambda boolean: "T" if boolean else "F"), + } # Some predefined iodata attributes which can be mapped # pos is assumed to be in angstrom, masses in amu (ase convention) # No unit convertion takes place for the other attributes - atom_column_map = {'pos': ('atcoords', None, (3,), float, - (lambda word: float(word) * angstrom), - (lambda value: "{:15.10f}".format(value / angstrom))), - 'masses': ('atmasses', None, (), float, - (lambda word: float(word) * amu), - (lambda value: "{:15.10f}".format(value / amu))), - 'force': ('atgradient', None, (3,), float, - (lambda word: -float(word)), - (lambda value: "{:15.10f}".format(-value)))} - atnum_column = ("atnums", None, (), int, - (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), - (lambda atnum: "{:2s}".format(num2sym[atnum]))) - splitted_properties = properties.split(':') + atom_column_map = { + "pos": ( + "atcoords", + None, + (3,), + float, + (lambda word: float(word) * angstrom), + (lambda value: "{:15.10f}".format(value / angstrom)), + ), + "masses": ( + "atmasses", + None, + (), + float, + (lambda word: float(word) * amu), + (lambda value: "{:15.10f}".format(value / amu)), + ), + "force": ( + "atgradient", + None, + (3,), + float, + (lambda word: -float(word)), + (lambda value: "{:15.10f}".format(-value)), + ), + } + atnum_column = ( + "atnums", + None, + (), + int, + (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), + (lambda atnum: "{:2s}".format(num2sym[atnum])), + ) + splitted_properties = properties.split(":") assert len(splitted_properties) % 3 == 0 # Each property has 3 values: its name, dtype and shape names = splitted_properties[::3] dtypes = splitted_properties[1::3] shapes = splitted_properties[2::3] - if 'Z' in names: + if "Z" in names: # Try to map 'Z' to the 'atnums' attribute - atom_column_map['Z'] = atnum_column - elif 'species' in names: + atom_column_map["Z"] = atnum_column + elif "species" in names: # If 'Z' is not present, use 'species' - atom_column_map['species'] = atnum_column + atom_column_map["species"] = atnum_column for name, dtype, shape in zip(names, dtypes, shapes): if name in atom_column_map.keys(): atom_columns.append(atom_column_map[name]) else: # Use the 'extra' attribute to store values which are not predefined in iodata - if shape == '1': + if shape == "1": shape_suffix = () else: shape_suffix = (int(shape),) - atom_columns.append(('extra', name, shape_suffix, *dtype_map[dtype])) + atom_columns.append(("extra", name, shape_suffix, *dtype_map[dtype])) return atom_columns @@ -132,28 +157,33 @@ def _parse_title(title: str): def load_cellvecs(word): return np.array(word.split(), dtype=float).reshape([3, 3]) * angstrom - iodata_attrs = {'energy': ('energy', float), - 'Lattice': ('cellvecs', load_cellvecs), - 'charge': ('charge', float)} + + iodata_attrs = { + "energy": ("energy", float), + "Lattice": ("cellvecs", load_cellvecs), + "charge": ("charge", float), + } data = {} for key_value_pair in key_value_pairs: - if '=' in key_value_pair: - key, value = key_value_pair.split('=', 1) - if key == 'Properties': + if "=" in key_value_pair: + key, value = key_value_pair.split("=", 1) + if key == "Properties": atom_columns = _parse_properties(value) elif key in iodata_attrs.keys(): data[iodata_attrs[key][0]] = iodata_attrs[key][1](value) else: - data.setdefault('extra', {})[key] = _convert_title_value(value) + data.setdefault("extra", {})[key] = _convert_title_value(value) else: # If no value is given, set it True - data.setdefault('extra', {})[key_value_pair] = True + data.setdefault("extra", {})[key_value_pair] = True return atom_columns, data -@document_load_one("EXTXYZ", ['title'], - ['atcoords', 'atgradient', 'atmasses', 'atnums', 'cellvecs', - 'charge', 'energy', 'extra']) +@document_load_one( + "EXTXYZ", + ["title"], + ["atcoords", "atgradient", "atmasses", "atnums", "cellvecs", "charge", "energy", "extra"], +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" atom_line = next(lit) @@ -164,15 +194,17 @@ def load_one(lit: LineIterator) -> dict: lit.back(atom_line) xyz_data = load_one_xyz(lit, atom_columns) # If the extra attribute is present, prevent it from overwriting itself - if 'extra' in title_data.keys() and 'extra' in xyz_data.keys(): - xyz_data['extra'].update(title_data['extra']) + if "extra" in title_data.keys() and "extra" in xyz_data.keys(): + xyz_data["extra"].update(title_data["extra"]) title_data.update(xyz_data) return title_data -@document_load_many("EXTXYZ", ['title'], - ['atcoords', 'atgradient', 'atmasses', 'atnums', 'cellvecs', - 'charge', 'energy', 'extra']) +@document_load_many( + "EXTXYZ", + ["title"], + ["atcoords", "atgradient", "atmasses", "atnums", "cellvecs", "charge", "energy", "extra"], +) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # XYZ Trajectory files are a simple concatenation of individual XYZ files,' diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 1fea1416..72d16bdb 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -18,7 +18,6 @@ # -- """Gaussian FCHK file format.""" - from fnmatch import fnmatch from typing import List, Tuple, Iterator, TextIO @@ -35,89 +34,126 @@ __all__ = [] -PATTERNS = ['*.fchk', '*.fch'] +PATTERNS = ["*.fchk", "*.fch"] CONVENTIONS = { - (9, 'p'): HORTON2_CONVENTIONS[(9, 'p')], - (8, 'p'): HORTON2_CONVENTIONS[(8, 'p')], - (7, 'p'): HORTON2_CONVENTIONS[(7, 'p')], - (6, 'p'): HORTON2_CONVENTIONS[(6, 'p')], - (5, 'p'): HORTON2_CONVENTIONS[(5, 'p')], - (4, 'p'): HORTON2_CONVENTIONS[(4, 'p')], - (3, 'p'): HORTON2_CONVENTIONS[(3, 'p')], - (2, 'p'): HORTON2_CONVENTIONS[(2, 'p')], - (0, 'c'): ['1'], - (1, 'c'): ['x', 'y', 'z'], - (2, 'c'): ['xx', 'yy', 'zz', 'xy', 'xz', 'yz'], - (3, 'c'): ['xxx', 'yyy', 'zzz', 'xyy', 'xxy', 'xxz', 'xzz', 'yzz', 'yyz', 'xyz'], - (4, 'c'): HORTON2_CONVENTIONS[(4, 'c')][::-1], - (5, 'c'): HORTON2_CONVENTIONS[(5, 'c')][::-1], - (6, 'c'): HORTON2_CONVENTIONS[(6, 'c')][::-1], - (7, 'c'): HORTON2_CONVENTIONS[(7, 'c')][::-1], - (8, 'c'): HORTON2_CONVENTIONS[(8, 'c')][::-1], - (9, 'c'): HORTON2_CONVENTIONS[(9, 'c')][::-1], + (9, "p"): HORTON2_CONVENTIONS[(9, "p")], + (8, "p"): HORTON2_CONVENTIONS[(8, "p")], + (7, "p"): HORTON2_CONVENTIONS[(7, "p")], + (6, "p"): HORTON2_CONVENTIONS[(6, "p")], + (5, "p"): HORTON2_CONVENTIONS[(5, "p")], + (4, "p"): HORTON2_CONVENTIONS[(4, "p")], + (3, "p"): HORTON2_CONVENTIONS[(3, "p")], + (2, "p"): HORTON2_CONVENTIONS[(2, "p")], + (0, "c"): ["1"], + (1, "c"): ["x", "y", "z"], + (2, "c"): ["xx", "yy", "zz", "xy", "xz", "yz"], + (3, "c"): ["xxx", "yyy", "zzz", "xyy", "xxy", "xxz", "xzz", "yzz", "yyz", "xyz"], + (4, "c"): HORTON2_CONVENTIONS[(4, "c")][::-1], + (5, "c"): HORTON2_CONVENTIONS[(5, "c")][::-1], + (6, "c"): HORTON2_CONVENTIONS[(6, "c")][::-1], + (7, "c"): HORTON2_CONVENTIONS[(7, "c")][::-1], + (8, "c"): HORTON2_CONVENTIONS[(8, "c")][::-1], + (9, "c"): HORTON2_CONVENTIONS[(9, "c")][::-1], } # pylint: disable=too-many-branches,too-many-statements @document_load_one( "Gaussian Formatted Checkpoint", - ['atcharges', 'atcoords', 'atnums', 'atcorenums', 'lot', 'mo', 'obasis', - 'obasis_name', 'run_type', 'title'], - ['energy', 'atfrozen', 'atgradient', 'athessian', 'atmasses', 'one_rdms', 'extra', 'moments']) + [ + "atcharges", + "atcoords", + "atnums", + "atcorenums", + "lot", + "mo", + "obasis", + "obasis_name", + "run_type", + "title", + ], + ["energy", "atfrozen", "atgradient", "athessian", "atmasses", "one_rdms", "extra", "moments"], +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" - fchk = _load_fchk_low(lit, [ - "Number of electrons", "Number of basis functions", - "Number of alpha electrons", "Number of beta electrons", - "Atomic numbers", "Current cartesian coordinates", - "Real atomic weights", - "Shell types", "Shell to atom map", "Shell to atom map", - "Number of primitives per shell", "Primitive exponents", - "Contraction coefficients", "P(S=P) Contraction coefficients", - "Alpha Orbital Energies", "Alpha MO coefficients", - "Beta Orbital Energies", "Beta MO coefficients", - "Total Energy", "Nuclear charges", - 'Total SCF Density', 'Spin SCF Density', - 'Total MP2 Density', 'Spin MP2 Density', - 'Total MP3 Density', 'Spin MP3 Density', - 'Total CC Density', 'Spin CC Density', - 'Total CI Density', 'Spin CI Density', - 'Mulliken Charges', 'ESP Charges', 'NPA Charges', - 'MBS Charges', 'Type 6 Charges', 'Type 7 Charges', - 'Polarizability', 'Dipole Moment', 'Quadrupole Moment', - 'Cartesian Gradient', 'Cartesian Force Constants', 'MicOpt', - ]) + fchk = _load_fchk_low( + lit, + [ + "Number of electrons", + "Number of basis functions", + "Number of alpha electrons", + "Number of beta electrons", + "Atomic numbers", + "Current cartesian coordinates", + "Real atomic weights", + "Shell types", + "Shell to atom map", + "Shell to atom map", + "Number of primitives per shell", + "Primitive exponents", + "Contraction coefficients", + "P(S=P) Contraction coefficients", + "Alpha Orbital Energies", + "Alpha MO coefficients", + "Beta Orbital Energies", + "Beta MO coefficients", + "Total Energy", + "Nuclear charges", + "Total SCF Density", + "Spin SCF Density", + "Total MP2 Density", + "Spin MP2 Density", + "Total MP3 Density", + "Spin MP3 Density", + "Total CC Density", + "Spin CC Density", + "Total CI Density", + "Spin CI Density", + "Mulliken Charges", + "ESP Charges", + "NPA Charges", + "MBS Charges", + "Type 6 Charges", + "Type 7 Charges", + "Polarizability", + "Dipole Moment", + "Quadrupole Moment", + "Cartesian Gradient", + "Cartesian Force Constants", + "MicOpt", + ], + ) # A) Load a bunch of simple things result = { - 'title': fchk['title'], + "title": fchk["title"], # if "Total Energy" is not present in FCHk, None is returned. - 'energy': fchk.get('Total Energy', None), - 'lot': fchk['lot'].lower(), - 'obasis_name': fchk['obasis_name'].lower(), - 'atcoords': fchk["Current cartesian coordinates"].reshape(-1, 3), - 'atnums': fchk["Atomic numbers"], - 'atcorenums': fchk["Nuclear charges"], + "energy": fchk.get("Total Energy", None), + "lot": fchk["lot"].lower(), + "obasis_name": fchk["obasis_name"].lower(), + "atcoords": fchk["Current cartesian coordinates"].reshape(-1, 3), + "atnums": fchk["Atomic numbers"], + "atcorenums": fchk["Nuclear charges"], } atmasses = fchk.get("Real atomic weights") if atmasses is not None: - result['atmasses'] = atmasses * amu - atgradient = fchk.get('Cartesian Gradient') + result["atmasses"] = atmasses * amu + atgradient = fchk.get("Cartesian Gradient") if atgradient is not None: - result['atgradient'] = atgradient.reshape(-1, 3) - athessian = fchk.get('Cartesian Force Constants') + result["atgradient"] = atgradient.reshape(-1, 3) + athessian = fchk.get("Cartesian Force Constants") if athessian is not None: - result['athessian'] = _triangle_to_dense(athessian) + result["athessian"] = _triangle_to_dense(athessian) atfrozen = fchk.get("MicOpt") if atfrozen is not None: - result['atfrozen'] = (atfrozen == -2) - run_types = {'SP': 'energy', 'FOpt': 'opt', 'Scan': 'scan', 'Freq': 'freq'} - run_type = run_types.get(fchk['command']) + result["atfrozen"] = atfrozen == -2 + run_types = {"SP": "energy", "FOpt": "opt", "Scan": "scan", "Freq": "freq"} + run_type = run_types.get(fchk["command"]) if run_type is not None: - result['run_type'] = run_type + result["run_type"] = run_type # B) Load the orbital basis set shell_types = fchk["Shell types"] @@ -133,104 +169,113 @@ def load_one(lit: LineIterator) -> dict: for i, n in enumerate(nprims): if shell_types[i] == -1: # Special treatment for SP shell type - shells.append(Shell( - shell_map[i], - [0, 1], - ['c', 'c'], - exponents[counter:counter + n], - np.stack([ccoeffs_level1[counter:counter + n], - ccoeffs_level2[counter:counter + n]], axis=1) - )) + shells.append( + Shell( + shell_map[i], + [0, 1], + ["c", "c"], + exponents[counter : counter + n], + np.stack( + [ + ccoeffs_level1[counter : counter + n], + ccoeffs_level2[counter : counter + n], + ], + axis=1, + ), + ) + ) else: - shells.append(Shell( - shell_map[i], - [abs(shell_types[i])], - ['p' if shell_types[i] < 0 else 'c'], - exponents[counter:counter + n], - ccoeffs_level1[counter:counter + n][:, np.newaxis] - )) + shells.append( + Shell( + shell_map[i], + [abs(shell_types[i])], + ["p" if shell_types[i] < 0 else "c"], + exponents[counter : counter + n], + ccoeffs_level1[counter : counter + n][:, np.newaxis], + ) + ) counter += n del shell_map del shell_types del nprims del exponents - result['obasis'] = MolecularBasis(shells, CONVENTIONS, 'L2') + result["obasis"] = MolecularBasis(shells, CONVENTIONS, "L2") nbasis = fchk["Number of basis functions"] # C) Load density matrices one_rdms = {} - _load_dm('Total SCF Density', fchk, one_rdms, 'scf') - _load_dm('Spin SCF Density', fchk, one_rdms, 'scf_spin') + _load_dm("Total SCF Density", fchk, one_rdms, "scf") + _load_dm("Spin SCF Density", fchk, one_rdms, "scf_spin") # only one of the lots should be present, hence using the same key - for lot in 'MP2', 'MP3', 'CC', 'CI': - _load_dm('Total {} Density'.format(lot), fchk, one_rdms, 'post_scf_ao') - _load_dm('Spin {} Density'.format(lot), fchk, one_rdms, 'post_scf_spin_ao') + for lot in "MP2", "MP3", "CC", "CI": + _load_dm("Total {} Density".format(lot), fchk, one_rdms, "post_scf_ao") + _load_dm("Spin {} Density".format(lot), fchk, one_rdms, "post_scf_spin_ao") if one_rdms: - result['one_rdms'] = one_rdms + result["one_rdms"] = one_rdms # D) Load the wavefunction # Load orbitals - nalpha = fchk['Number of alpha electrons'] - nbeta = fchk['Number of beta electrons'] + nalpha = fchk["Number of alpha electrons"] + nbeta = fchk["Number of beta electrons"] if nalpha < 0 or nbeta < 0 or nalpha + nbeta <= 0: - lit.error('The number of electrons is not positive.') + lit.error("The number of electrons is not positive.") if nalpha < nbeta: - raise ValueError('n_alpha={0} < n_beta={1} is not valid!'.format(nalpha, nbeta)) + raise ValueError("n_alpha={0} < n_beta={1} is not valid!".format(nalpha, nbeta)) - norba = fchk['Alpha Orbital Energies'].shape[0] - mo_coeffs = np.copy(fchk['Alpha MO coefficients'].reshape(norba, nbasis).T) - mo_energies = np.copy(fchk['Alpha Orbital Energies']) + norba = fchk["Alpha Orbital Energies"].shape[0] + mo_coeffs = np.copy(fchk["Alpha MO coefficients"].reshape(norba, nbasis).T) + mo_energies = np.copy(fchk["Alpha Orbital Energies"]) - if 'Beta Orbital Energies' in fchk: + if "Beta Orbital Energies" in fchk: # unrestricted - norbb = fchk['Beta Orbital Energies'].shape[0] - mo_coeffs_b = np.copy(fchk['Beta MO coefficients'].reshape(norbb, nbasis).T) + norbb = fchk["Beta Orbital Energies"].shape[0] + mo_coeffs_b = np.copy(fchk["Beta MO coefficients"].reshape(norbb, nbasis).T) mo_coeffs = np.concatenate((mo_coeffs, mo_coeffs_b), axis=1) - mo_energies = np.concatenate((mo_energies, np.copy(fchk['Beta Orbital Energies'])), axis=0) + mo_energies = np.concatenate((mo_energies, np.copy(fchk["Beta Orbital Energies"])), axis=0) mo_occs = np.zeros(norba + norbb) mo_occs[:nalpha] = 1.0 - mo_occs[norba: norba + nbeta] = 1.0 - mo = MolecularOrbitals('unrestricted', norba, norbb, mo_occs, mo_coeffs, mo_energies) + mo_occs[norba : norba + nbeta] = 1.0 + mo = MolecularOrbitals("unrestricted", norba, norbb, mo_occs, mo_coeffs, mo_energies) else: # restricted closed-shell and open-shell mo_occs = np.zeros(norba) mo_occs[:nalpha] = 1.0 mo_occs[:nbeta] = 2.0 - if nalpha != nbeta and 'one_rdms' in result: + if nalpha != nbeta and "one_rdms" in result: # delete dm_full_scf because it is known to be buggy - if 'scf' in result['one_rdms']: - result['one_rdms'].pop('scf') - mo = MolecularOrbitals('restricted', norba, norba, mo_occs, mo_coeffs, mo_energies) - result['mo'] = mo + if "scf" in result["one_rdms"]: + result["one_rdms"].pop("scf") + mo = MolecularOrbitals("restricted", norba, norba, mo_occs, mo_coeffs, mo_energies) + result["mo"] = mo # E) Load properties - if 'Polarizability' in fchk: - result['extra'] = {'polarizability_tensor': _triangle_to_dense(fchk['Polarizability'])} + if "Polarizability" in fchk: + result["extra"] = {"polarizability_tensor": _triangle_to_dense(fchk["Polarizability"])} moments = {} - if 'Dipole Moment' in fchk: - moments[(1, 'c')] = fchk['Dipole Moment'] - if 'Quadrupole Moment' in fchk: + if "Dipole Moment" in fchk: + moments[(1, "c")] = fchk["Dipole Moment"] + if "Quadrupole Moment" in fchk: # Convert to alphabetical ordering: xx, xy, xz, yy, yz, zz - moments[(2, 'c')] = fchk['Quadrupole Moment'][[0, 3, 4, 1, 5, 2]] + moments[(2, "c")] = fchk["Quadrupole Moment"][[0, 3, 4, 1, 5, 2]] if moments: - result['moments'] = moments + result["moments"] = moments atcharges = {} - if 'Mulliken Charges' in fchk: - atcharges['mulliken'] = fchk['Mulliken Charges'] - if 'ESP Charges' in fchk: - atcharges['esp'] = fchk['ESP Charges'] - if 'NPA Charges' in fchk: - atcharges['npa'] = fchk['NPA Charges'] - if 'MBS Charges' in fchk: - atcharges['mbs'] = fchk['MBS Charges'] - if 'Type 6 Charges' in fchk: - atcharges['hirshfeld'] = fchk['Type 6 Charges'] - if 'Type 7 Charges' in fchk: - atcharges['cm5'] = fchk['Type 7 Charges'] + if "Mulliken Charges" in fchk: + atcharges["mulliken"] = fchk["Mulliken Charges"] + if "ESP Charges" in fchk: + atcharges["esp"] = fchk["ESP Charges"] + if "NPA Charges" in fchk: + atcharges["npa"] = fchk["NPA Charges"] + if "MBS Charges" in fchk: + atcharges["mbs"] = fchk["MBS Charges"] + if "Type 6 Charges" in fchk: + atcharges["hirshfeld"] = fchk["Type 6 Charges"] + if "Type 7 Charges" in fchk: + atcharges["cm5"] = fchk["Type 7 Charges"] if atcharges: - result['atcharges'] = atcharges + result["atcharges"] = atcharges return result @@ -251,13 +296,26 @@ def load_one(lit: LineIterator) -> dict: """ -@document_load_many("XYZ", ['atcoords', 'atgradient', 'atnums', 'atcorenums', - 'energy', 'extra', 'title'], [], {}, LOAD_MANY_NOTES) +@document_load_many( + "XYZ", + ["atcoords", "atgradient", "atnums", "atcorenums", "energy", "extra", "title"], + [], + {}, + LOAD_MANY_NOTES, +) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" - fchk = _load_fchk_low(lit, [ - "Atomic numbers", "Current cartesian coordinates", "Nuclear charges", - "IRC *", "Optimization *", "Opt point *"]) + fchk = _load_fchk_low( + lit, + [ + "Atomic numbers", + "Current cartesian coordinates", + "Nuclear charges", + "IRC *", + "Optimization *", + "Opt point *", + ], + ) # Determine the type of calculation: IRC or Optimization if "IRC Number of geometries" in fchk: @@ -272,29 +330,34 @@ def load_many(lit: LineIterator) -> Iterator[dict]: natom = fchk["Atomic numbers"].size for ipoint, nstep in enumerate(nsteps): results_geoms = fchk["{} {:7d} Results for each geome".format(prefix, ipoint + 1)] - trajectory = list(zip( - results_geoms[::2], results_geoms[1::2], - fchk["{} {:7d} Geometries".format(prefix, ipoint + 1)].reshape(-1, natom, 3), - fchk["{} {:7d} Gradient at each geome".format(prefix, ipoint + 1)].reshape(-1, natom, 3) - )) + trajectory = list( + zip( + results_geoms[::2], + results_geoms[1::2], + fchk["{} {:7d} Geometries".format(prefix, ipoint + 1)].reshape(-1, natom, 3), + fchk["{} {:7d} Gradient at each geome".format(prefix, ipoint + 1)].reshape( + -1, natom, 3 + ), + ) + ) assert len(trajectory) == nstep for istep, (energy, recor, atcoords, gradients) in enumerate(trajectory): data = { - 'title': fchk['title'], - 'atnums': fchk["Atomic numbers"], - 'atcorenums': fchk["Nuclear charges"], - 'energy': energy, - 'atcoords': atcoords, - 'atgradient': gradients, - 'extra': { - 'ipoint': ipoint, - 'npoint': len(nsteps), - 'istep': istep, - 'nstep': nstep, + "title": fchk["title"], + "atnums": fchk["Atomic numbers"], + "atcorenums": fchk["Nuclear charges"], + "energy": energy, + "atcoords": atcoords, + "atgradient": gradients, + "extra": { + "ipoint": ipoint, + "npoint": len(nsteps), + "istep": istep, + "nstep": nstep, }, } if prefix == "IRC point": - data['extra']['reaction_coordinate'] = recor + data["extra"]["reaction_coordinate"] = recor yield data @@ -316,14 +379,14 @@ def _load_fchk_low(lit: LineIterator, label_patterns: List[str] = None) -> dict: """ # Read the two-line header - result = {'title': next(lit).strip()} + result = {"title": next(lit).strip()} words = next(lit).split() if len(words) == 3: - result['command'], result['lot'], result['obasis_name'] = words + result["command"], result["lot"], result["obasis_name"] = words elif len(words) == 2: - result['command'], result['lot'] = words + result["command"], result["lot"] = words else: - lit.error('The second line of the FCHK file should contain two or three words.') + lit.error("The second line of the FCHK file should contain two or three words.") while True: try: @@ -362,14 +425,16 @@ def _load_fchk_field(lit: LineIterator, label_patterns: List[str]) -> Tuple[str, words = line[43:].split() if not words: continue - if words[0] == 'I': + if words[0] == "I": datatype = int - elif words[0] == 'R': + elif words[0] == "R": datatype = float else: continue - if not (label_patterns is None - or any(fnmatch(label, label_pattern) for label_pattern in label_patterns)): + if not ( + label_patterns is None + or any(fnmatch(label, label_pattern) for label_pattern in label_patterns) + ): continue if len(words) == 2: try: @@ -390,7 +455,7 @@ def _load_fchk_field(lit: LineIterator, label_patterns: List[str]) -> Tuple[str, try: value[counter] = datatype(word) except (ValueError, OverflowError): - lit.error('Could not interpret: {}'.format(word)) + lit.error("Could not interpret: {}".format(word)) counter += 1 return label, value @@ -435,8 +500,8 @@ def _triangle_to_dense(triangle: np.ndarray) -> np.ndarray: begin = 0 for irow in range(nrow): end = begin + irow + 1 - result[irow, :irow + 1] = triangle[begin:end] - result[:irow + 1, irow] = triangle[begin:end] + result[irow, : irow + 1] = triangle[begin:end] + result[: irow + 1, irow] = triangle[begin:end] begin = end return result @@ -461,7 +526,7 @@ def _dump_integer_arrays(name: str, val: np.ndarray, f: TextIO): print("{0:40} I N={1:12}".format(name, nval), file=f) k = 0 for i in range(nval): - print("{0:12}".format(int(val[i])), file=f, end='') + print("{0:12}".format(int(val[i])), file=f, end="") k += 1 if k == 6 or i == nval - 1: print("", file=f) @@ -476,7 +541,7 @@ def _dump_real_arrays(name: str, val: np.ndarray, f: TextIO): print("{0:40} R N={1:12}".format(name, nval), file=f) k = 0 for i in range(nval): - print("{0: 16.8E}".format(val[i]), file=f, end='') + print("{0: 16.8E}".format(val[i]), file=f, end="") k += 1 if k == 5 or i == nval - 1: print("", file=f) @@ -485,10 +550,24 @@ def _dump_real_arrays(name: str, val: np.ndarray, f: TextIO): @document_dump_one( "Gaussian Formatted Checkpoint", - ['atnums', 'atcorenums'], - ['atcharges', 'atcoords', 'atfrozen', 'atgradient', 'athessian', 'atmasses', - 'charge', 'energy', 'lot', 'mo', 'one_rdms', 'obasis_name', - 'extra', 'moments']) + ["atnums", "atcorenums"], + [ + "atcharges", + "atcoords", + "atfrozen", + "atgradient", + "athessian", + "atmasses", + "charge", + "energy", + "lot", + "mo", + "one_rdms", + "obasis_name", + "extra", + "moments", + ], +) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # write title @@ -536,7 +615,6 @@ def dump_one(f: TextIO, data: IOData): # write molecular orbital basis set if data.obasis is not None: - # number of primitives per shell nprims = np.array([shell.nprim for shell in data.obasis.shells]) exponents = np.array([item for shell in data.obasis.shells for item in shell.exponents]) @@ -547,9 +625,9 @@ def dump_one(f: TextIO, data: IOData): # get list of shell types: 0=s, 1=p, -1=sp, 2=6d, -2=5d, 3=10f, -3=7f... shell_types = [] for shell in data.obasis.shells: - if shell.ncon == 1 and shell.kinds == ['c']: + if shell.ncon == 1 and shell.kinds == ["c"]: shell_types.append(shell.angmoms[0]) - elif shell.ncon == 1 and shell.kinds == ['p']: + elif shell.ncon == 1 and shell.kinds == ["p"]: shell_types.append(-1 * shell.angmoms[0]) elif shell.ncon == 2 and shell.angmoms == [0, 1]: shell_types.append(-1) @@ -577,7 +655,7 @@ def dump_one(f: TextIO, data: IOData): if -1 in shell_types: sp_coeffs = [] - for (shell, shell_type) in zip(data.obasis.shells, shell_types): + for shell, shell_type in zip(data.obasis.shells, shell_types): if shell_type == -1: sp_coeffs.extend([shell.coeffs[i][1] for i in range(shell.nprim)]) else: @@ -604,8 +682,8 @@ def dump_one(f: TextIO, data: IOData): # write reduced density matrix, if available # get level of theory, use 'NA' if not available - level = data.lot.upper() if data.lot is not None else 'NA' - for item in ['MP2', 'MP3', 'CC', 'CI']: + level = data.lot.upper() if data.lot is not None else "NA" + for item in ["MP2", "MP3", "CC", "CI"]: if item in level: level = item for key, arr in data.one_rdms.items(): @@ -626,17 +704,17 @@ def dump_one(f: TextIO, data: IOData): _dump_real_arrays(title, mat, f) # write atomic charges - if 'mulliken' in data.atcharges: + if "mulliken" in data.atcharges: _dump_real_arrays("Mulliken Charges", data.atcharges["mulliken"], f) - if 'esp' in data.atcharges: + if "esp" in data.atcharges: _dump_real_arrays("ESP Charges", data.atcharges["esp"], f) - if 'npa' in data.atcharges: + if "npa" in data.atcharges: _dump_real_arrays("NPA Charges", data.atcharges["npa"], f) - if 'mbs' in data.atcharges: + if "mbs" in data.atcharges: _dump_real_arrays("MBS Charges", data.atcharges["mbs"], f) - if 'hirshfeld' in data.atcharges: + if "hirshfeld" in data.atcharges: _dump_real_arrays("Type 6 Charges", data.atcharges["hirshfeld"], f) - if 'cm5' in data.atcharges: + if "cm5" in data.atcharges: _dump_real_arrays("Type 7 Charges", data.atcharges["cm5"], f) # write atomic gradient @@ -649,16 +727,16 @@ def dump_one(f: TextIO, data: IOData): _dump_real_arrays("Cartesian Force Constants", arr, f) # write moments - if (1, 'c') in data.moments: - _dump_real_arrays("Dipole Moment", data.moments[(1, 'c')], f) - if (2, 'c') in data.moments and len(data.moments[(2, 'c')]) != 0: + if (1, "c") in data.moments: + _dump_real_arrays("Dipole Moment", data.moments[(1, "c")], f) + if (2, "c") in data.moments and len(data.moments[(2, "c")]) != 0: # quadrupole moments are stored as XX, XY, XZ, YY, YZ, ZZ in IOData, so they need to # be permuted to have XX, YY, ZZ, XY, XZ, YZ order for FCHK. - quadrupole = data.moments[(2, 'c')][[0, 3, 5, 1, 2, 4]] + quadrupole = data.moments[(2, "c")][[0, 3, 5, 1, 2, 4]] _dump_real_arrays("Quadrupole Moment", quadrupole, f) # write polarizability tensor - if 'polarizability_tensor' in data.extra: + if "polarizability_tensor" in data.extra: arr = data.extra["polarizability_tensor"] arr = arr[np.tril_indices(arr.shape[0])] _dump_real_arrays("Polarizability", arr, f) diff --git a/iodata/formats/fcidump.py b/iodata/formats/fcidump.py index 6817d804..28d1441f 100644 --- a/iodata/formats/fcidump.py +++ b/iodata/formats/fcidump.py @@ -28,7 +28,6 @@ """ - from typing import TextIO import numpy as np @@ -41,28 +40,29 @@ __all__ = [] -PATTERNS = ['*FCIDUMP*'] +PATTERNS = ["*FCIDUMP*"] -@document_load_one("Molpro 2012 FCIDUMP", ['core_energy', 'one_ints', 'nelec', 'spinpol', - 'two_ints']) +@document_load_one( + "Molpro 2012 FCIDUMP", ["core_energy", "one_ints", "nelec", "spinpol", "two_ints"] +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # check header line = next(lit) - if not line.startswith(' &FCI NORB='): - lit.error('Incorrect file header') + if not line.startswith(" &FCI NORB="): + lit.error("Incorrect file header") # read info from header - words = line[5:].split(',') + words = line[5:].split(",") header_info = {} for word in words: - if word.count('=') == 1: - key, value = word.split('=') + if word.count("=") == 1: + key, value = word.split("=") header_info[key.strip()] = value.strip() - nbasis = int(header_info['NORB']) - nelec = int(header_info['NELEC']) - spinpol = int(header_info['MS2']) + nbasis = int(header_info["NORB"]) + nelec = int(header_info["NELEC"]) + spinpol = int(header_info["MS2"]) # skip rest of header for line in lit: @@ -78,9 +78,9 @@ def load_one(lit: LineIterator) -> dict: for line in lit: words = line.split() if len(words) != 5: - lit.error('Expecting 5 fields on each data line in FCIDUMP') + lit.error("Expecting 5 fields on each data line in FCIDUMP") value = float(words[0]) - if words[3] != '0': + if words[3] != "0": ii = int(words[1]) - 1 ij = int(words[2]) - 1 ik = int(words[3]) - 1 @@ -89,7 +89,7 @@ def load_one(lit: LineIterator) -> dict: # FCIDUMP file does not contain duplicate 4-index entries. # assert two_mo.get_element(ii,ik,ij,il) == 0.0 set_four_index_element(two_mo, ii, ik, ij, il, value) - elif words[1] != '0': + elif words[1] != "0": ii = int(words[1]) - 1 ij = int(words[2]) - 1 one_mo[ii, ij] = value @@ -98,11 +98,11 @@ def load_one(lit: LineIterator) -> dict: core_energy = value return { - 'nelec': nelec, - 'spinpol': spinpol, - 'one_ints': {'core_mo': one_mo}, - 'two_ints': {'two_mo': two_mo}, - 'core_energy': core_energy, + "nelec": nelec, + "spinpol": spinpol, + "one_ints": {"core_mo": one_mo}, + "two_ints": {"two_mo": two_mo}, + "core_energy": core_energy, } @@ -112,23 +112,28 @@ def load_one(lit: LineIterator) -> dict: """ -@document_dump_one("Molpro 2012 FCIDUMP", ['one_ints', 'two_ints'], - ['core_energy', 'nelec', 'spinpol'], {}, LOAD_ONE_NOTES) +@document_dump_one( + "Molpro 2012 FCIDUMP", + ["one_ints", "two_ints"], + ["core_energy", "nelec", "spinpol"], + {}, + LOAD_ONE_NOTES, +) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" - one_mo = data.one_ints['core_mo'] + one_mo = data.one_ints["core_mo"] # Write header nactive = one_mo.shape[0] nelec = data.nelec or 0 spinpol = data.spinpol or 0 - print(f' &FCI NORB={nactive:d},NELEC={nelec:d},MS2={spinpol:d},', file=f) + print(f" &FCI NORB={nactive:d},NELEC={nelec:d},MS2={spinpol:d},", file=f) print(f" ORBSYM= {','.join('1' for v in range(nactive))},", file=f) - print(' ISYM=1', file=f) - print(' &END', file=f) + print(" ISYM=1", file=f) + print(" &END", file=f) # Write integrals and core energy - two_mo = data.two_ints['two_mo'] + two_mo = data.two_ints["two_mo"] for i in range(nactive): # pylint: disable=too-many-nested-blocks for j in range(i + 1): for k in range(nactive): @@ -136,11 +141,11 @@ def dump_one(f: TextIO, data: IOData): if (i * (i + 1)) / 2 + j >= (k * (k + 1)) / 2 + l: value = two_mo[i, k, j, l] if value != 0.0: - print(f'{value:23.16e} {i+1:4d} {j+1:4d} {k+1:4d} {l+1:4d}', file=f) + print(f"{value:23.16e} {i+1:4d} {j+1:4d} {k+1:4d} {l+1:4d}", file=f) for i in range(nactive): for j in range(i + 1): value = one_mo[i, j] if value != 0.0: - print(f'{value:23.16e} {i+1:4d} {j+1:4d} {0:4d} {0:4d}', file=f) + print(f"{value:23.16e} {i+1:4d} {j+1:4d} {0:4d} {0:4d}", file=f) if data.core_energy is not None: - print(f'{data.core_energy:23.16e} {0:4d} {0:4d} {0:4d} {0:4d}', file=f) + print(f"{data.core_energy:23.16e} {0:4d} {0:4d} {0:4d} {0:4d}", file=f) diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index 627fb411..da241608 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -18,7 +18,6 @@ # -- """GAMESS punch file format.""" - import numpy as np from ..docstrings import document_load_one @@ -28,7 +27,7 @@ __all__ = [] -PATTERNS = ['*.dat'] +PATTERNS = ["*.dat"] def _read_data(lit: LineIterator) -> tuple: @@ -99,7 +98,7 @@ def _read_hessian(lit: LineIterator, result: dict) -> np.ndarray: break line = line[5:-1] for j in range(len(line) // 15): - tmp[counter] = float(line[j * 15:(j + 1) * 15]) + tmp[counter] = float(line[j * 15 : (j + 1) * 15]) counter += 1 return hessian @@ -117,8 +116,10 @@ def _read_masses(lit: LineIterator, result: dict) -> np.ndarray: return masses -@document_load_one("PUNCH", ['title', 'energy', 'grot', 'atgradient', 'athessian', 'atmasses', - 'atnums', 'atcoords']) +@document_load_one( + "PUNCH", + ["title", "energy", "grot", "atgradient", "athessian", "atmasses", "atnums", "atcoords"], +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" result = {} diff --git a/iodata/formats/gaussianinput.py b/iodata/formats/gaussianinput.py index c1c2c382..1ce6622e 100644 --- a/iodata/formats/gaussianinput.py +++ b/iodata/formats/gaussianinput.py @@ -31,31 +31,31 @@ PATTERNS = ["*.com", "*.gjf"] -@document_load_one("Gaussian Input File", ['atcoords', 'atnums', 'title'], []) +@document_load_one("Gaussian Input File", ["atcoords", "atnums", "title"], []) def load_one(lit: LineIterator): """Do not edit this docstring. It will be overwritten.""" line = next(lit) # check multiple-link 0 section starts with '%' - while line.startswith(r'%'): + while line.startswith(r"%"): line = next(lit) # check multiple-line route section data = {} - route_line = '' + route_line = "" while line.strip(): - route_line += (' ' + line.strip()) + route_line += " " + line.strip() line = next(lit) route_line = route_line[1:] line = next(lit) - title_line = '' + title_line = "" while line.strip(): - title_line += (' ' + line.strip()) + title_line += " " + line.strip() line = next(lit) title_line = title_line[1:] - data['title'] = title_line + data["title"] = title_line # charge_spin_mult_line _ = next(lit) @@ -74,7 +74,7 @@ def load_one(lit: LineIterator): coor = list(map(float, contents[1:])) coordinates.append(coor) coord_line = next(lit) - data['atnums'] = np.array(numbers) - data['atcoords'] = np.array(coordinates) * angstrom + data["atnums"] = np.array(numbers) + data["atcoords"] = np.array(coordinates) * angstrom return data diff --git a/iodata/formats/gaussianlog.py b/iodata/formats/gaussianlog.py index a2b3b155..ec1106e8 100644 --- a/iodata/formats/gaussianlog.py +++ b/iodata/formats/gaussianlog.py @@ -27,7 +27,6 @@ """ - import numpy as np from ..docstrings import document_load_one @@ -37,15 +36,15 @@ __all__ = [] -PATTERNS = ['*.log'] +PATTERNS = ["*.log"] -@document_load_one("Gaussian Log", [], ['one_ints', 'two_ints']) +@document_load_one("Gaussian Log", [], ["one_ints", "two_ints"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # First get the line with the number of orbital basis functions for line in lit: - if line.startswith(' NBasis ='): + if line.startswith(" NBasis ="): nbasis = int(line[12:18]) break @@ -58,20 +57,20 @@ def load_one(lit: LineIterator) -> dict: line = next(lit) if line.startswith(" Normal termination of Gaussian"): break - if line.startswith(' *** Overlap ***'): - one_ints['olp'] = _load_twoindex_g09(lit, nbasis) - elif line.startswith(' *** Kinetic Energy ***'): - one_ints['kin_ao'] = _load_twoindex_g09(lit, nbasis) - elif line.startswith(' ***** Potential Energy *****'): - one_ints['na_ao'] = _load_twoindex_g09(lit, nbasis) - elif line.startswith(' *** Dumping Two-Electron integrals ***'): - two_ints['er_ao'] = _load_fourindex_g09(lit, nbasis) + if line.startswith(" *** Overlap ***"): + one_ints["olp"] = _load_twoindex_g09(lit, nbasis) + elif line.startswith(" *** Kinetic Energy ***"): + one_ints["kin_ao"] = _load_twoindex_g09(lit, nbasis) + elif line.startswith(" ***** Potential Energy *****"): + one_ints["na_ao"] = _load_twoindex_g09(lit, nbasis) + elif line.startswith(" *** Dumping Two-Electron integrals ***"): + two_ints["er_ao"] = _load_fourindex_g09(lit, nbasis) result = {} if one_ints: - result['one_ints'] = one_ints + result["one_ints"] = one_ints if two_ints: - result['two_ints'] = two_ints + result["two_ints"] = two_ints return result @@ -101,7 +100,7 @@ def _load_twoindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: for i in range(nrow): words = next(lit).split()[1:] for j, word in enumerate(words): - value = float(word.replace('D', 'E')) + value = float(word.replace("D", "E")) result[i + block_counter, j + block_counter] = value result[j + block_counter, i + block_counter] = value block_counter += 5 @@ -131,14 +130,14 @@ def _load_fourindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: # Start reading elements until a line is encountered that does not start # with ' I=' for line in lit: - if not line.startswith(' I='): + if not line.startswith(" I="): break # print line[3:7], line[9:13], line[15:19], line[21:25], line[28:].replace('D', 'E') i = int(line[3:7]) - 1 j = int(line[9:13]) - 1 k = int(line[15:19]) - 1 l = int(line[21:25]) - 1 - value = float(line[29:].replace('D', 'E')) + value = float(line[29:].replace("D", "E")) # Gaussian uses the chemists notation for the 4-center indexes. IOdata # uses the physicists notation. set_four_index_element(result, i, k, j, l, value) diff --git a/iodata/formats/gromacs.py b/iodata/formats/gromacs.py index 37e1c0e9..836a6708 100644 --- a/iodata/formats/gromacs.py +++ b/iodata/formats/gromacs.py @@ -25,22 +25,21 @@ """ - from typing import Tuple, Iterator import numpy as np -from ..docstrings import (document_load_one, document_load_many) +from ..docstrings import document_load_one, document_load_many from ..utils import nanometer, picosecond, LineIterator __all__ = [] -PATTERNS = ['*.gro'] +PATTERNS = ["*.gro"] -@document_load_one('GRO', ['atcoords', 'atffparams', 'cellvecs', 'extra', 'title']) +@document_load_one("GRO", ["atcoords", "atffparams", "cellvecs", "extra", "title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" while True: @@ -56,27 +55,20 @@ def load_one(lit: LineIterator) -> dict: atcoords = data[5] velocities = data[6] cellvecs = data[7] - atffparams = { - 'attypes': attypes, - 'resnames': resnames, - 'resnums': resnums - } - extra = { - 'time': time, - 'velocities': velocities - } + atffparams = {"attypes": attypes, "resnames": resnames, "resnums": resnums} + extra = {"time": time, "velocities": velocities} result = { - 'atcoords': atcoords, - 'atffparams': atffparams, - 'cellvecs': cellvecs, - 'extra': extra, - 'title': title, + "atcoords": atcoords, + "atffparams": atffparams, + "cellvecs": cellvecs, + "extra": extra, + "title": title, } return result - lit.error('Gromacs gro file could not be read.') + lit.error("Gromacs gro file could not be read.") -@document_load_many('GRO', ['atcoords', 'atffparams', 'cellvecs', 'extra', 'title']) +@document_load_many("GRO", ["atcoords", "atffparams", "cellvecs", "extra", "title"]) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # gro files can be used as trajectory by simply concatenating files, @@ -93,10 +85,10 @@ def _helper_read_frame(lit: LineIterator) -> Tuple: # Read the first line, get the title and try to get the time. # Time field is optional. line = next(lit) - title = line.split(',')[0] if 't=' in line else line[:-1] + title = line.split(",")[0] if "t=" in line else line[:-1] time = 0.0 - if 't=' in line: - time = float(line.split('t=')[1]) * picosecond + if "t=" in line: + time = float(line.split("t=")[1]) * picosecond # Read the second line for number of atoms. natoms = int(next(lit)) # Read the atom lines diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 18ea7026..caa0484e 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -564,7 +564,6 @@ """ - import json from typing import List, TextIO, Union from warnings import warn @@ -587,7 +586,8 @@ @document_load_one( "QCSchema", ["atnums", "atcorenums", "atcoords", "charge", "nelec", "spinpol"], - ["atmasses", "bonds", "energy", "g_rot", "lot", "obasis", "obasis_name", "title", "extra"]) + ["atmasses", "bonds", "energy", "g_rot", "lot", "obasis", "obasis_name", "title", "extra"], +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # Use python standard lib json module to read the file to a dict @@ -1117,7 +1117,11 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: if "keywords" in result: keywords_dict = result["keywords"] if "run_type" in keywords_dict and keywords_dict["run_type"].lower() in { - "energy", "energy_force", "opt", "scan", "freq" + "energy", + "energy_force", + "opt", + "scan", + "freq", }: input_dict["run_type"] = keywords_dict["run_type"] extra_dict["keywords"] = keywords_dict @@ -1184,9 +1188,7 @@ def _parse_driver(driver: str, lit: LineIterator) -> str: if driver not in ["energy", "gradient", "hessian", "properties"]: raise FileFormatError( "{}: QCSchema driver must be one of `energy`, `gradient`, `hessian`, " - "or `properties`".format( - lit.filename - ) + "or `properties`".format(lit.filename) ) return driver @@ -1261,17 +1263,13 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: warn( "{}: Protocols `wavefunction` key not specified, no properties will be kept.", FileFormatWarning, - 2 + 2, ) wavefunction = "none" else: wavefunction = protocols["wavefunction"] if "stdout" not in protocols: - warn( - "{}: Protocols `stdout` key not specified, stdout will be kept.", - FileFormatWarning, - 2 - ) + warn("{}: Protocols `stdout` key not specified, stdout will be kept.", FileFormatWarning, 2) keep_stdout = True else: keep_stdout = protocols["stdout"] @@ -1397,7 +1395,7 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: "provenance", "properties", "success", - "return_result" + "return_result", } passthrough_dict = _find_passthrough_dict(result, output_keys) if passthrough_dict: @@ -1454,7 +1452,8 @@ def _parse_provenance( @document_dump_one( "QCSchema", ["atnums", "atcoords", "charge", "spinpol"], - ["title", "atcorenums", "atmasses", "bonds", "g_rot", "extra"]) + ["title", "atcorenums", "atmasses", "bonds", "g_rot", "extra"], +) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" if "schema_name" not in data.extra: @@ -1546,11 +1545,13 @@ def _dump_qcschema_molecule(data: IOData) -> dict: fragment.tolist() for fragment in data.extra["molecule"]["fragments"]["indices"] ] if "indices" in data.extra["molecule"]["fragments"]: - molecule_dict["fragment_charges"] = \ - data.extra["molecule"]["fragments"]["charges"].tolist() + molecule_dict["fragment_charges"] = data.extra["molecule"]["fragments"][ + "charges" + ].tolist() if "indices" in data.extra["molecule"]["fragments"]: - molecule_dict["fragment_multiplicities"] = \ - data.extra["molecule"]["fragments"]["multiplicities"].tolist() + molecule_dict["fragment_multiplicities"] = data.extra["molecule"]["fragments"][ + "multiplicities" + ].tolist() if "fix_com" in data.extra["molecule"]: molecule_dict["fix_com"] = data.extra["molecule"]["fix_com"] if "fix_orientation" in data.extra["molecule"]: diff --git a/iodata/formats/locpot.py b/iodata/formats/locpot.py index 0708969e..df9a74f7 100644 --- a/iodata/formats/locpot.py +++ b/iodata/formats/locpot.py @@ -25,7 +25,6 @@ different conversions to atomic units. """ - from ..docstrings import document_load_one from ..utils import electronvolt, LineIterator from .chgcar import _load_vasp_grid @@ -34,13 +33,13 @@ __all__ = [] -PATTERNS = ['LOCPOT*'] +PATTERNS = ["LOCPOT*"] -@document_load_one("VASP 5 LOCPOT", ['atcoords', 'atnums', 'cellvecs', 'cube', 'title']) +@document_load_one("VASP 5 LOCPOT", ["atcoords", "atnums", "cellvecs", "cube", "title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" result = _load_vasp_grid(lit) # convert locpot to atomic units - result['cube'].data[:] *= electronvolt + result["cube"].data[:] *= electronvolt return result diff --git a/iodata/formats/mol2.py b/iodata/formats/mol2.py index 9fdd8f1d..6d8b02e6 100644 --- a/iodata/formats/mol2.py +++ b/iodata/formats/mol2.py @@ -22,13 +22,16 @@ was the main objective to write out files with atomic charges used by antechamber. """ - from typing import TextIO, Iterator, Tuple import numpy as np -from ..docstrings import (document_load_one, document_load_many, document_dump_one, - document_dump_many) +from ..docstrings import ( + document_load_one, + document_load_many, + document_dump_one, + document_dump_many, +) from ..iodata import IOData from ..periodic import sym2num, num2sym, bond2num, num2bond from ..utils import angstrom, LineIterator @@ -37,10 +40,10 @@ __all__ = [] -PATTERNS = ['*.mol2'] +PATTERNS = ["*.mol2"] -@document_load_one("MOL2", ['atcoords', 'atnums', 'atcharges', 'atffparams'], ['title']) +@document_load_one("MOL2", ["atcoords", "atnums", "atcharges", "atffparams"], ["title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" molecule_found = False @@ -65,23 +68,24 @@ def load_one(lit: LineIterator) -> dict: atcharges = {"mol2charges": atchgs} atffparams = {"attypes": attypes} result = { - 'atcoords': atcoords, - 'atnums': atnums, - 'atcharges': atcharges, - 'atffparams': atffparams, - 'title': title + "atcoords": atcoords, + "atnums": atnums, + "atcharges": atcharges, + "atffparams": atffparams, + "title": title, } molecule_found = True if words[0] == "@BOND": bonds = _load_helper_bonds(lit, nbonds) - result['bonds'] = bonds + result["bonds"] = bonds if not molecule_found: raise lit.error("Molecule could not be read") return result -def _load_helper_atoms(lit: LineIterator, natoms: int)\ - -> Tuple[np.ndarray, np.ndarray, np.ndarray, tuple]: +def _load_helper_atoms( + lit: LineIterator, natoms: int +) -> Tuple[np.ndarray, np.ndarray, np.ndarray, tuple]: """Load element numbers, coordinates and atomic charges.""" atnums = np.empty(natoms) atcoords = np.empty((natoms, 3)) @@ -95,7 +99,7 @@ def _load_helper_atoms(lit: LineIterator, natoms: int)\ atnum = sym2num.get(symbol, sym2num.get(symbol[0], None)) if atnum is None: atnum = 0 - lit.warn(f'Can not convert {words[1][:2]} to elements') + lit.warn(f"Can not convert {words[1][:2]} to elements") atnums[i] = atnum attypes.append(words[5]) atcoords[i] = [float(words[2]), float(words[3]), float(words[4])] @@ -126,16 +130,16 @@ def _load_helper_bonds(lit: LineIterator, nbonds: int) -> Tuple[np.ndarray]: int(words[1]) - 1, int(words[2]) - 1, # convert mol2 bond type to integer - bond2num.get(words[3], bond2num["un"]) + bond2num.get(words[3], bond2num["un"]), ] if bond is None: bond = [0, 0, 0] - lit.warn(f'Something wrong in the bond section: {bond}') + lit.warn(f"Something wrong in the bond section: {bond}") bonds[i] = bond return bonds -@document_load_many("MOL2", ['atcoords', 'atnums', 'atcharges', 'atffparams'], ['title']) +@document_load_many("MOL2", ["atcoords", "atnums", "atcharges", "atffparams"], ["title"]) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # MOL2 files with more molecules are a simple concatenation of individual MOL2 files,' @@ -147,39 +151,38 @@ def load_many(lit: LineIterator) -> Iterator[dict]: return -@document_dump_one("MOL2", ['atcoords', 'atnums'], ['atcharges', 'atffparams', 'title']) +@document_dump_one("MOL2", ["atcoords", "atnums"], ["atcharges", "atffparams", "title"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # The first six lines are reserved for comments print("# Mol2 file created with Iodata", file=f) print("\n\n\n\n\n", file=f) print("@MOLECULE", file=f) - print(data.title or 'Created with IOData', file=f) + print(data.title or "Created with IOData", file=f) if data.bonds is not None: bonds = len(data.bonds) - print(f'{data.natom:5d} {bonds:6d} {0:6d} {0:6d}', file=f) + print(f"{data.natom:5d} {bonds:6d} {0:6d} {0:6d}", file=f) else: - print(f'{data.natom:5d} {0:6d} {0:6d} {0:6d}', file=f) + print(f"{data.natom:5d} {0:6d} {0:6d} {0:6d}", file=f) print("@ATOM", file=f) - atcharges = data.atcharges.get('mol2charges') - attypes = data.atffparams.get('attypes') + atcharges = data.atcharges.get("mol2charges") + attypes = data.atffparams.get("attypes") for i in range(data.natom): n = num2sym[data.atnums[i]] x, y, z = data.atcoords[i] / angstrom - out1 = f'{i+1:7d} {n:2s} {x:15.4f} {y:9.4f} {z:9.4f} ' + out1 = f"{i+1:7d} {n:2s} {x:15.4f} {y:9.4f} {z:9.4f} " atcharge = 0.0 if atcharges is None else atcharges[i] attype = n if attypes is None else attypes[i] - out2 = f'{attype:6s} {1:4d} XXX {atcharge:14.4f}' + out2 = f"{attype:6s} {1:4d} XXX {atcharge:14.4f}" print(out1 + out2, file=f) if data.bonds is not None: print("@BOND", file=f) for i, bond in enumerate(data.bonds): bondtype = num2bond.get(bond[2], "un") - print(f'{i+1:6d} {bond[0]+1:4d} {bond[1]+1:4d} {bondtype:2s}', - file=f) + print(f"{i+1:6d} {bond[0]+1:4d} {bond[1]+1:4d} {bondtype:2s}", file=f) -@document_dump_many("MOL2", ['atcoords', 'atnums', 'atcharges'], ['title']) +@document_dump_many("MOL2", ["atcoords", "atnums", "atcharges"], ["title"]) def dump_many(f: TextIO, datas: Iterator[IOData]): """Do not edit this docstring. It will be overwritten.""" # Similar to load_many, this is relatively easy. diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index bd16e963..af19f8aa 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -25,15 +25,20 @@ errors are corrected when loading them with IOData. """ - from typing import Tuple, Union, TextIO import copy import attr import numpy as np -from ..basis import (angmom_its, angmom_sti, MolecularBasis, Shell, - convert_conventions, HORTON2_CONVENTIONS) +from ..basis import ( + angmom_its, + angmom_sti, + MolecularBasis, + Shell, + convert_conventions, + HORTON2_CONVENTIONS, +) from ..docstrings import document_load_one, document_dump_one from ..iodata import IOData from ..periodic import sym2num, num2sym @@ -45,7 +50,7 @@ __all__ = [] -PATTERNS = ['*.molden.input', '*.molden'] +PATTERNS = ["*.molden.input", "*.molden"] # From the Molden format documentation: # 5D: D 0, D+1, D-1, D+2, D-2 @@ -59,28 +64,45 @@ # xxyy xxzz yyzz xxyz yyxz zzxy CONVENTIONS = { - (0, 'c'): ['1'], - (1, 'c'): ['x', 'y', 'z'], - (2, 'p'): HORTON2_CONVENTIONS[(2, 'p')], - (2, 'c'): ['xx', 'yy', 'zz', 'xy', 'xz', 'yz'], - (3, 'p'): HORTON2_CONVENTIONS[(3, 'p')], - (3, 'c'): ['xxx', 'yyy', 'zzz', 'xyy', 'xxy', 'xxz', 'xzz', 'yzz', 'yyz', 'xyz'], - (4, 'p'): HORTON2_CONVENTIONS[(4, 'p')], - (4, 'c'): ['xxxx', 'yyyy', 'zzzz', 'xxxy', 'xxxz', 'xyyy', 'yyyz', 'xzzz', - 'yzzz', 'xxyy', 'xxzz', 'yyzz', 'xxyz', 'xyyz', 'xyzz'], + (0, "c"): ["1"], + (1, "c"): ["x", "y", "z"], + (2, "p"): HORTON2_CONVENTIONS[(2, "p")], + (2, "c"): ["xx", "yy", "zz", "xy", "xz", "yz"], + (3, "p"): HORTON2_CONVENTIONS[(3, "p")], + (3, "c"): ["xxx", "yyy", "zzz", "xyy", "xxy", "xxz", "xzz", "yzz", "yyz", "xyz"], + (4, "p"): HORTON2_CONVENTIONS[(4, "p")], + (4, "c"): [ + "xxxx", + "yyyy", + "zzzz", + "xxxy", + "xxxz", + "xyyy", + "yyyz", + "xzzz", + "yzzz", + "xxyy", + "xxzz", + "yyzz", + "xxyz", + "xyyz", + "xyzz", + ], # H fubnctions are not officially supported by the Molden format but PSI4 # and ORCA write out such files anyway. - (5, 'p'): HORTON2_CONVENTIONS[(5, 'p')], + (5, "p"): HORTON2_CONVENTIONS[(5, "p")], } @document_load_one( "Molden", - ['atcoords', 'atnums', 'atcorenums', 'mo', 'obasis'], - ['title'], - {"norm_threshold": "When the normalization of one of the orbitals exceeds " - "norm_threshold, a correction is attempted or an error " - "is raised when no suitable correction can be found."} + ["atcoords", "atnums", "atcorenums", "mo", "obasis"], + ["title"], + { + "norm_threshold": "When the normalization of one of the orbitals exceeds " + "norm_threshold, a correction is attempted or an error " + "is raised when no suitable correction can be found." + }, ) def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: """Do not edit this docstring. It will be overwritten.""" @@ -119,8 +141,8 @@ def _load_low(lit: LineIterator) -> dict: title = None line = next(lit) - if line.strip() != '[Molden Format]': - lit.error('Molden header not found') + if line.strip() != "[Molden Format]": + lit.error("Molden header not found") # The order of sections, denoted by "[...]", is not fixed in the Molden # format, so we need a loop that checks for all possible sections at # each iteration. If needed, the contents of the section is read. @@ -133,36 +155,36 @@ def _load_low(lit: LineIterator) -> dict: # than reaching the end of the file. break # settings for pure or Cartesian shells. - if line.startswith('[5d]') or line.startswith('[5d7f]'): + if line.startswith("[5d]") or line.startswith("[5d7f]"): pure_angmoms.add(2) pure_angmoms.add(3) - elif line.lower().startswith('[7f]'): + elif line.lower().startswith("[7f]"): pure_angmoms.add(3) - elif line.lower().startswith('[5d10f]'): + elif line.lower().startswith("[5d10f]"): pure_angmoms.add(2) - elif line.lower().startswith('[9g]'): + elif line.lower().startswith("[9g]"): pure_angmoms.add(4) # H functions are not part of the Molden standard but the # following line is compatible with files containing H functions # writen by PSI4 and ORCA. pure_angmoms.add(5) # title - elif line == '[title]': + elif line == "[title]": title = next(lit).strip() # atoms - elif line.startswith('[atoms]'): - if 'au' in line: + elif line.startswith("[atoms]"): + if "au" in line: cunit = 1.0 - elif 'angs' in line: + elif "angs" in line: cunit = angstrom atnums, atcorenums, atcoords = _load_helper_atoms(lit, cunit) # we only support Gaussian-type orbitals (gto's) - elif line == '[gto]': + elif line == "[gto]": obasis = _load_helper_obasis(lit) - elif line == '[sto]': - lit.error('Slater-type orbitals are not supported by IODATA.') + elif line == "[sto]": + lit.error("Slater-type orbitals are not supported by IODATA.") # molecular-orbital coefficients. - elif line == '[mo]': + elif line == "[mo]": data_alpha, data_beta = _load_helper_coeffs(lit) occsa, coeffsa, energiesa, irrepsa = data_alpha occsb, coeffsb, energiesb, irrepsb = data_beta @@ -172,38 +194,42 @@ def _load_low(lit: LineIterator) -> dict: for shell in obasis.shells: # Code only has to work for segmented contractions if shell.angmoms[0] in pure_angmoms: - shell.kinds[0] = 'p' + shell.kinds[0] = "p" if coeffsb is None: if coeffsa.shape[0] != obasis.nbasis: lit.error("Number of alpha orbital coefficients does not match the size of the basis.") mo = MolecularOrbitals( - 'restricted', coeffsa.shape[1], coeffsa.shape[1], - occsa, coeffsa, energiesa, irrepsa) + "restricted", coeffsa.shape[1], coeffsa.shape[1], occsa, coeffsa, energiesa, irrepsa + ) else: if coeffsb.shape[0] != obasis.nbasis: lit.error("Number of beta orbital coefficients does not match the size of the basis.") mo = MolecularOrbitals( - 'unrestricted', coeffsa.shape[1], coeffsb.shape[1], + "unrestricted", + coeffsa.shape[1], + coeffsb.shape[1], np.concatenate((occsa, occsb), axis=0), np.concatenate((coeffsa, coeffsb), axis=1), np.concatenate((energiesa, energiesb), axis=0), - irrepsa + irrepsb) + irrepsa + irrepsb, + ) result = { - 'atcoords': atcoords, - 'atnums': atnums, - 'obasis': obasis, - 'mo': mo, - 'atcorenums': atcorenums, + "atcoords": atcoords, + "atnums": atnums, + "obasis": obasis, + "mo": mo, + "atcorenums": atcorenums, } if title is not None: - result['title'] = title + result["title"] = title return result -def _load_helper_atoms(lit: LineIterator, cunit: float) -> \ - Tuple[np.ndarray, np.ndarray, np.ndarray]: +def _load_helper_atoms( + lit: LineIterator, cunit: float +) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: """Load element numbers and coordinates.""" atnums = [] atcorenums = [] @@ -250,11 +276,11 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: coeffs = np.zeros((nprim, 1)) for iprim in range(nprim): words = next(lit).split() - exponents[iprim] = float(words[0].replace('D', 'E')) - coeffs[iprim, 0] = float(words[1].replace('D', 'E')) + exponents[iprim] = float(words[0].replace("D", "E")) + coeffs[iprim, 0] = float(words[1].replace("D", "E")) # Unless changed later, all shells are assumed to be Cartesian. - shells.append(Shell(icenter, [angmom], ['c'], exponents, coeffs)) - return MolecularBasis(shells, CONVENTIONS, 'L2') + shells.append(Shell(icenter, [angmom], ["c"], exponents, coeffs)) + return MolecularBasis(shells, CONVENTIONS, "L2") def _load_helper_coeffs(lit: LineIterator) -> Tuple: @@ -281,24 +307,24 @@ def _load_helper_coeffs(lit: LineIterator) -> Tuple: # An bracket also means we are done and a new section has started. # Other parts of the parser may need this section line, so we push it # back. - if '[' in line: + if "[" in line: lit.back(line) break # prepare array with orbital coefficients info = {} lit.back(line) for line in lit: - if line.count('=') != 1: + if line.count("=") != 1: lit.back(line) break - key, value = line.split('=') + key, value = line.split("=") info[key.strip().lower()] = value - occ = float(info['occup']) + occ = float(info["occup"]) col = [] - energy = float(info['ene']) - irrep = info.get('sym', '??').strip() + energy = float(info["ene"]) + irrep = info.get("sym", "??").strip() # store column of coefficients, i.e. one orbital, energy and occ - if info['spin'].strip().lower() == 'alpha': + if info["spin"].strip().lower() == "alpha": occsa.append(occ) coeffsa.append(col) energiesa.append(energy) @@ -331,9 +357,13 @@ def _load_helper_coeffs(lit: LineIterator) -> Tuple: return (occsa, coeffsa, energiesa, irrepsa), (occsb, coeffsb, energiesb, irrepsb) -def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, - orb_alpha: np.ndarray, orb_beta: np.ndarray, - norm_threshold: float = 1e-4) -> bool: +def _is_normalized_properly( + obasis: MolecularBasis, + atcoords: np.ndarray, + orb_alpha: np.ndarray, + orb_beta: np.ndarray, + norm_threshold: float = 1e-4, +) -> bool: """Test the normalization of the occupied and virtual orbitals. Parameters @@ -391,18 +421,33 @@ def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: different sign conventions for some of the pure functions. """ orca_conventions = { - (0, 'c'): ['1'], - (1, 'c'): ['x', 'y', 'z'], - (2, 'p'): ['c0', 'c1', 's1', 'c2', 's2'], - (2, 'c'): ['xx', 'yy', 'zz', 'xy', 'xz', 'yz'], - (3, 'p'): ['c0', 'c1', 's1', 'c2', 's2', '-c3', '-s3'], - (3, 'c'): ['xxx', 'yyy', 'zzz', 'xyy', 'xxy', 'xxz', 'xzz', 'yzz', 'yyz', 'xyz'], - (4, 'p'): ['c0', 'c1', 's1', 'c2', 's2', '-c3', '-s3', '-c4', '-s4'], - (4, 'c'): ['xxxx', 'yyyy', 'zzzz', 'xxxy', 'xxxz', 'xyyy', 'yyyz', 'xzzz', - 'yzzz', 'xxyy', 'xxzz', 'yyzz', 'xxyz', 'xyyz', 'xyzz'], + (0, "c"): ["1"], + (1, "c"): ["x", "y", "z"], + (2, "p"): ["c0", "c1", "s1", "c2", "s2"], + (2, "c"): ["xx", "yy", "zz", "xy", "xz", "yz"], + (3, "p"): ["c0", "c1", "s1", "c2", "s2", "-c3", "-s3"], + (3, "c"): ["xxx", "yyy", "zzz", "xyy", "xxy", "xxz", "xzz", "yzz", "yyz", "xyz"], + (4, "p"): ["c0", "c1", "s1", "c2", "s2", "-c3", "-s3", "-c4", "-s4"], + (4, "c"): [ + "xxxx", + "yyyy", + "zzzz", + "xxxy", + "xxxz", + "xyyy", + "yyyz", + "xzzz", + "yzzz", + "xxyy", + "xxzz", + "yyzz", + "xxyz", + "xyyz", + "xyzz", + ], # H functions are not officialy supported by Molden, but this is how # ORCA writes Molden files anyway: - (5, 'p'): ['c0', 'c1', 's1', 'c2', 's2', '-c3', '-s3', '-c4', '-s4', 'c5', 's5'], + (5, "p"): ["c0", "c1", "s1", "c2", "s2", "-c3", "-s3", "-c4", "-s4", "c5", "s5"], } fixed_shells = [] for shell in obasis.shells: @@ -418,13 +463,13 @@ def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: correction = gob_cart_normalization(exponent, np.array([0, 0, 0])) elif angmom == 1: correction = gob_cart_normalization(exponent, np.array([1, 0, 0])) - elif angmom == 2 and kind == 'p': + elif angmom == 2 and kind == "p": correction = gob_cart_normalization(exponent, np.array([1, 1, 0])) - elif angmom == 3 and kind == 'p': + elif angmom == 3 and kind == "p": correction = gob_cart_normalization(exponent, np.array([1, 1, 1])) - elif angmom == 4 and kind == 'p': + elif angmom == 4 and kind == "p": correction = gob_cart_normalization(exponent, np.array([2, 1, 1])) - elif angmom == 5 and kind == 'p': + elif angmom == 5 and kind == "p": correction = gob_cart_normalization(exponent, np.array([5, 0, 0])) if correction != 1.0: fixed_shell.coeffs[iprim, 0] /= correction @@ -452,9 +497,9 @@ def _fix_obasis_psi4(obasis: MolecularBasis) -> Union[MolecularBasis, None]: correction = gob_cart_normalization(exponent, np.array([0, 0, 0])) elif angmom == 1: correction = gob_cart_normalization(exponent, np.array([1, 0, 0])) - elif angmom == 2 and kind == 'p': + elif angmom == 2 and kind == "p": correction = gob_cart_normalization(exponent, np.array([1, 1, 0])) / np.sqrt(3.0) - elif angmom == 3 and kind == 'p': + elif angmom == 3 and kind == "p": correction = gob_cart_normalization(exponent, np.array([1, 1, 1])) / np.sqrt(15.0) # elif angmom == 4 and kind == 'p': ## ! Not tested # correction = gob_cart_normalization(exponent, np.array([2, 1, 1]))/np.sqrt(105.0) @@ -482,11 +527,11 @@ def _fix_obasis_turbomole(obasis: MolecularBasis) -> Union[MolecularBasis, None] for iprim in range(shell.nprim): # Default 1.0: do not to correct anything, unless we know how to correct. correction = 1.0 - if angmom == 2 and kind == 'c': + if angmom == 2 and kind == "c": correction = 1.0 / np.sqrt(3.0) - elif angmom == 3 and kind == 'c': + elif angmom == 3 and kind == "c": correction = 1.0 / np.sqrt(15.0) - elif angmom == 4 and kind == 'c': + elif angmom == 4 and kind == "c": correction = 1.0 / np.sqrt(105.0) if correction != 1.0: corrected = True @@ -509,9 +554,7 @@ def _fix_obasis_normalize_contractions(obasis: MolecularBasis) -> MolecularBasis fixed_shells = [] for shell in obasis.shells: shell_obasis = MolecularBasis( - [attr.evolve(shell, icenter=0)], - obasis.conventions, - obasis.primitive_normalization + [attr.evolve(shell, icenter=0)], obasis.conventions, obasis.primitive_normalization ) # 2) Get the first diagonal element of the overlap matrix olpdiag = compute_overlap(shell_obasis, np.zeros((1, 3), float))[0, 0] @@ -576,8 +619,12 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: elif angmom == 3: factors = np.array([1.0 / np.sqrt(15.0)] * 3 + [1.0 / (np.sqrt(3.0))] * 6 + [1.0]) elif angmom == 4: - factors = np.array([1.0 / np.sqrt(105.0)] * 3 + [1.0 / (np.sqrt(15.0))] * 6 - + [1.0 / 3.0] * 3 + [1.0 / (np.sqrt(3.0))] * 3) + factors = np.array( + [1.0 / np.sqrt(105.0)] * 3 + + [1.0 / (np.sqrt(15.0))] * 6 + + [1.0 / 3.0] * 3 + + [1.0 / (np.sqrt(3.0))] * 3 + ) if factors is None: factors = np.ones(shell.nbasis) else: @@ -609,17 +656,17 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold """ # pylint: disable=too-many-return-statements - obasis = result['obasis'] - atcoords = result['atcoords'] - if result['mo'].kind == 'restricted': - coeffsa = result['mo'].coeffs + obasis = result["obasis"] + atcoords = result["atcoords"] + if result["mo"].kind == "restricted": + coeffsa = result["mo"].coeffs # Skip testing coeffsb if it is the same array as coeffsa. coeffsb = None - elif result['mo'].kind == 'unrestricted': - coeffsa = result['mo'].coeffsa - coeffsb = result['mo'].coeffsb + elif result["mo"].kind == "unrestricted": + coeffsa = result["mo"].coeffsa + coeffsb = result["mo"].coeffsb else: - raise ValueError('Molecular orbital kind={0} not recognized'.format(result['mo'].kind)) + raise ValueError("Molecular orbital kind={0} not recognized".format(result["mo"].kind)) if _is_normalized_properly(obasis, atcoords, coeffsa, coeffsb, norm_threshold): # The file is good. No need to change obasis. @@ -628,24 +675,26 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold # --- ORCA orca_obasis = _fix_obasis_orca(obasis) if _is_normalized_properly(orca_obasis, atcoords, coeffsa, coeffsb, norm_threshold): - lit.warn('Corrected for typical ORCA errors in Molden/MKL file.') - result['obasis'] = orca_obasis + lit.warn("Corrected for typical ORCA errors in Molden/MKL file.") + result["obasis"] = orca_obasis return # --- PSI4 < 1.0 psi4_obasis = _fix_obasis_psi4(obasis) - if psi4_obasis is not None and \ - _is_normalized_properly(psi4_obasis, atcoords, coeffsa, coeffsb, norm_threshold): - lit.warn('Corrected for PSI4 < 1.0 errors in Molden/MKL file.') - result['obasis'] = psi4_obasis + if psi4_obasis is not None and _is_normalized_properly( + psi4_obasis, atcoords, coeffsa, coeffsb, norm_threshold + ): + lit.warn("Corrected for PSI4 < 1.0 errors in Molden/MKL file.") + result["obasis"] = psi4_obasis return # -- Turbomole turbom_obasis = _fix_obasis_turbomole(obasis) - if turbom_obasis is not None and \ - _is_normalized_properly(turbom_obasis, atcoords, coeffsa, coeffsb, norm_threshold): - lit.warn('Corrected for Turbomole errors in Molden/MKL file.') - result['obasis'] = turbom_obasis + if turbom_obasis is not None and _is_normalized_properly( + turbom_obasis, atcoords, coeffsa, coeffsb, norm_threshold + ): + lit.warn("Corrected for Turbomole errors in Molden/MKL file.") + result["obasis"] = turbom_obasis return # --- CFOUR 2.1 @@ -656,24 +705,21 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold coeffsb_cfour = None else: coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] - if _is_normalized_properly(obasis, - atcoords, - coeffsa_cfour, - coeffsb_cfour, norm_threshold): - lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') - result['obasis'] = obasis - if result['mo'].kind == 'restricted': - result['mo'].coeffs[:] = coeffsa_cfour + if _is_normalized_properly(obasis, atcoords, coeffsa_cfour, coeffsb_cfour, norm_threshold): + lit.warn("Corrected for CFOUR 2.1 errors in Molden/MKL file.") + result["obasis"] = obasis + if result["mo"].kind == "restricted": + result["mo"].coeffs[:] = coeffsa_cfour else: - result['mo'].coeffsa[:] = coeffsa_cfour - result['mo'].coeffsb[:] = coeffsb_cfour + result["mo"].coeffsa[:] = coeffsa_cfour + result["mo"].coeffsb[:] = coeffsb_cfour return # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb, norm_threshold): - lit.warn('Corrected for unnormalized contractions in Molden/MKL file.') - result['obasis'] = normed_obasis + lit.warn("Corrected for unnormalized contractions in Molden/MKL file.") + result["obasis"] = normed_obasis return # --- PSI4 <= 1.3.2 @@ -684,47 +730,52 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold coeffsb_psi4 = None else: coeffsb_psi4 = coeffsb / psi4_coeff_correction[:, np.newaxis] - if _is_normalized_properly(normed_obasis, atcoords, coeffsa_psi4, - coeffsb_psi4, norm_threshold): - lit.warn('Corrected for PSI4 <= 1.3.2 errors in Molden/MKL file.') - result['obasis'] = normed_obasis - if result['mo'].kind == 'restricted': - result['mo'].coeffs[:] = coeffsa_psi4 + if _is_normalized_properly( + normed_obasis, atcoords, coeffsa_psi4, coeffsb_psi4, norm_threshold + ): + lit.warn("Corrected for PSI4 <= 1.3.2 errors in Molden/MKL file.") + result["obasis"] = normed_obasis + if result["mo"].kind == "restricted": + result["mo"].coeffs[:] = coeffsa_psi4 else: - result['mo'].coeffsa[:] = coeffsa_psi4 - result['mo'].coeffsb[:] = coeffsb_psi4 + result["mo"].coeffsa[:] = coeffsa_psi4 + result["mo"].coeffsb[:] = coeffsb_psi4 return - lit.error('Could not correct the data read from {}. The molden or mkl file ' - 'you are trying to load contains errors. Please make an issue ' - 'here: https://github.com/theochem/iodata/issues, and attach ' - 'this file. Please provide one or more small files causing this ' - 'error. Thanks!') + lit.error( + "Could not correct the data read from {}. The molden or mkl file " + "you are trying to load contains errors. Please make an issue " + "here: https://github.com/theochem/iodata/issues, and attach " + "this file. Please provide one or more small files causing this " + "error. Thanks!" + ) -@document_dump_one("Molden", ['atcoords', 'atnums', 'mo', 'obasis'], ['atcorenums', 'title']) +@document_dump_one("Molden", ["atcoords", "atnums", "mo", "obasis"], ["atcorenums", "title"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # Print the header - f.write('[Molden Format]\n') + f.write("[Molden Format]\n") if data.title is not None: - f.write('[Title]\n') - f.write(' {}\n'.format(data.title)) + f.write("[Title]\n") + f.write(" {}\n".format(data.title)) # Print the elements numbers and the coordinates - f.write('[Atoms] AU\n') + f.write("[Atoms] AU\n") for iatom in range(data.natom): atnum = data.atnums[iatom] atcorenum = data.atcorenums[iatom] x, y, z = data.atcoords[iatom] - f.write('{:2s} {:3d} {:3.0f} {:25.18f} {:25.18f} {:25.18f}\n'.format( - num2sym[atnum].ljust(2), iatom + 1, atcorenum, x, y, z - )) - f.write('\n') + f.write( + "{:2s} {:3d} {:3.0f} {:25.18f} {:25.18f} {:25.18f}\n".format( + num2sym[atnum].ljust(2), iatom + 1, atcorenum, x, y, z + ) + ) + f.write("\n") # Print the basis set if data.obasis is None: - raise IOError('A Gaussian orbital basis is required to write a molden file.') + raise IOError("A Gaussian orbital basis is required to write a molden file.") obasis = data.obasis # Figure out the pure/Cartesian situation. Note that the Molden @@ -736,31 +787,33 @@ def dump_one(f: TextIO, data: IOData): for angmom, kind in zip(shell.angmoms, shell.kinds): if angmom in angmom_kinds: if kind != angmom_kinds[angmom]: - raise IOError('Molden format does not support mixed ' - 'pure+Cartesian functions for one ' - 'angular momentum.') + raise IOError( + "Molden format does not support mixed " + "pure+Cartesian functions for one " + "angular momentum." + ) else: angmom_kinds[angmom] = kind # Fill in some defaults (Cartesian) for angmom kinds if needed. - angmom_kinds.setdefault(2, 'c') - angmom_kinds.setdefault(3, 'c') - angmom_kinds.setdefault(4, 'c') - angmom_kinds.setdefault(5, 'c') + angmom_kinds.setdefault(2, "c") + angmom_kinds.setdefault(3, "c") + angmom_kinds.setdefault(4, "c") + angmom_kinds.setdefault(5, "c") # Write out the Cartesian/Pure conventions. What a messy format... - if angmom_kinds[2] == 'p': - if angmom_kinds[3] == 'p': - f.write('[5D]\n') + if angmom_kinds[2] == "p": + if angmom_kinds[3] == "p": + f.write("[5D]\n") else: - f.write('[5D10F]\n') + f.write("[5D10F]\n") else: - if angmom_kinds[3] == 'p': - f.write('[7F]\n') - if angmom_kinds[4] == 'p': - f.write('[9G]\n') + if angmom_kinds[3] == "p": + f.write("[7F]\n") + if angmom_kinds[4] == "p": + f.write("[9G]\n") - f.write('[GTO]\n') + f.write("[GTO]\n") last_icenter = -1 # The shells must be sorted by center. for shell in sorted(obasis.shells, key=(lambda s: s.icenter)): @@ -768,54 +821,69 @@ def dump_one(f: TextIO, data: IOData): if last_icenter != -1: f.write("\n") last_icenter = shell.icenter - f.write('%3i 0\n' % (shell.icenter + 1)) + f.write("%3i 0\n" % (shell.icenter + 1)) # Write out as a segmented basis. Molden format does not support # generalized contractions. for iangmom, angmom in enumerate(shell.angmoms): - f.write(' {:1s} {:3d} 1.00\n'.format(angmom_its(angmom), shell.nprim)) + f.write(" {:1s} {:3d} 1.00\n".format(angmom_its(angmom), shell.nprim)) for exponent, coeff in zip(shell.exponents, shell.coeffs[:, iangmom]): - f.write('{:20.10f} {:20.10f}\n'.format(exponent, coeff)) + f.write("{:20.10f} {:20.10f}\n".format(exponent, coeff)) f.write("\n") # Get the permutation to convert the orbital coefficients to Molden conventions. permutation, signs = convert_conventions(obasis, CONVENTIONS) # Print the mean-field orbitals - if data.mo.kind == 'unrestricted': - f.write('[MO]\n') + if data.mo.kind == "unrestricted": + f.write("[MO]\n") irrepsa = data.mo.irrepsa if irrepsa is None: - irrepsa = ['1a'] * data.mo.norba - _dump_helper_orb(f, 'Alpha', data.mo.occsa, - data.mo.coeffsa[permutation] * signs.reshape(-1, 1), - data.mo.energiesa, irrepsa) + irrepsa = ["1a"] * data.mo.norba + _dump_helper_orb( + f, + "Alpha", + data.mo.occsa, + data.mo.coeffsa[permutation] * signs.reshape(-1, 1), + data.mo.energiesa, + irrepsa, + ) irrepsb = data.mo.irrepsb if irrepsb is None: - irrepsb = ['1a'] * data.mo.norbb - _dump_helper_orb(f, 'Beta', data.mo.occsb, - data.mo.coeffsb[permutation] * signs.reshape(-1, 1), - data.mo.energiesb, irrepsb) - elif data.mo.kind == 'restricted': - f.write('[MO]\n') + irrepsb = ["1a"] * data.mo.norbb + _dump_helper_orb( + f, + "Beta", + data.mo.occsb, + data.mo.coeffsb[permutation] * signs.reshape(-1, 1), + data.mo.energiesb, + irrepsb, + ) + elif data.mo.kind == "restricted": + f.write("[MO]\n") irreps = data.mo.irreps if irreps is None: - irreps = ['1a'] * data.mo.norb - _dump_helper_orb(f, 'Alpha', data.mo.occs, - data.mo.coeffs[permutation] * signs.reshape(-1, 1), - data.mo.energies, irreps) + irreps = ["1a"] * data.mo.norb + _dump_helper_orb( + f, + "Alpha", + data.mo.occs, + data.mo.coeffs[permutation] * signs.reshape(-1, 1), + data.mo.energies, + irreps, + ) else: raise NotImplementedError def _dump_helper_orb(f, spin, occs, coeffs, energies, irreps): for ifn in range(coeffs.shape[1]): - f.write(f' Ene= {energies[ifn]:.17e}\n') - f.write(f' Sym= {irreps[ifn]}\n') - f.write(f' Spin= {spin}\n') - f.write(f' Occup= {occs[ifn]:.17e}\n') + f.write(f" Ene= {energies[ifn]:.17e}\n") + f.write(f" Sym= {irreps[ifn]}\n") + f.write(f" Spin= {spin}\n") + f.write(f" Occup= {occs[ifn]:.17e}\n") for ibasis in range(coeffs.shape[0]): # The original molden floating-point formatting is too low # precision. Molden also reads high-precision, so we use this # instead. # f.write('{:4d} {:10.6f}\n'.format(ibasis + 1, orb_coeffs[ibasis, ifn])) - f.write('{:4d} {:.17e}\n'.format(ibasis + 1, coeffs[ibasis, ifn])) + f.write("{:4d} {:.17e}\n".format(ibasis + 1, coeffs[ibasis, ifn])) diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 24ed9c53..b81daf92 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -38,7 +38,7 @@ __all__ = [] -PATTERNS = ['*.mkl'] +PATTERNS = ["*.mkl"] def _load_helper_charge_spinpol(lit: LineIterator) -> List[int]: @@ -51,18 +51,18 @@ def _load_helper_charges(lit: LineIterator) -> dict: atcharges = [] for line in lit: line = line.strip() - if line == '$END': + if line == "$END": break atcharges.append(float(line)) - return {'mulliken': np.array(atcharges)} + return {"mulliken": np.array(atcharges)} def _load_helper_atoms(lit: LineIterator) -> Tuple[np.ndarray, np.ndarray]: atnums = [] atcoords = [] for line in lit: - if line.strip() == '$END': + if line.strip() == "$END": break words = line.split() atnums.append(int(words[0])) @@ -77,24 +77,25 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: icenter = 0 while True: line = next(lit).strip() - if line == '$END': + if line == "$END": break if line == "": continue - if line == '$$': + if line == "$$": icenter += 1 continue # Shell header, always assuming pure functions words = line.split() angmom = angmom_sti(words[1]) nbasis_shell = int(words[0]) - if nbasis_shell == len(CONVENTIONS[(angmom, 'c')]): - kind = 'c' - elif nbasis_shell == len(CONVENTIONS[(angmom, 'p')]): - kind = 'p' + if nbasis_shell == len(CONVENTIONS[(angmom, "c")]): + kind = "c" + elif nbasis_shell == len(CONVENTIONS[(angmom, "p")]): + kind = "p" else: - lit.error('Cannot interpret angmom={} with nbasis_shell={}'.format( - angmom, nbasis_shell)) + lit.error( + "Cannot interpret angmom={} with nbasis_shell={}".format(angmom, nbasis_shell) + ) exponents = [] coeffs = [] for line in lit: @@ -105,7 +106,7 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: exponents.append(float(words[0])) coeffs.append([float(words[1])]) shells.append(Shell(icenter, [angmom], [kind], np.array(exponents), np.array(coeffs))) - return MolecularBasis(shells, CONVENTIONS, 'L2') + return MolecularBasis(shells, CONVENTIONS, "L2") def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> Tuple[np.ndarray, np.ndarray]: @@ -116,7 +117,7 @@ def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> Tuple[np.ndarray, np. in_orb = 0 for line in lit: line = line.strip() - if line == '$END': + if line == "$END": break if in_orb == 0: # read a1g line @@ -153,7 +154,7 @@ def _load_helper_occ(lit: LineIterator) -> np.ndarray: occs = [] for line in lit: line = line.strip() - if line == '$END': + if line == "$END": break for word in line.split(): occs.append(float(word)) @@ -163,10 +164,13 @@ def _load_helper_occ(lit: LineIterator) -> np.ndarray: # pylint: disable=too-many-branches,too-many-statements @document_load_one( "Molekel", - ['atcoords', 'atnums', 'mo', 'obasis'], ['atcharges'], - {"norm_threshold": "When the normalization of one of the orbitals exceeds " - "norm_threshold, a correction is attempted or an error " - "is raised when no suitable correction can be found."} + ["atcoords", "atnums", "mo", "obasis"], + ["atcharges"], + { + "norm_threshold": "When the normalization of one of the orbitals exceeds " + "norm_threshold, a correction is attempted or an error " + "is raised when no suitable correction can be found." + }, ) def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: """Do not edit this docstring. It will be overwritten.""" @@ -192,33 +196,33 @@ def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: # There is no file-end marker we can use, so we only stop when # reaching the end of the file. break - if line == '$CHAR_MULT': + if line == "$CHAR_MULT": charge, spinpol = _load_helper_charge_spinpol(lit) - elif line == '$CHARGES': + elif line == "$CHARGES": atcharges = _load_helper_charges(lit) - elif line == '$COORD': + elif line == "$COORD": atnums, atcoords = _load_helper_atoms(lit) - elif line == '$BASIS': + elif line == "$BASIS": obasis = _load_helper_obasis(lit) - elif line == '$COEFF_ALPHA': + elif line == "$COEFF_ALPHA": coeffsa, energiesa, irrepsa = _load_helper_coeffs(lit, obasis.nbasis) - elif line == '$OCC_ALPHA': + elif line == "$OCC_ALPHA": occsa = _load_helper_occ(lit) - elif line == '$COEFF_BETA': + elif line == "$COEFF_BETA": coeffsb, energiesb, irrepsb = _load_helper_coeffs(lit, obasis.nbasis) - elif line == '$OCC_BETA': + elif line == "$OCC_BETA": occsb = _load_helper_occ(lit) if charge is None: - lit.error('Charge and spin polarization not found.') + lit.error("Charge and spin polarization not found.") if atcoords is None: - lit.error('Coordinates not found.') + lit.error("Coordinates not found.") if obasis is None: - lit.error('Orbital basis not found.') + lit.error("Orbital basis not found.") if coeffsa is None: - lit.error('Alpha orbitals not found.') + lit.error("Alpha orbitals not found.") if occsa is None: - lit.error('Alpha occupation numbers not found.') + lit.error("Alpha occupation numbers not found.") nelec = atnums.sum() - charge if coeffsb is None: @@ -226,12 +230,13 @@ def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: assert nelec % 2 == 0 assert abs(occsa.sum() - nelec) < 1e-7 mo = MolecularOrbitals( - 'restricted', coeffsa.shape[1], coeffsa.shape[1], - occsa, coeffsa, energiesa, irrepsa) + "restricted", coeffsa.shape[1], coeffsa.shape[1], occsa, coeffsa, energiesa, irrepsa + ) else: if occsb is None: - lit.error('Beta occupation numbers not found in mkl file while ' - 'beta orbitals were present.') + lit.error( + "Beta occupation numbers not found in mkl file while " "beta orbitals were present." + ) nalpha = int(np.round(occsa.sum())) nbeta = int(np.round(occsb.sum())) assert abs(spinpol - abs(nalpha - nbeta)) < 1e-7 @@ -240,100 +245,101 @@ def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: assert energiesa.shape == energiesb.shape assert occsa.shape == occsb.shape mo = MolecularOrbitals( - 'unrestricted', + "unrestricted", coeffsa.shape[1], coeffsb.shape[1], np.concatenate((occsa, occsb), axis=0), np.concatenate((coeffsa, coeffsb), axis=1), np.concatenate((energiesa, energiesb), axis=0), - irrepsa + irrepsb) + irrepsa + irrepsb, + ) result = { - 'atcoords': atcoords, - 'atnums': atnums, - 'obasis': obasis, - 'mo': mo, - 'atcharges': atcharges, + "atcoords": atcoords, + "atnums": atnums, + "obasis": obasis, + "mo": mo, + "atcharges": atcharges, } _fix_molden_from_buggy_codes(result, lit, norm_threshold) return result -@document_dump_one("Molekel", ['atcoords', 'atnums', 'mo', 'obasis'], ['atcharges']) +@document_dump_one("Molekel", ["atcoords", "atnums", "mo", "obasis"], ["atcharges"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # Header - f.write('$MKL\n') - f.write('#\n') - f.write('# MKL format file produced by IOData\n') - f.write('#\n') + f.write("$MKL\n") + f.write("#\n") + f.write("# MKL format file produced by IOData\n") + f.write("#\n") # CHAR_MUL - f.write('$CHAR_MULT\n') - f.write(' {:.0f} {:.0f}\n'.format(data.charge, data.spinpol + 1)) - f.write('$END\n') - f.write('\n') + f.write("$CHAR_MULT\n") + f.write(" {:.0f} {:.0f}\n".format(data.charge, data.spinpol + 1)) + f.write("$END\n") + f.write("\n") # COORD atcoords = data.atcoords / angstrom - f.write('$COORD\n') + f.write("$COORD\n") for n, coord in zip(data.atnums, atcoords): - f.write(' {:d} {: ,.6f} {: ,.6f} {: ,.6f}\n'.format(n, coord[0], coord[1], coord[2])) - f.write('$END\n') - f.write('\n') + f.write(" {:d} {: ,.6f} {: ,.6f} {: ,.6f}\n".format(n, coord[0], coord[1], coord[2])) + f.write("$END\n") + f.write("\n") # CHARGES - if 'mulliken' in data.atcharges: - f.write('$CHARGES\n') - for charge in data.atcharges['mulliken']: - f.write(' {: ,.6f}\n'.format(charge)) - f.write('$END\n') - f.write('\n') + if "mulliken" in data.atcharges: + f.write("$CHARGES\n") + for charge in data.atcharges["mulliken"]: + f.write(" {: ,.6f}\n".format(charge)) + f.write("$END\n") + f.write("\n") # BASIS - f.write('$BASIS\n') + f.write("$BASIS\n") iatom_last = 0 for shell in data.obasis.shells: iatom_new = shell.icenter if iatom_new != iatom_last: - f.write('$$\n') + f.write("$$\n") for iangmom, (angmom, kind) in enumerate(zip(shell.angmoms, shell.kinds)): iatom_last = shell.icenter nbasis = len(CONVENTIONS[(angmom, kind)]) - f.write(' {} {:1s} 1.00\n'.format(nbasis, angmom_its(angmom).capitalize())) + f.write(" {} {:1s} 1.00\n".format(nbasis, angmom_its(angmom).capitalize())) for exponent, coeff in zip(shell.exponents, shell.coeffs[:, iangmom]): - f.write('{:20.10f} {:17.10f}\n'.format(exponent, coeff)) - f.write('\n') - f.write('$END\n') - f.write('\n') + f.write("{:20.10f} {:17.10f}\n".format(exponent, coeff)) + f.write("\n") + f.write("$END\n") + f.write("\n") - if data.mo.kind == 'restricted': + if data.mo.kind == "restricted": # COEFF_ALPHA - f.write('$COEFF_ALPHA\n') - _dump_helper_coeffs(f, data, spin='a') + f.write("$COEFF_ALPHA\n") + _dump_helper_coeffs(f, data, spin="a") # OCC_ALPHA - f.write('$OCC_ALPHA\n') - _dump_helper_occ(f, data, spin='ab') + f.write("$OCC_ALPHA\n") + _dump_helper_occ(f, data, spin="ab") # Not taking into account generalized. - elif data.mo.kind == 'unrestricted': + elif data.mo.kind == "unrestricted": # COEFF_ALPHA - f.write('$COEFF_ALPHA\n') - _dump_helper_coeffs(f, data, spin='a') + f.write("$COEFF_ALPHA\n") + _dump_helper_coeffs(f, data, spin="a") # OCC_ALPHA - f.write('$OCC_ALPHA\n') - _dump_helper_occ(f, data, spin='a') - f.write('\n') + f.write("$OCC_ALPHA\n") + _dump_helper_occ(f, data, spin="a") + f.write("\n") # COEFF_BETA - f.write('$COEFF_BETA\n') - _dump_helper_coeffs(f, data, spin='b') + f.write("$COEFF_BETA\n") + _dump_helper_coeffs(f, data, spin="b") # OCC_BETA - f.write('$OCC_BETA\n') - _dump_helper_occ(f, data, spin='b') + f.write("$OCC_BETA\n") + _dump_helper_occ(f, data, spin="b") else: raise ValueError(f"The MKL format does not support {data.mo.kind} orbitals.") @@ -342,52 +348,52 @@ def dump_one(f: TextIO, data: IOData): # Defining help dumping functions def _dump_helper_coeffs(f, data, spin=None): permutation, signs = convert_conventions(data.obasis, CONVENTIONS) - if spin == 'a': + if spin == "a": norb = data.mo.norba coeff = data.mo.coeffsa[permutation] * signs.reshape(-1, 1) ener = data.mo.energiesa if data.mo.irreps is not None: irreps = data.mo.irreps[:norb] else: - irreps = ['a1g'] * norb - elif spin == 'b': + irreps = ["a1g"] * norb + elif spin == "b": norb = data.mo.norbb coeff = data.mo.coeffsb[permutation] * signs.reshape(-1, 1) ener = data.mo.energiesb if data.mo.irreps is not None: irreps = data.mo.irreps[norb:] else: - irreps = ['a1g'] * norb + irreps = ["a1g"] * norb else: - raise IOError('A spin must be specified') + raise IOError("A spin must be specified") for j in range(0, norb, 5): - en = ' '.join([' {: ,.12f}'.format(e) for e in ener[j:j + 5]]) - irre = ' '.join(['{}'.format(irr) for irr in irreps[j:j + 5]]) - f.write(irre + '\n') - f.write(en + '\n') - for orb in coeff[:, j:j + 5]: - coeffs = ' '.join([' {: ,.12f}'.format(c) for c in orb]) - f.write(coeffs + '\n') + en = " ".join([" {: ,.12f}".format(e) for e in ener[j : j + 5]]) + irre = " ".join(["{}".format(irr) for irr in irreps[j : j + 5]]) + f.write(irre + "\n") + f.write(en + "\n") + for orb in coeff[:, j : j + 5]: + coeffs = " ".join([" {: ,.12f}".format(c) for c in orb]) + f.write(coeffs + "\n") - f.write(' $END\n') - f.write('\n') + f.write(" $END\n") + f.write("\n") def _dump_helper_occ(f, data, spin=None): - if spin == 'a': + if spin == "a": norb = data.mo.norba occ = data.mo.occsa - elif spin == 'b': + elif spin == "b": norb = data.mo.norbb occ = data.mo.occsb - elif spin == 'ab': + elif spin == "ab": norb = data.mo.norba occ = data.mo.occs else: - raise IOError('A spin must be specified') + raise IOError("A spin must be specified") for j in range(0, norb, 5): - occs = ' '.join([' {: ,.7f}'.format(o) for o in occ[j:j + 5]]) - f.write(occs + '\n') - f.write(' $END\n') + occs = " ".join([" {: ,.7f}".format(o) for o in occ[j : j + 5]]) + f.write(occs + "\n") + f.write(" $END\n") diff --git a/iodata/formats/mwfn.py b/iodata/formats/mwfn.py index d3f3b987..21e0a425 100644 --- a/iodata/formats/mwfn.py +++ b/iodata/formats/mwfn.py @@ -18,7 +18,6 @@ # -- """Multiwfn MWFN file format.""" - import numpy as np from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell @@ -30,7 +29,7 @@ __all__ = [] -PATTERNS = ['*.mwfn'] +PATTERNS = ["*.mwfn"] # From the MWFN chemrxiv paper @@ -67,10 +66,18 @@ } # fmt: on + def _load_helper_opener(lit: LineIterator) -> dict: """Read initial variables at the beginning of a MWFN file.""" - keys = {"Wfntype": int, "Charge": float, "Naelec": float, "Nbelec": float, "E_tot": float, - "VT_ratio": float, "Ncenter": int} + keys = { + "Wfntype": int, + "Charge": float, + "Naelec": float, + "Nbelec": float, + "E_tot": float, + "VT_ratio": float, + "Ncenter": int, + } max_count = len(keys) count = 0 data = {} @@ -78,7 +85,7 @@ def _load_helper_opener(lit: LineIterator) -> dict: line = next(lit) for name, ftype in keys.items(): if name in line: - data[name] = ftype(line.split('=')[1].strip()) + data[name] = ftype(line.split("=")[1].strip()) count += 1 # check values parsed @@ -113,7 +120,7 @@ def _load_helper_basis(lit: LineIterator) -> dict: line = next(lit) for name in keys: if name in line: - data[name] = int(line.split('=')[1].strip()) + data[name] = int(line.split("=")[1].strip()) count += 1 break return data @@ -129,7 +136,7 @@ def _load_helper_atoms(lit: LineIterator, natom: int) -> dict: # skip lines until "$Centers" section is reached line = next(lit) - while '$Centers' not in line and line is not None: + while "$Centers" not in line and line is not None: line = next(lit) for atom in range(natom): @@ -161,14 +168,15 @@ def _load_helper_shells(lit: LineIterator, nshell: int) -> dict: for section, name in zip(sections, var_name): if not line.startswith(section): lit.error(f"Expected line to start with {section}, but got line={line}.") - data[name] = _load_helper_section(lit, nshell, ' ', 0, int) + data[name] = _load_helper_section(lit, nshell, " ", 0, int) line = next(lit) lit.back(line) return data -def _load_helper_section(lit: LineIterator, nprim: int, start: str, skip: int, - dtype: np.dtype) -> np.ndarray: +def _load_helper_section( + lit: LineIterator, nprim: int, start: str, skip: int, dtype: np.dtype +) -> np.ndarray: """Read single or multiple line(s) section.""" section = [] while len(section) < nprim: @@ -192,9 +200,9 @@ def _load_helper_mo(lit: LineIterator, n_basis: int, n_mo: int) -> dict: for index in range(n_mo): line = next(lit) - while 'Index' not in line: + while "Index" not in line: line = next(lit) - assert line.startswith('Index') + assert line.startswith("Index") data["mo_numbers"][index] = line.split()[1] data["mo_type"][index] = next(lit).split()[1] data["mo_energies"][index] = next(lit).split()[1] @@ -202,7 +210,7 @@ def _load_helper_mo(lit: LineIterator, n_basis: int, n_mo: int) -> dict: data["mo_sym"][index] = next(lit).split()[1] # skip "$Coeff line next(lit) - data["mo_coeffs"][:, index] = _load_helper_section(lit, n_basis, '', 0, float) + data["mo_coeffs"][:, index] = _load_helper_section(lit, n_basis, "", 0, float) return data @@ -243,10 +251,10 @@ def _load_mwfn_low(lit: LineIterator) -> dict: # load primitive exponents & coefficients if not next(lit).startswith("$Primitive exponents"): lit.error("Expected '$Primitive exponents' section!") - data["exponents"] = _load_helper_section(lit, data["Nprimshell"], '', 0, float) + data["exponents"] = _load_helper_section(lit, data["Nprimshell"], "", 0, float) if not next(lit).startswith("$Contraction coefficients"): lit.error("Expected '$Contraction coefficients' section!") - data["coeffs"] = _load_helper_section(lit, data["Nprimshell"], '', 0, float) + data["coeffs"] = _load_helper_section(lit, data["Nprimshell"], "", 0, float) # get number of basis & molecular orbitals (MO) # Note: MWFN includes virtual orbitals, so num_mo equals number independent basis functions @@ -260,36 +268,43 @@ def _load_mwfn_low(lit: LineIterator) -> dict: return data -@document_load_one("MWFN", ['atcoords', 'atnums', 'atcorenums', 'energy', - 'mo', 'obasis', 'extra', 'title']) +@document_load_one( + "MWFN", ["atcoords", "atnums", "atcorenums", "energy", "mo", "obasis", "extra", "title"] +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" inp = _load_mwfn_low(lit) # store certain information loaded from MWFN in extra dictionary - extra = {'wfntype': inp['Wfntype'], - 'nindbasis': inp['Nindbasis'], - 'mo_sym': inp['mo_sym'], - 'full_virial_ratio': inp['VT_ratio']} + extra = { + "wfntype": inp["Wfntype"], + "nindbasis": inp["Nindbasis"], + "mo_sym": inp["mo_sym"], + "full_virial_ratio": inp["VT_ratio"], + } # Build MolecularBasis instance # Unlike WFN, MWFN does include orbital expansion coefficients. shells = [] counter = 0 - for center, stype, ncon in zip(inp['shell_centers'], inp['shell_types'], inp['shell_ncons']): - shells.append(Shell( - center, - [abs(stype)], - ['p' if stype < 0 else 'c'], - inp['exponents'][counter:counter + ncon], - inp['coeffs'][counter:counter + ncon][:, np.newaxis] - )) + for center, stype, ncon in zip(inp["shell_centers"], inp["shell_types"], inp["shell_ncons"]): + shells.append( + Shell( + center, + [abs(stype)], + ["p" if stype < 0 else "c"], + inp["exponents"][counter : counter + ncon], + inp["coeffs"][counter : counter + ncon][:, np.newaxis], + ) + ) counter += ncon - obasis = MolecularBasis(shells, CONVENTIONS, 'L2') + obasis = MolecularBasis(shells, CONVENTIONS, "L2") # check number of basis functions if obasis.nbasis != inp["Nbasis"]: - raise ValueError(f"Number of basis in MolecularBasis is not equal to the 'Nbasis'. " - f"{obasis.nbasis} != {inp['Nbasis']}") + raise ValueError( + f"Number of basis in MolecularBasis is not equal to the 'Nbasis'. " + f"{obasis.nbasis} != {inp['Nbasis']}" + ) # Determine number of orbitals of each kind. if inp["mo_kind"] == "restricted": @@ -305,27 +320,32 @@ def load_one(lit: LineIterator) -> dict: assert (inp["mo_type"] == 0).sum() == 0 # Build MolecularOrbitals instance mo = MolecularOrbitals( - inp["mo_kind"], norba, norbb, inp['mo_occs'], inp['mo_coeffs'], - inp['mo_energies'], None + inp["mo_kind"], norba, norbb, inp["mo_occs"], inp["mo_coeffs"], inp["mo_energies"], None ) # check number of electrons - if mo.nelec != inp['Naelec'] + inp['Nbelec']: - raise ValueError(f"Number of electrons in MolecularOrbitals is not equal to the sum of " - f"'Naelec' and 'Nbelec'. {mo.nelec} != {inp['Naelec']} + {inp['Nbelec']}") - if mo.occsa.sum() != inp['Naelec']: - raise ValueError(f"Number of alpha electrons in MolecularOrbitals is not equal to the " - f"'Naelec'. {mo.occsa.sum()} != {inp['Naelec']}") - if mo.occsb.sum() != inp['Nbelec']: - raise ValueError(f"Number of beta electrons in MolecularOrbitals is not equal to the " - f"'Nbelec'. {mo.occsb.sum()} != {inp['Nbelec']}") + if mo.nelec != inp["Naelec"] + inp["Nbelec"]: + raise ValueError( + f"Number of electrons in MolecularOrbitals is not equal to the sum of " + f"'Naelec' and 'Nbelec'. {mo.nelec} != {inp['Naelec']} + {inp['Nbelec']}" + ) + if mo.occsa.sum() != inp["Naelec"]: + raise ValueError( + f"Number of alpha electrons in MolecularOrbitals is not equal to the " + f"'Naelec'. {mo.occsa.sum()} != {inp['Naelec']}" + ) + if mo.occsb.sum() != inp["Nbelec"]: + raise ValueError( + f"Number of beta electrons in MolecularOrbitals is not equal to the " + f"'Nbelec'. {mo.occsb.sum()} != {inp['Nbelec']}" + ) return { - 'title': inp['title'], - 'atcoords': inp['atcoords'], - 'atnums': inp['atnums'], - 'atcorenums': inp['atcorenums'], - 'obasis': obasis, - 'mo': mo, - 'energy': inp['E_tot'], - 'extra': extra, + "title": inp["title"], + "atcoords": inp["atcoords"], + "atnums": inp["atnums"], + "atcorenums": inp["atcorenums"], + "obasis": obasis, + "mo": mo, + "energy": inp["E_tot"], + "extra": extra, } diff --git a/iodata/formats/orcalog.py b/iodata/formats/orcalog.py index 738dccf9..e88fa4a4 100644 --- a/iodata/formats/orcalog.py +++ b/iodata/formats/orcalog.py @@ -18,7 +18,6 @@ # -- """Orca output file format.""" - from typing import TextIO, Tuple import numpy as np @@ -30,10 +29,10 @@ __all__ = [] -PATTERNS = ['*.out'] +PATTERNS = ["*.out"] -@document_load_one("Orca output", ['atcoords', 'atnums', 'energy', 'moments', 'extra']) +@document_load_one("Orca output", ["atcoords", "atnums", "energy", "moments", "extra"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" result = {} @@ -44,25 +43,25 @@ def load_one(lit: LineIterator) -> dict: # Read until the end of the file. break # Get the total number of atoms - if line.startswith('CARTESIAN COORDINATES (ANGSTROEM)'): + if line.startswith("CARTESIAN COORDINATES (ANGSTROEM)"): natom = _helper_number_atoms(lit) # Every Cartesian coordinates found are replaced with the old ones # to maintain the ones from the final SCF iteration in e.g. optimization run - if line.startswith('CARTESIAN COORDINATES (A.U.)'): - result['atnums'], result['atcoords'] = _helper_geometry(lit, natom) + if line.startswith("CARTESIAN COORDINATES (A.U.)"): + result["atnums"], result["atcoords"] = _helper_geometry(lit, natom) # Read the energies of each SCF cycle in iodata.extra - if line.startswith('SCF ITERATIONS'): + if line.startswith("SCF ITERATIONS"): scf_energies = _helper_scf_energies(lit) - result['extra'] = {'scf_energies': scf_energies} + result["extra"] = {"scf_energies": scf_energies} # The final SCF energy is obtained - if line.startswith('FINAL SINGLE POINT ENERGY'): + if line.startswith("FINAL SINGLE POINT ENERGY"): words = line.split() - result['energy'] = float(words[4]) + result["energy"] = float(words[4]) # Read also the dipole moment - if line.startswith('Total Dipole Moment'): + if line.startswith("Total Dipole Moment"): words = line.split() dipole = np.array([float(words[4]), float(words[5]), float(words[6])]) - result['moments'] = {(1, 'c'): dipole} + result["moments"] = {(1, "c"): dipole} return result @@ -84,7 +83,7 @@ def _helper_number_atoms(lit: LineIterator) -> int: next(lit) natom = 0 # Add until an empty line is found - while next(lit).strip() != '': + while next(lit).strip() != "": natom += 1 return natom @@ -138,7 +137,7 @@ def _helper_scf_energies(lit: TextIO) -> Tuple[np.ndarray, np.ndarray]: energies = [] line = next(lit) # read the next line until blank line - while line.strip() != '': + while line.strip() != "": words = line.split() if words[0].isdigit(): energies.append(float(words[1])) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 34fec6e0..928001a5 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -23,13 +23,16 @@ http://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html """ - from typing import TextIO, Iterator import numpy as np -from ..docstrings import (document_load_one, document_load_many, document_dump_one, - document_dump_many) +from ..docstrings import ( + document_load_one, + document_load_many, + document_dump_one, + document_dump_many, +) from ..iodata import IOData from ..periodic import sym2num, num2sym, bond2num from ..utils import angstrom, LineIterator @@ -38,7 +41,7 @@ __all__ = [] -PATTERNS = ['*.pdb'] +PATTERNS = ["*.pdb"] def _parse_pdb_atom_line(line, lit): @@ -132,14 +135,14 @@ def _parse_pdb_conect_line(line): # 27 - 31 Integer serial Serial number of bonded atom iatom0 = int(line[7:12]) - 1 for ipos in 12, 17, 22, 27: - serial_str = line[ipos: ipos + 5].strip() + serial_str = line[ipos : ipos + 5].strip() if serial_str != "": iatom1 = int(serial_str) - 1 if iatom1 > iatom0: yield iatom0, iatom1 -@document_load_one("PDB", ['atcoords', 'atnums', 'atffparams', 'extra'], ['title', 'bonds']) +@document_load_one("PDB", ["atcoords", "atnums", "atffparams", "extra"], ["title", "bonds"]) def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, too-many-statements """Do not edit this docstring. It will be overwritten.""" title_lines = [] @@ -166,8 +169,9 @@ def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, t if line.startswith("COMPND"): compnd_lines.append(line[10:].strip()) if line.startswith("ATOM") or line.startswith("HETATM"): - (atnum, attype, restype, chainid, resnum, atcoord, occupancy, - bfactor) = _parse_pdb_atom_line(line, lit) + (atnum, attype, restype, chainid, resnum, atcoord, occupancy, bfactor) = ( + _parse_pdb_atom_line(line, lit) + ) atnums.append(atnum) attypes.append(attype) restypes.append(restype) @@ -202,7 +206,7 @@ def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, t if len(compnd_lines) > 0: extra["compound"] = "\n".join(compnd_lines) # add chain id, if it wasn't all empty - if not np.all(chainids == [' '] * len(chainids)): + if not np.all(chainids == [" "] * len(chainids)): extra["chainids"] = np.array(chainids) # Set a useful title if len(title_lines) == 0: @@ -216,11 +220,11 @@ def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, t else: title = "\n".join(title_lines) result = { - 'atcoords': np.array(atcoords), - 'atnums': np.array(atnums), - 'atffparams': atffparams, - 'title': title, - 'extra': extra, + "atcoords": np.array(atcoords), + "atnums": np.array(atnums), + "atffparams": atffparams, + "title": title, + "extra": extra, } # assign bonds only if some were present if len(bonds) > 0: @@ -228,7 +232,7 @@ def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, t return result -@document_load_many("PDB", ['atcoords', 'atnums', 'atffparams', 'extra'], ['title']) +@document_load_many("PDB", ["atcoords", "atnums", "atffparams", "extra"], ["title"]) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # PDB files with more molecules are a simple concatenation of individual PDB files,' @@ -259,19 +263,19 @@ def _dump_multiline_str(f: TextIO, key: str, value: str): prefix = key + str(iline + 2).rjust(10 - len(key)) + " " -@document_dump_one("PDB", ['atcoords', 'atnums', 'extra'], ['atffparams', 'title', 'bonds']) +@document_dump_one("PDB", ["atcoords", "atnums", "extra"], ["atffparams", "title", "bonds"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" _dump_multiline_str(f, "TITLE", data.title or "Created with IOData") if "compound" in data.extra: _dump_multiline_str(f, "COMPND", data.extra["compound"]) # Prepare for ATOM lines. - attypes = data.atffparams.get('attypes', None) - restypes = data.atffparams.get('restypes', None) - resnums = data.atffparams.get('resnums', None) - occupancies = data.extra.get('occupancies', None) - bfactors = data.extra.get('bfactors', None) - chainids = data.extra.get('chainids', None) + attypes = data.atffparams.get("attypes", None) + restypes = data.atffparams.get("restypes", None) + resnums = data.atffparams.get("resnums", None) + occupancies = data.extra.get("occupancies", None) + bfactors = data.extra.get("bfactors", None) + chainids = data.extra.get("chainids", None) # Write ATOM lines. for i in range(data.natom): n = num2sym[data.atnums[i]] @@ -282,8 +286,8 @@ def dump_one(f: TextIO, data: IOData): attype = str(n + str(i + 1)) if attypes is None else attypes[i] restype = "XXX" if restypes is None else restypes[i] chain = " " if chainids is None else chainids[i] - out1 = f'{i+1:>5d} {attype:<4s} {restype:3s} {chain:1s}{resnum:>4d} ' - out2 = f'{x:8.3f}{y:8.3f}{z:8.3f}{occ:6.2f}{b:6.2f}{n:>12s}' + out1 = f"{i+1:>5d} {attype:<4s} {restype:3s} {chain:1s}{resnum:>4d} " + out2 = f"{x:8.3f}{y:8.3f}{z:8.3f}{occ:6.2f}{b:6.2f}{n:>12s}" print("ATOM " + out1 + out2, file=f) if data.bonds is not None: # Prepare for CONECT lines. @@ -298,13 +302,14 @@ def dump_one(f: TextIO, data: IOData): for ichunk in range(len(iatoms1) // 4 + 1): other_atoms_str = "".join( "{:5d}".format(iatom1 + 1) - for iatom1 in iatoms1[ichunk * 4:ichunk * 4 + 4]) + for iatom1 in iatoms1[ichunk * 4 : ichunk * 4 + 4] + ) conect_line = f"CONECT{iatom0 + 1:5d}{other_atoms_str}" print(conect_line, file=f) print("END", file=f) -@document_dump_many("PDB", ['atcoords', 'atnums', 'extra'], ['atffparams', 'title']) +@document_dump_many("PDB", ["atcoords", "atnums", "extra"], ["atffparams", "title"]) def dump_many(f: TextIO, datas: Iterator[IOData]): """Do not edit this docstring. It will be overwritten.""" # Similar to load_many, this is relatively easy. diff --git a/iodata/formats/poscar.py b/iodata/formats/poscar.py index a2f59806..014553aa 100644 --- a/iodata/formats/poscar.py +++ b/iodata/formats/poscar.py @@ -22,7 +22,6 @@ `VESTA `_. """ - from typing import TextIO import numpy as np @@ -37,41 +36,41 @@ __all__ = [] -PATTERNS = ['POSCAR*'] +PATTERNS = ["POSCAR*"] -@document_load_one("VASP 5 POSCAR", ['atcoords', 'atnums', 'cellvecs', 'title']) +@document_load_one("VASP 5 POSCAR", ["atcoords", "atnums", "cellvecs", "title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # Load header title, cellvecs, atnums, atcoords = _load_vasp_header(lit) return { - 'title': title, - 'atcoords': atcoords, - 'atnums': atnums, - 'cellvecs': cellvecs, + "title": title, + "atcoords": atcoords, + "atnums": atnums, + "cellvecs": cellvecs, } -@document_dump_one("VASP 5 POSCAR", ['atcoords', 'atnums', 'cellvecs'], ['title']) +@document_dump_one("VASP 5 POSCAR", ["atcoords", "atnums", "cellvecs"], ["title"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" - print(data.title or 'Created with IOData', file=f) - print(' 1.00000000000000', file=f) + print(data.title or "Created with IOData", file=f) + print(" 1.00000000000000", file=f) # Write cell vectors, each row is one vector in angstrom: cellvecs = data.cellvecs for rvec in cellvecs: r = rvec / angstrom - print(f'{r[0]: 21.16f} {r[1]: 21.16f} {r[2]: 21.16f}', file=f) + print(f"{r[0]: 21.16f} {r[1]: 21.16f} {r[2]: 21.16f}", file=f) # Construct list of elements to make sure the coordinates get written # in this order. Heaviest elements are put furst. uatnums = sorted(np.unique(data.atnums))[::-1] - print(' '.join(f'{num2sym[uatnum]:5s}' for uatnum in uatnums), file=f) - print(' '.join(f'{(data.atnums == uatnum).sum():5d}' for uatnum in uatnums), file=f) - print('Selective dynamics', file=f) - print('Direct', file=f) + print(" ".join(f"{num2sym[uatnum]:5s}" for uatnum in uatnums), file=f) + print(" ".join(f"{(data.atnums == uatnum).sum():5d}" for uatnum in uatnums), file=f) + print("Selective dynamics", file=f) + print("Direct", file=f) # Write the coordinates gvecs = np.linalg.inv(data.cellvecs).T @@ -79,4 +78,4 @@ def dump_one(f: TextIO, data: IOData): indexes = (data.atnums == uatnum).nonzero()[0] for index in indexes: row = np.dot(gvecs, data.atcoords[index]) - print(f' {row[0]: 21.16f} {row[1]: 21.16f} {row[2]: 21.16f} F F F', file=f) + print(f" {row[0]: 21.16f} {row[1]: 21.16f} {row[2]: 21.16f} F F F", file=f) diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 58af4cdb..0ced6c79 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -32,20 +32,41 @@ __all__ = [] -PATTERNS = ['*.qchemlog'] - - -@document_load_one("qchemlog", - ['atcoords', 'atmasses', 'atnums', 'energy', 'g_rot', 'mo', - 'lot', 'obasis_name', 'run_type', 'extra'], - ['athessian']) +PATTERNS = ["*.qchemlog"] + + +@document_load_one( + "qchemlog", + [ + "atcoords", + "atmasses", + "atnums", + "energy", + "g_rot", + "mo", + "lot", + "obasis_name", + "run_type", + "extra", + ], + ["athessian"], +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" data = load_qchemlog_low(lit) # add these labels if they are loaded - result_labels = ['atcoords', 'atmasses', 'atnums', 'energy', 'g_rot', - 'run_type', 'athessian', 'lot', 'obasis_name'] + result_labels = [ + "atcoords", + "atmasses", + "atnums", + "energy", + "g_rot", + "run_type", + "athessian", + "lot", + "obasis_name", + ] result = {label: data[label] for label in result_labels if data.get(label) is not None} # mulliken charges @@ -54,67 +75,76 @@ def load_one(lit: LineIterator) -> dict: # build molecular orbitals # ------------------------ - if data['unrestricted']: + if data["unrestricted"]: # unrestricted case - mo_energies = np.concatenate((data['mo_a_occ'], data['mo_a_vir'], - data['mo_b_occ'], data['mo_b_vir']), axis=0) - mo_coeffs = np.full((data['nbasis'], data['norba'] + data['norbb']), np.nan) + mo_energies = np.concatenate( + (data["mo_a_occ"], data["mo_a_vir"], data["mo_b_occ"], data["mo_b_vir"]), axis=0 + ) + mo_coeffs = np.full((data["nbasis"], data["norba"] + data["norbb"]), np.nan) mo_occs = np.zeros(mo_coeffs.shape[1]) # number of alpha & beta electrons and number of alpha molecular orbitals - na, nb = data['alpha_elec'], data['beta_elec'] - na_mo = len(data['mo_a_occ']) + len(data['mo_a_vir']) + na, nb = data["alpha_elec"], data["beta_elec"] + na_mo = len(data["mo_a_occ"]) + len(data["mo_a_vir"]) mo_occs[:na] = 1.0 - mo_occs[na_mo: na_mo + nb] = 1.0 - mo = MolecularOrbitals("unrestricted", data['norba'], data['norbb'], - mo_occs, mo_coeffs, mo_energies, None) + mo_occs[na_mo : na_mo + nb] = 1.0 + mo = MolecularOrbitals( + "unrestricted", data["norba"], data["norbb"], mo_occs, mo_coeffs, mo_energies, None + ) else: # restricted case - mo_energies = np.concatenate((data['mo_a_occ'], data['mo_a_vir']), axis=0) - mo_coeffs = np.full((data['nbasis'], data['norba']), np.nan) + mo_energies = np.concatenate((data["mo_a_occ"], data["mo_a_vir"]), axis=0) + mo_coeffs = np.full((data["nbasis"], data["norba"]), np.nan) mo_occs = np.zeros(mo_coeffs.shape[1]) - mo_occs[:data['alpha_elec']] = 1.0 - mo_occs[:data['beta_elec']] += 1.0 - mo = MolecularOrbitals("restricted", data['norba'], data['norba'], - mo_occs, mo_coeffs, mo_energies, None) - result['mo'] = mo + mo_occs[: data["alpha_elec"]] = 1.0 + mo_occs[: data["beta_elec"]] += 1.0 + mo = MolecularOrbitals( + "restricted", data["norba"], data["norba"], mo_occs, mo_coeffs, mo_energies, None + ) + result["mo"] = mo # moments moments = {} - if 'dipole' in data: - moments[(1, 'c')] = data['dipole'] - if 'quadrupole' in data: + if "dipole" in data: + moments[(1, "c")] = data["dipole"] + if "quadrupole" in data: # Convert to alphabetical ordering: xx, xy, xz, yy, yz, zz - moments[(2, 'c')] = data['quadrupole'][[0, 1, 3, 2, 4, 5]] + moments[(2, "c")] = data["quadrupole"][[0, 1, 3, 2, 4, 5]] # check total dipole parsed - if 'dipole_tol' in data and 'dipole' in data: - assert abs(np.linalg.norm(data['dipole']) - data['dipole_tol']) < 1.0e-4 + if "dipole_tol" in data and "dipole" in data: + assert abs(np.linalg.norm(data["dipole"]) - data["dipole_tol"]) < 1.0e-4 if moments: - result['moments'] = moments + result["moments"] = moments # extra dictionary # ---------------- # add labels to extra dictionary if they are loaded - extra_labels = ['nuclear_repulsion_energy', 'polarizability_tensor', 'imaginary_freq', - 'vib_energy', 'eda2', 'frags'] + extra_labels = [ + "nuclear_repulsion_energy", + "polarizability_tensor", + "imaginary_freq", + "vib_energy", + "eda2", + "frags", + ] extra = {label: data[label] for label in extra_labels if data.get(label) is not None} # if present, convert vibrational energy from kcal/mol to "atomic units + K" - if 'vib_energy' in extra: - extra['vib_energy'] *= kcalmol + if "vib_energy" in extra: + extra["vib_energy"] *= kcalmol # if present, convert enthalpy terms from kcal/mol to "atomic units + K" - if 'enthalpy_dict' in data: - extra['enthalpy_dict'] = {k: v * kcalmol for k, v in data['enthalpy_dict'].items()} + if "enthalpy_dict" in data: + extra["enthalpy_dict"] = {k: v * kcalmol for k, v in data["enthalpy_dict"].items()} # if present, convert entropy terms from cal/mol.K to "atomic units + Kalvin" - if 'entropy_dict' in data: - extra['entropy_dict'] = {k: v * calmol for k, v in data['entropy_dict'].items()} + if "entropy_dict" in data: + extra["entropy_dict"] = {k: v * calmol for k, v in data["entropy_dict"].items()} # if present, convert eda terms from kj/mol to atomic units - if 'eda2' in data: - extra['eda2'] = {k: v * kjmol for k, v in data['eda2'].items()} + if "eda2" in data: + extra["eda2"] = {k: v * kjmol for k, v in data["eda2"].items()} - result['extra'] = extra + result["extra"] = extra return result @@ -129,72 +159,72 @@ def load_qchemlog_low(lit: LineIterator) -> dict: # pylint: disable=too-many-br break # job specifications - if line.startswith('$rem') and 'run_type' not in data: + if line.startswith("$rem") and "run_type" not in data: data.update(_helper_rem_job(lit)) # standard nuclear orientation (make sure multi-step jobs does not over-write this) - elif line.startswith('Standard Nuclear Orientation (Angstroms)') and 'atcoords' not in data: + elif line.startswith("Standard Nuclear Orientation (Angstroms)") and "atcoords" not in data: data.update(_helper_structure(lit)) # standard nuclear orientation for fragments in EDA jobs - elif line.startswith('Standard Nuclear Orientation (Angstroms)'): - if 'frags' not in data: - data['frags'] = [] - data['frags'].append(_helper_structure(lit)) + elif line.startswith("Standard Nuclear Orientation (Angstroms)"): + if "frags" not in data: + data["frags"] = [] + data["frags"].append(_helper_structure(lit)) # energy (the last energy in a multi-step job) - elif line.startswith('Total energy in the final basis set'): - data['energy'] = float(line.split()[-1]) - elif line.startswith('the SCF tolerance is set'): - data['energy'] = _helper_energy(lit) + elif line.startswith("Total energy in the final basis set"): + data["energy"] = float(line.split()[-1]) + elif line.startswith("the SCF tolerance is set"): + data["energy"] = _helper_energy(lit) # orbital energies (the last orbital energies in a multi-step job) - elif line.startswith('Orbital Energies (a.u.)') and not data['unrestricted']: + elif line.startswith("Orbital Energies (a.u.)") and not data["unrestricted"]: result = _helper_orbital_energies_restricted(lit) - data['mo_a_occ'], data['mo_a_vir'] = result + data["mo_a_occ"], data["mo_a_vir"] = result # compute number of alpha - data['norba'] = len(data['mo_a_occ']) + len(data['mo_a_vir']) + data["norba"] = len(data["mo_a_occ"]) + len(data["mo_a_vir"]) # orbital energies (the last orbital energies in a multi-step job) - elif line.startswith('Orbital Energies (a.u.)') and data['unrestricted']: + elif line.startswith("Orbital Energies (a.u.)") and data["unrestricted"]: data.update(_helper_orbital_energies_unrestricted(lit)) # compute number of alpha and beta molecular orbitals - data['norba'] = len(data['mo_a_occ']) + len(data['mo_a_vir']) - data['norbb'] = len(data['mo_b_occ']) + len(data['mo_b_vir']) + data["norba"] = len(data["mo_a_occ"]) + len(data["mo_a_vir"]) + data["norbb"] = len(data["mo_b_occ"]) + len(data["mo_b_vir"]) # mulliken charges (the last charges in a multi-step job) - elif line.startswith('Ground-State Mulliken Net Atomic Charges'): - data['mulliken_charges'] = _helper_mulliken(lit) + elif line.startswith("Ground-State Mulliken Net Atomic Charges"): + data["mulliken_charges"] = _helper_mulliken(lit) # cartesian multipole moments (the last mutipole moments in a multi-step job) - elif line.startswith('Cartesian Multipole Moments'): - data['dipole'], data['quadrupole'], data['dipole_tol'] = _helper_dipole_moments(lit) + elif line.startswith("Cartesian Multipole Moments"): + data["dipole"], data["quadrupole"], data["dipole_tol"] = _helper_dipole_moments(lit) # polarizability matrix - elif line.startswith('Polarizability Matrix (a.u.)'): - data['polarizability_tensor'] = _helper_polar(lit) + elif line.startswith("Polarizability Matrix (a.u.)"): + data["polarizability_tensor"] = _helper_polar(lit) # hessian matrix - elif line.startswith('Hessian of the SCF Energy'): - data['athessian'] = _helper_hessian(lit, len(data['atnums'])) + elif line.startswith("Hessian of the SCF Energy"): + data["athessian"] = _helper_hessian(lit, len(data["atnums"])) # vibrational analysis - elif line.startswith('** VIBRATIONAL ANALYSIS'): - data['imaginary_freq'], data['vib_energy'], data['atmasses'] = _helper_vibrational(lit) + elif line.startswith("** VIBRATIONAL ANALYSIS"): + data["imaginary_freq"], data["vib_energy"], data["atmasses"] = _helper_vibrational(lit) # rotational symmetry number - elif line.startswith('Rotational Symmetry Number'): - data['g_rot'] = int(line.split()[-1]) - data['enthalpy_dict'], data['entropy_dict'] = _helper_thermo(lit) + elif line.startswith("Rotational Symmetry Number"): + data["g_rot"] = int(line.split()[-1]) + data["enthalpy_dict"], data["entropy_dict"] = _helper_thermo(lit) # energy decomposition analysis 2 (EDA2) - elif line.startswith('Results of EDA2'): + elif line.startswith("Results of EDA2"): eda2 = _helper_eda2(lit) # add fragment energies to frags - energies = eda2.pop('energies') + energies = eda2.pop("energies") for index, energy in enumerate(energies): - data['frags'][index]['energy'] = energy - data['eda2'] = eda2 + data["frags"][index]["energy"] = energy + data["eda2"] = eda2 return data @@ -203,20 +233,20 @@ def _helper_rem_job(lit: LineIterator) -> Tuple: """Load job specifications from Q-Chem output file format.""" data_rem = {} for line in lit: - if line.strip() == '$end': + if line.strip() == "$end": break line = line.strip() # parse job type section; some sections might not be available - if line.lower().startswith('jobtype'): - data_rem['run_type'] = line.split()[1].lower() - elif line.lower().startswith('method'): - data_rem['lot'] = line.split()[1].lower() - elif line.lower().startswith('unrestricted'): - data_rem['unrestricted'] = bool(strtobool(line.split()[1])) - elif line.split()[0].lower() == 'basis': - data_rem['obasis_name'] = line.split()[1].lower() - elif line.lower().startswith('symmetry'): - data_rem['symm'] = bool(strtobool(line.split()[1])) + if line.lower().startswith("jobtype"): + data_rem["run_type"] = line.split()[1].lower() + elif line.lower().startswith("method"): + data_rem["lot"] = line.split()[1].lower() + elif line.lower().startswith("unrestricted"): + data_rem["unrestricted"] = bool(strtobool(line.split()[1])) + elif line.split()[0].lower() == "basis": + data_rem["obasis_name"] = line.split()[1].lower() + elif line.lower().startswith("symmetry"): + data_rem["symm"] = bool(strtobool(line.split()[1])) return data_rem @@ -228,13 +258,15 @@ def _helper_structure(lit: LineIterator): atsymbols = [] atcoords = [] for line in lit: - if line.strip().startswith('-------------'): + if line.strip().startswith("-------------"): break atsymbols.append(line.split()[1]) atcoords.append([float(i) for i in line.split()[2:]]) - subdata = {"atnums": np.array([sym2num[i] for i in atsymbols]), - "atcoords": np.array(atcoords) * angstrom, - "nuclear_repulsion_energy": float(next(lit).split()[-2])} + subdata = { + "atnums": np.array([sym2num[i] for i in atsymbols]), + "atcoords": np.array(atcoords) * angstrom, + "nuclear_repulsion_energy": float(next(lit).split()[-2]), + } # number of alpha and beta elections line = next(lit).split() subdata["alpha_elec"] = int(line[2]) @@ -248,7 +280,7 @@ def _helper_structure(lit: LineIterator): def _helper_energy(lit: LineIterator): for line in lit: - if line.strip().endswith('Convergence criterion met'): + if line.strip().endswith("Convergence criterion met"): energy = float(line.split()[1]) break return energy @@ -257,9 +289,9 @@ def _helper_energy(lit: LineIterator): def _helper_orbital_energies_restricted(lit: LineIterator) -> Tuple: """Load occupied and virtual orbital energies for restricted calculation.""" # alpha occupied MOs - mo_a_occupied = _helper_section('-- Occupied --', '-- Virtual --', lit, backward=True) + mo_a_occupied = _helper_section("-- Occupied --", "-- Virtual --", lit, backward=True) # alpha unoccupied MOs - mo_a_unoccupied = _helper_section('-- Virtual --', '-' * 62, lit, backward=False) + mo_a_unoccupied = _helper_section("-- Virtual --", "-" * 62, lit, backward=False) return mo_a_occupied, mo_a_unoccupied @@ -267,13 +299,13 @@ def _helper_orbital_energies_unrestricted(lit: LineIterator) -> Tuple: """Load occupied and virtual orbital energies for unrestricted calculation.""" subdata = {} # alpha occupied MOs - subdata['mo_a_occ'] = _helper_section('-- Occupied --', '-- Virtual --', lit, backward=True) + subdata["mo_a_occ"] = _helper_section("-- Occupied --", "-- Virtual --", lit, backward=True) # alpha unoccupied MOs - subdata['mo_a_vir'] = _helper_section('-- Virtual --', '', lit, backward=False) + subdata["mo_a_vir"] = _helper_section("-- Virtual --", "", lit, backward=False) # beta occupied MOs - subdata['mo_b_occ'] = _helper_section('-- Occupied --', '-- Virtual --', lit, backward=True) + subdata["mo_b_occ"] = _helper_section("-- Occupied --", "-- Virtual --", lit, backward=True) # beta unoccupied MOs - subdata['mo_b_vir'] = _helper_section('-- Virtual --', '-' * 62, lit, backward=False) + subdata["mo_b_vir"] = _helper_section("-- Virtual --", "-" * 62, lit, backward=False) return subdata @@ -299,12 +331,12 @@ def _helper_mulliken(lit: LineIterator) -> np.ndarray: # skip line between 'Ground-State Mulliken Net Atomic Charges' line & atomic charge entries while True: line = next(lit).strip() - if line.startswith('------'): + if line.startswith("------"): break # store atomic charges until enf of table is reached mulliken_charges = [] for line in lit: - if line.strip().startswith('--------'): + if line.strip().startswith("--------"): break mulliken_charges.append(line.split()[2]) return np.array(mulliken_charges, dtype=float) @@ -313,7 +345,7 @@ def _helper_mulliken(lit: LineIterator) -> np.ndarray: def _helper_dipole_moments(lit: LineIterator) -> Tuple: """Load cartesian multiple moments.""" for line in lit: - if line.strip().startswith('Dipole Moment (Debye)'): + if line.strip().startswith("Dipole Moment (Debye)"): break # parse dipole moment (only load the float numbers) dipole = next(lit).split() @@ -333,7 +365,7 @@ def _helper_polar(lit: LineIterator) -> np.ndarray: next(lit) polarizability_tensor = [] for line in lit: - if line.strip().startswith('Calculating analytic Hessian'): + if line.strip().startswith("Calculating analytic Hessian"): break polarizability_tensor.append(line.split()[1:]) return np.array(polarizability_tensor, dtype=float) @@ -345,21 +377,21 @@ def _helper_hessian(lit: LineIterator, natom: int) -> np.ndarray: col_idx = [int(i) for i in next(lit).split()] hessian = np.empty((natom * 3, natom * 3), dtype=object) for line in lit: - if line.strip().startswith('****************'): + if line.strip().startswith("****************"): break - if line.startswith(' '): + if line.startswith(" "): col_idx = [int(i) for i in line.split()] else: line_list = line.split() row_idx = int(line_list[0]) - 1 - hessian[row_idx, col_idx[0] - 1:col_idx[-1]] = line_list[1:] + hessian[row_idx, col_idx[0] - 1 : col_idx[-1]] = line_list[1:] return hessian.astype(float) def _helper_vibrational(lit: LineIterator) -> Tuple: """Load vibrational analysis.""" for line in lit: - if line.strip().startswith('This Molecule has'): + if line.strip().startswith("This Molecule has"): break # pylint: disable= W0631 imaginary_freq = int(line.split()[3]) @@ -367,7 +399,7 @@ def _helper_vibrational(lit: LineIterator) -> Tuple: next(lit) atmasses = [] for line in lit: - if line.strip().startswith('Molecular Mass:'): + if line.strip().startswith("Molecular Mass:"): break atmasses.append(line.split()[-1]) atmasses = np.array(atmasses, dtype=float) @@ -380,24 +412,24 @@ def _helper_thermo(lit: LineIterator) -> Tuple: entropy_dict = {} for line in lit: line_str = line.strip() - if line_str.startswith('Archival summary:'): + if line_str.startswith("Archival summary:"): break - if line_str.startswith('Translational Enthalpy'): - enthalpy_dict['trans_enthalpy'] = float(line_str.split()[-2]) - elif line_str.startswith('Rotational Enthalpy'): - enthalpy_dict['rot_enthalpy'] = float(line_str.split()[-2]) - elif line_str.startswith('Vibrational Enthalpy'): - enthalpy_dict['vib_enthalpy'] = float(line_str.split()[-2]) - elif line_str.startswith('Total Enthalpy'): - enthalpy_dict['enthalpy_total'] = float(line_str.split()[-2]) - elif line_str.startswith('Translational Entropy'): - entropy_dict['trans_entropy'] = float(line_str.split()[-2]) - elif line_str.startswith('Rotational Entropy'): - entropy_dict['rot_entropy'] = float(line_str.split()[-2]) - elif line_str.startswith('Vibrational Entropy'): - entropy_dict['vib_entropy'] = float(line_str.split()[-2]) - elif line_str.startswith('Total Entropy'): - entropy_dict['entropy_total'] = float(line_str.split()[-2]) + if line_str.startswith("Translational Enthalpy"): + enthalpy_dict["trans_enthalpy"] = float(line_str.split()[-2]) + elif line_str.startswith("Rotational Enthalpy"): + enthalpy_dict["rot_enthalpy"] = float(line_str.split()[-2]) + elif line_str.startswith("Vibrational Enthalpy"): + enthalpy_dict["vib_enthalpy"] = float(line_str.split()[-2]) + elif line_str.startswith("Total Enthalpy"): + enthalpy_dict["enthalpy_total"] = float(line_str.split()[-2]) + elif line_str.startswith("Translational Entropy"): + entropy_dict["trans_entropy"] = float(line_str.split()[-2]) + elif line_str.startswith("Rotational Entropy"): + entropy_dict["rot_entropy"] = float(line_str.split()[-2]) + elif line_str.startswith("Vibrational Entropy"): + entropy_dict["vib_entropy"] = float(line_str.split()[-2]) + elif line_str.startswith("Total Entropy"): + entropy_dict["entropy_total"] = float(line_str.split()[-2]) return enthalpy_dict, entropy_dict @@ -406,56 +438,55 @@ def _helper_eda2(lit: LineIterator) -> dict: # pylint: disable=too-many-branche next(lit) eda2 = {} for line in lit: - - if line.strip().startswith('Fragment Energies'): + if line.strip().startswith("Fragment Energies"): for line_2 in lit: - if line_2.strip().startswith('-----'): + if line_2.strip().startswith("-----"): break - eda2.setdefault('energies', []).append(float(line_2.split()[-1])) + eda2.setdefault("energies", []).append(float(line_2.split()[-1])) - if line.strip().startswith('Orthogonal Fragment Subspace Decomposition'): + if line.strip().startswith("Orthogonal Fragment Subspace Decomposition"): next(lit) for line_2 in lit: - if line_2.strip().startswith('-----'): + if line_2.strip().startswith("-----"): break info = line_2.split() - if info[0] in ['E_elec', 'E_pauli', 'E_disp']: + if info[0] in ["E_elec", "E_pauli", "E_disp"]: eda2[info[0].lower()] = float(info[-1]) - elif line.strip().startswith('Terms summing to E_pauli'): + elif line.strip().startswith("Terms summing to E_pauli"): next(lit) for line_2 in lit: - if line_2.strip().startswith('-----'): + if line_2.strip().startswith("-----"): break info = line_2.split() - if info[0] in ['E_kep_pauli', 'E_disp_free_pauli']: + if info[0] in ["E_kep_pauli", "E_disp_free_pauli"]: eda2[info[0].lower()] = float(info[-1]) - elif line.strip().startswith('Classical Frozen Decomposition'): + elif line.strip().startswith("Classical Frozen Decomposition"): next(lit) for line_2 in lit: - if line_2.strip().startswith('-----'): + if line_2.strip().startswith("-----"): break info = line_2.split() - if info[0] in ['E_cls_elec', 'E_cls_pauli']: + if info[0] in ["E_cls_elec", "E_cls_pauli"]: eda2[info[0].lower()] = float(info[5]) - elif info[0].split("[")[1] == 'E_mod_pauli': + elif info[0].split("[")[1] == "E_mod_pauli": eda2[info[0].split("[")[1].lower()] = float(info[5]) - elif line.strip().startswith('Simplified EDA Summary'): + elif line.strip().startswith("Simplified EDA Summary"): next(lit) for line_2 in lit: - if line_2.strip().startswith('-----'): + if line_2.strip().startswith("-----"): break info = line_2.split() - if info[0] in ['PREPARATION', 'FROZEN', 'DISPERSION', 'POLARIZATION', 'TOTAL']: + if info[0] in ["PREPARATION", "FROZEN", "DISPERSION", "POLARIZATION", "TOTAL"]: eda2[info[0].lower()] = float(info[1]) - elif info[0].split("[")[-1] == 'PAULI': + elif info[0].split("[")[-1] == "PAULI": eda2[info[0].split("[")[-1].lower()] = float(info[1].split("]")[0]) - elif info[0] == 'CHARGE': - eda2[info[0].lower() + ' ' + info[1].lower()] = float(info[2]) + elif info[0] == "CHARGE": + eda2[info[0].lower() + " " + info[1].lower()] = float(info[2]) - elif line.strip().startswith('-------------------------------------------------------'): + elif line.strip().startswith("-------------------------------------------------------"): break return eda2 diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index 8e4d3b73..9eb5e2a9 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -29,13 +29,16 @@ https://en.wikipedia.org/wiki/Chemical_table_file """ - from typing import TextIO, Iterator import numpy as np -from ..docstrings import (document_load_one, document_load_many, document_dump_one, - document_dump_many) +from ..docstrings import ( + document_load_one, + document_load_many, + document_dump_one, + document_dump_many, +) from ..iodata import IOData from ..periodic import sym2num, num2sym from ..utils import angstrom, LineIterator @@ -44,10 +47,10 @@ __all__ = [] -PATTERNS = ['*.sdf'] +PATTERNS = ["*.sdf"] -@document_load_one("SDF", ['atcoords', 'atnums', 'bonds', 'title']) +@document_load_one("SDF", ["atcoords", "atnums", "bonds", "title"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" title = next(lit).strip() @@ -84,14 +87,14 @@ def load_one(lit: LineIterator) -> dict: if words == "$$$$\n": break return { - 'title': title, - 'atcoords': atcoords, - 'atnums': atnums, - 'bonds': bonds, + "title": title, + "atcoords": atcoords, + "atnums": atnums, + "bonds": bonds, } -@document_load_many("SDF", ['atcoords', 'atnums', 'bonds', 'title']) +@document_load_many("SDF", ["atcoords", "atnums", "bonds", "title"]) def load_many(lit: LineIterator) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # SDF files with more molecules are a simple concatenation of individual SDF files,' @@ -103,28 +106,26 @@ def load_many(lit: LineIterator) -> Iterator[dict]: return -@document_dump_one("SDF", ['atcoords', 'atnums'], ['title', 'bonds']) +@document_dump_one("SDF", ["atcoords", "atnums"], ["title", "bonds"]) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" - print(data.title or 'Created with IOData', file=f) - print('', file=f) - print('', file=f) + print(data.title or "Created with IOData", file=f) + print("", file=f) + print("", file=f) nbond = 0 if data.bonds is None else len(data.bonds) print("{:3d}{:3d} 0 0 0 0 0 0 0999 V2000".format(data.natom, nbond), file=f) for iatom in range(data.natom): n = num2sym[data.atnums[iatom]] x, y, z = data.atcoords[iatom] / angstrom - print(f'{x:10.4f}{y:10.4f}{z:10.4f} {n:<3s} 0 0 0 0 0 0 0 0 0 0 0 0', file=f) + print(f"{x:10.4f}{y:10.4f}{z:10.4f} {n:<3s} 0 0 0 0 0 0 0 0 0 0 0 0", file=f) if data.bonds is not None: for iatom, jatom, bondtype in data.bonds: - print('{:3d}{:3d}{:3d} 0 0 0 0'.format( - iatom + 1, jatom + 1, bondtype - ), file=f) - print('M END', file=f) - print('$$$$', file=f) + print("{:3d}{:3d}{:3d} 0 0 0 0".format(iatom + 1, jatom + 1, bondtype), file=f) + print("M END", file=f) + print("$$$$", file=f) -@document_dump_many("SDF", ['atcoords', 'atnums'], ['title', 'bonds']) +@document_dump_many("SDF", ["atcoords", "atnums"], ["title", "bonds"]) def dump_many(f: TextIO, datas: Iterator[IOData]): """Do not edit this docstring. It will be overwritten.""" # Similar to load_many, this is relatively easy. diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 88e5146e..2a5036ea 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -25,7 +25,6 @@ Gaussian functions. """ - from typing import List, TextIO, Tuple import numpy as np @@ -42,7 +41,7 @@ __all__ = [] -PATTERNS = ['*.wfn'] +PATTERNS = ["*.wfn"] # From the AIMALL documentation @@ -120,13 +119,13 @@ # Definition of primitives in the WFN format. This is the order of the primitive # types as documented by aimall, used in the field TYPE ASSIGNMENTS. -PRIMITIVE_NAMES = sum([CONVENTIONS[(angmom, 'c')] for angmom in range(6)], []) +PRIMITIVE_NAMES = sum([CONVENTIONS[(angmom, "c")] for angmom in range(6)], []) def _load_helper_num(lit: LineIterator) -> List[int]: """Read number of orbitals, primitives and atoms.""" line = next(lit) - if not line.startswith('G'): + if not line.startswith("G"): lit.error("Expecting line to start with 'G'") # FORMAT (16X,I7,13X,I7,11X,I9) num_mo = int(line[16:23]) @@ -154,8 +153,9 @@ def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> Tuple[np.ndarray, n return atnums, atcoords -def _load_helper_section(lit: LineIterator, n: int, start: str, skip: int, step: int, - dtype: np.dtype) -> np.ndarray: +def _load_helper_section( + lit: LineIterator, n: int, start: str, skip: int, step: int, dtype: np.dtype +) -> np.ndarray: """Read CENTRE ASSIGNMENTS, TYPE ASSIGNMENTS, and EXPONENTS sections.""" section = [] while len(section) < n: @@ -164,7 +164,7 @@ def _load_helper_section(lit: LineIterator, n: int, start: str, skip: int, step: lit.error(f"Expecting line to start with '{start}'") line = line[skip:] while len(line) >= step: - section.append(dtype(line[:step].replace('D', 'E'))) + section.append(dtype(line[:step].replace("D", "E"))) line = line[step:] if len(section) != n: lit.error("Number of elements in section do not match 'n'") @@ -174,21 +174,21 @@ def _load_helper_section(lit: LineIterator, n: int, start: str, skip: int, step: def _load_helper_mo(lit: LineIterator, nprim: int) -> Tuple[int, float, float, np.ndarray]: """Read one section of MO information.""" line = next(lit) - if not line.startswith('MO'): + if not line.startswith("MO"): lit.error("Expecting line to start with 'MO'") # FORMAT (2X,I5,27X,F13.7,15X,F12.6) number = int(line[2:7]) occ = float(line[34:47]) energy = float(line[62:74]) # FORMAT (5E16.8) - coeffs = _load_helper_section(lit, nprim, '', 0, 16, float) + coeffs = _load_helper_section(lit, nprim, "", 0, 16, float) return number, occ, energy, coeffs def _load_helper_energy(lit: LineIterator) -> float: """Read energy.""" line = next(lit) - while 'ENERGY' not in line and line is not None: + while "ENERGY" not in line and line is not None: line = next(lit) # FORMAT (17X,F20.12) # Note: this differs between *.WFN files -- in some files the energy field ends at @@ -203,7 +203,7 @@ def _load_helper_multiwfn(lit: LineIterator, num_mo: int) -> np.ndarray: for line in lit: if "$MOSPIN $END" in line: # FORMAT (40I2) - return _load_helper_section(lit, num_mo, '', 0, 2, int) + return _load_helper_section(lit, num_mo, "", 0, 2, int) return np.empty((0,), dtype=int) @@ -221,30 +221,43 @@ def load_wfn_low(lit: LineIterator) -> Tuple: num_mo, nprim, num_atoms = _load_helper_num(lit) atnums, atcoords = _load_helper_atoms(lit, num_atoms) # centers are indexed from zero in HORTON - icenters = _load_helper_section(lit, nprim, 'CENTRE ASSIGNMENTS', 20, 3, int) - 1 + icenters = _load_helper_section(lit, nprim, "CENTRE ASSIGNMENTS", 20, 3, int) - 1 # The type assignments are integer indices for individual basis functions, # while in IOData, only the order within shells is fixed by configurable # conventions. In principle, the wfn format makes it possible for two # shells with the same angular momentum to have a different ordering of # the basis functions. - type_assignments = _load_helper_section(lit, nprim, 'TYPE ASSIGNMENTS', 20, 3, int) - 1 - exponent = _load_helper_section(lit, nprim, 'EXPONENTS', 10, 14, float) + type_assignments = _load_helper_section(lit, nprim, "TYPE ASSIGNMENTS", 20, 3, int) - 1 + exponent = _load_helper_section(lit, nprim, "EXPONENTS", 10, 14, float) mo_numbers = np.empty(num_mo, int) mo_occs = np.empty(num_mo, float) mo_energies = np.empty(num_mo, float) mo_coeffs = np.empty([nprim, num_mo], float) for mo in range(num_mo): - mo_numbers[mo], mo_occs[mo], mo_energies[mo], mo_coeffs[:, mo] = \ - _load_helper_mo(lit, nprim) + mo_numbers[mo], mo_occs[mo], mo_energies[mo], mo_coeffs[:, mo] = _load_helper_mo(lit, nprim) energy, virial = _load_helper_energy(lit) mo_spin = _load_helper_multiwfn(lit, num_mo) - return title, atnums, atcoords, icenters, type_assignments, exponent, \ - mo_numbers, mo_occs, mo_energies, mo_coeffs, energy, virial, mo_spin + return ( + title, + atnums, + atcoords, + icenters, + type_assignments, + exponent, + mo_numbers, + mo_occs, + mo_energies, + mo_coeffs, + energy, + virial, + mo_spin, + ) # pylint: disable=too-many-branches -def build_obasis(icenters: np.ndarray, type_assignments: np.ndarray, - exponents: np.ndarray, lit: LineIterator) -> Tuple[MolecularBasis, np.ndarray]: +def build_obasis( + icenters: np.ndarray, type_assignments: np.ndarray, exponents: np.ndarray, lit: LineIterator +) -> Tuple[MolecularBasis, np.ndarray]: """Construct a basis set using the arrays read from a WFN or WFX file. Parameters @@ -276,7 +289,7 @@ def build_obasis(icenters: np.ndarray, type_assignments: np.ndarray, # functions) can match one angular momentum. angmom = len(PRIMITIVE_NAMES[type_assignments[ibasis]]) # The number of cartesian functions for the current angular momentum - ncart = len(CONVENTIONS[(angmom, 'c')]) + ncart = len(CONVENTIONS[(angmom, "c")]) # Determine how many shells are to be read in one batch. E.g. for a # contracted p shell, the WFN format contains first all px basis # functions, the all py, finally all pz. These need to be regrouped into @@ -291,44 +304,48 @@ def build_obasis(icenters: np.ndarray, type_assignments: np.ndarray, if angmom > 0: # batches for s-type functions are not necessary and may result in # multiple centers being pulled into one batch. - while (ibasis + ncon < len(type_assignments) - and type_assignments[ibasis + ncon] == type_assignment): + while ( + ibasis + ncon < len(type_assignments) + and type_assignments[ibasis + ncon] == type_assignment + ): ncon += 1 # Check if the type assignment is consistent for remaining basis # functions in this batch. for ifn in range(ncart): - if not (type_assignments[ibasis + ncon * ifn: ibasis + ncon * (ifn + 1)] - == type_assignments[ibasis + ncon * ifn]).all(): + if not ( + type_assignments[ibasis + ncon * ifn : ibasis + ncon * (ifn + 1)] + == type_assignments[ibasis + ncon * ifn] + ).all(): lit.error("Inconcsistent type assignments in current batch of shells.") # Check if all basis functions in the current batch sit on # the same center. If not, IOData cannot read this file. icenter = icenters[ibasis] - if not (icenters[ibasis: ibasis + ncon * ncart] == icenter).all(): + if not (icenters[ibasis : ibasis + ncon * ncart] == icenter).all(): lit.error("Incomplete shells in WFN file not supported by IOData.") # Check if the same exponent is used for corresponding basis functions. - batch_exponents = exponents[ibasis: ibasis + ncon] + batch_exponents = exponents[ibasis : ibasis + ncon] for ifn in range(ncart): - if not (exponents[ibasis + ncon * ifn: ibasis + ncon * (ifn + 1)] - == batch_exponents).all(): + if not ( + exponents[ibasis + ncon * ifn : ibasis + ncon * (ifn + 1)] == batch_exponents + ).all(): lit.error("Exponents must be the same for corresponding basis functions.") # A permutation is needed because we need to regroup basis functions # into shells. batch_primitive_names = [ - PRIMITIVE_NAMES[type_assignments[ibasis + ifn * ncon]] - for ifn in range(ncart)] + PRIMITIVE_NAMES[type_assignments[ibasis + ifn * ncon]] for ifn in range(ncart) + ] for irep in range(ncon): for i, primitive_name in enumerate(batch_primitive_names): - ifn = CONVENTIONS[(angmom, 'c')].index(primitive_name) + ifn = CONVENTIONS[(angmom, "c")].index(primitive_name) permutation[ibasis + irep * ncart + ifn] = ibasis + irep + i * ncon # WFN uses non-normalized primitives, which will be corrected for # when processing the MO coefficients. Normalized primitives will # be used here. No attempt is made here to reconstruct the contraction. for exponent in batch_exponents: - shells.append(Shell(icenter, [angmom], ['c'], np.array([exponent]), - np.array([[1.0]]))) + shells.append(Shell(icenter, [angmom], ["c"], np.array([exponent]), np.array([[1.0]]))) # Move on to the next contraction ibasis += ncart * ncon - obasis = MolecularBasis(shells, CONVENTIONS, 'L2') + obasis = MolecularBasis(shells, CONVENTIONS, "L2") assert obasis.nbasis == nbasis return obasis, permutation @@ -351,26 +368,39 @@ def get_mocoeff_scales(obasis: MolecularBasis) -> np.ndarray: scales = [] for shell in obasis.shells: angmom = shell.angmoms[0] - for name in obasis.conventions[(angmom, 'c')]: - if name == '1': + for name in obasis.conventions[(angmom, "c")]: + if name == "1": nx, ny, nz = 0, 0, 0 else: - nx = name.count('x') - ny = name.count('y') - nz = name.count('z') + nx = name.count("x") + ny = name.count("y") + nz = name.count("z") scales.append(gob_cart_normalization(shell.exponents[0], np.array([nx, ny, nz]))) return np.array(scales) -@document_load_one("WFN", ['atcoords', 'atnums', 'energy', 'mo', 'obasis', 'title', 'extra']) +@document_load_one("WFN", ["atcoords", "atnums", "energy", "mo", "obasis", "title", "extra"]) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" - (title, atnums, atcoords, icenters, type_assignments, exponents, mo_numbers, mo_occs, - mo_energies, mo_coeffs, energy, virial, mo_spin) = load_wfn_low(lit) + ( + title, + atnums, + atcoords, + icenters, + type_assignments, + exponents, + mo_numbers, + mo_occs, + mo_energies, + mo_coeffs, + energy, + virial, + mo_spin, + ) = load_wfn_low(lit) # Build the basis set and the permutation needed to regroup shells. obasis, permutation = build_obasis(icenters, type_assignments, exponents, lit) # Extra dict to return. - extra = {'virial_ratio': virial} + extra = {"virial_ratio": virial} # ---------------------------- # Build the molecular orbitals # ---------------------------- @@ -386,7 +416,7 @@ def load_one(lit: LineIterator) -> dict: norb_ab = np.sum(mo_spin == 3) if norb_a + norb_b + norb_ab != norb or (norb_b and norb_ab): lit.error("Invalid orbital spin types.") - extra['mo_spin'] = mo_spin + extra["mo_spin"] = mo_spin # Determine norb_a,norb_b,norb_ab for restricted wave function by heuristic. elif mo_occs.max() > 1.0: norb_a = 0 @@ -396,10 +426,12 @@ def load_one(lit: LineIterator) -> dict: # This may still fail in some corner cases. else: norb_a = 1 - while (norb_a < mo_coeffs.shape[1] - and mo_energies[norb_a] >= mo_energies[norb_a - 1] - and mo_occs[norb_a] <= mo_occs[norb_a - 1] - and mo_numbers[norb_a] == mo_numbers[norb_a - 1] + 1): + while ( + norb_a < mo_coeffs.shape[1] + and mo_energies[norb_a] >= mo_energies[norb_a - 1] + and mo_occs[norb_a] <= mo_occs[norb_a - 1] + and mo_numbers[norb_a] == mo_numbers[norb_a - 1] + 1 + ): norb_a += 1 norb_b = norb - norb_a norb_ab = 0 @@ -407,27 +439,25 @@ def load_one(lit: LineIterator) -> dict: if norb_ab: # Restricted wavefunction. mo = MolecularOrbitals( - 'restricted', norb_a + norb_ab, norb_a + norb_ab, - mo_occs, mo_coeffs, mo_energies) + "restricted", norb_a + norb_ab, norb_a + norb_ab, mo_occs, mo_coeffs, mo_energies + ) else: # Unrestricted wavefunction. - mo = MolecularOrbitals( - 'unrestricted', norb_a, norb_b, - mo_occs, mo_coeffs, mo_energies) + mo = MolecularOrbitals("unrestricted", norb_a, norb_b, mo_occs, mo_coeffs, mo_energies) return { - 'title': title, - 'atcoords': atcoords, - 'atnums': atnums, - 'obasis': obasis, - 'mo': mo, - 'energy': energy, - 'extra': extra, + "title": title, + "atcoords": atcoords, + "atnums": atnums, + "obasis": obasis, + "mo": mo, + "energy": energy, + "extra": extra, } def _format_helper_section(header: str, skip: int, spec: str, nline: int) -> Tuple[str, int]: """Return a format string for CENTRE_ASSIGMENTS, TYPE_ASSIGNMENTS, EXPONENTS lines.""" - return f'{header[:skip].ljust(skip)}{spec * nline}', len(spec) + return f"{header[:skip].ljust(skip)}{spec * nline}", len(spec) def _dump_helper_section(f: TextIO, data: np.ndarray, fmt: str, skip: int, step: int, nline: int): @@ -435,33 +465,33 @@ def _dump_helper_section(f: TextIO, data: np.ndarray, fmt: str, skip: int, step: while len(data) > 0: chunk = data[:nline] n_chunk = len(chunk) - print(fmt[:skip + n_chunk * step].format(*chunk), file=f) + print(fmt[: skip + n_chunk * step].format(*chunk), file=f) data = data[n_chunk:] # FORMAT (16X,I7,13X,I7,11X,I9) -FMT_NUM = 'GAUSSIAN {0:7d} MOL ORBITALS{1:7d} PRIMITIVES{2:9d} NUCLEI' +FMT_NUM = "GAUSSIAN {0:7d} MOL ORBITALS{1:7d} PRIMITIVES{2:9d} NUCLEI" # FORMAT (2X,A3,I3,11X,I3,2X,3F12.8,10X,F5.1) -FMT_ATM = ' {0:3s}{1:3d} (CENTRE{2:3d}) {3:12.8f}{4:12.8f}{5:12.8f} CHARGE ={6:5.1f}' +FMT_ATM = " {0:3s}{1:3d} (CENTRE{2:3d}) {3:12.8f}{4:12.8f}{5:12.8f} CHARGE ={6:5.1f}" # FORMAT (2X,5I,8X,F3.1,16X,F13.7,15X,F12.6) -FMT_MOS = 'MO{0:5d} MO {1:3.1f} OCC NO ={2:13.7f} ORB. ENERGY ={3:12.6f}' +FMT_MOS = "MO{0:5d} MO {1:3.1f} OCC NO ={2:13.7f} ORB. ENERGY ={3:12.6f}" # FORMAT (17X,F20.12,18X,F13.8) -FMT_ENERGY = ' TOTAL ENERGY = {0:20.12f} THE VIRIAL(-V/T)={1:13.8f}' +FMT_ENERGY = " TOTAL ENERGY = {0:20.12f} THE VIRIAL(-V/T)={1:13.8f}" # FORMAT (20X,20I3) -FMT_CNTR, STEP_CNTR = _format_helper_section('CENTRE ASSIGNMENTS', 20, '{:3d}', 20) +FMT_CNTR, STEP_CNTR = _format_helper_section("CENTRE ASSIGNMENTS", 20, "{:3d}", 20) # FORMAT (20X,20I3) -FMT_TYPE, STEP_TYPE = _format_helper_section('TYPE ASSIGNMENTS', 20, '{:3d}', 20) +FMT_TYPE, STEP_TYPE = _format_helper_section("TYPE ASSIGNMENTS", 20, "{:3d}", 20) # FORMAT (10X,5E14.7) -FMT_EXPN, STEP_EXPN = _format_helper_section('EXPONENTS', 10, '{:14.7E}', 5) +FMT_EXPN, STEP_EXPN = _format_helper_section("EXPONENTS", 10, "{:14.7E}", 5) # FORMAT (5E16.8) -FMT_COEF, STEP_COEF = _format_helper_section('', 0, '{:16.8E}', 5) +FMT_COEF, STEP_COEF = _format_helper_section("", 0, "{:16.8E}", 5) # FORMAT (40I2) -FMT_SPIN, STEP_SPIN = _format_helper_section('', 0, '{:2d}', 40) +FMT_SPIN, STEP_SPIN = _format_helper_section("", 0, "{:2d}", 40) # Default .WFN title DEFAULT_WFN_TTL = "WFN auto-generated by IOData" -@document_dump_one("WFN", ['atcoords', 'atnums', 'energy', 'mo', 'obasis', 'title', 'extra']) +@document_dump_one("WFN", ["atcoords", "atnums", "energy", "mo", "obasis", "title", "extra"]) def dump_one(f: TextIO, data: IOData) -> None: """Do not edit this docstring. It will be overwritten.""" # get shells for the de-contracted basis @@ -469,10 +499,13 @@ def dump_one(f: TextIO, data: IOData) -> None: for shell in data.obasis.shells: for i, (angmom, kind) in enumerate(zip(shell.angmoms, shell.kinds)): for exponent, coeff in zip(shell.exponents, shell.coeffs.T[i]): - if kind != 'c': + if kind != "c": raise ValueError("WFN can be generated only for Cartesian MolecularBasis!") - shells.append(Shell(shell.icenter, [angmom], [kind], np.array([exponent]), - coeff.reshape(-1, 1))) + shells.append( + Shell( + shell.icenter, [angmom], [kind], np.array([exponent]), coeff.reshape(-1, 1) + ) + ) # make a new instance of MolecularBasis with de-contracted basis shells; ideally for WFN we # want the primitive basis set, but IOData only supports shells. obasis = MolecularBasis(shells, data.obasis.conventions, data.obasis.primitive_normalization) @@ -485,9 +518,9 @@ def dump_one(f: TextIO, data: IOData) -> None: for shell in data.obasis.shells: for angmom, kind in zip(shell.angmoms, shell.kinds): n = len(data.obasis.conventions[angmom, kind]) - c = raw_coeffs[index_mo_old: index_mo_old + n] + c = raw_coeffs[index_mo_old : index_mo_old + n] for _ in range(shell.nprim): - mo_coeffs[index_mo_new: index_mo_new + n] = c + mo_coeffs[index_mo_new : index_mo_new + n] = c index_mo_new += n index_mo_old += n @@ -514,12 +547,12 @@ def dump_one(f: TextIO, data: IOData) -> None: angmom_prim = {} count = 1 for angmom in range(max([shell.angmoms[0] for shell in obasis.shells]) + 1): - angmom_prim[angmom] = [count + i for i in range(len(obasis.conventions[angmom, 'c']))] - count += len(obasis.conventions[angmom, 'c']) + angmom_prim[angmom] = [count + i for i in range(len(obasis.conventions[angmom, "c"]))] + count += len(obasis.conventions[angmom, "c"]) types = [item for shell in obasis.shells for item in angmom_prim[shell.angmoms[0]]] # Write header (title, # MOs, # primitives, # atoms) - print(f' {data.title if data.title else DEFAULT_WFN_TTL}', file=f) + print(f" {data.title if data.title else DEFAULT_WFN_TTL}", file=f) print(FMT_NUM.format(data.mo.norb, obasis.nbasis, data.natom), file=f) # Write atoms (symbol, atom #, centre #, x pos., y pos., z pos., charge) @@ -539,10 +572,10 @@ def dump_one(f: TextIO, data: IOData) -> None: _dump_helper_section(f, coeffs, FMT_COEF, 0, STEP_COEF, 5) # Write energy and virial coefficient - print('END DATA', file=f) - print(FMT_ENERGY.format(data.energy or np.nan, data.extra.get('virial_ratio', np.nan)), file=f) + print("END DATA", file=f) + print(FMT_ENERGY.format(data.energy or np.nan, data.extra.get("virial_ratio", np.nan)), file=f) # Write MOSPIN extension section (optional) - if data.extra.get('mo_spin') is not None: - print(' $MOSPIN $END\n\n', file=f) - _dump_helper_section(f, data.extra['mo_spin'], FMT_SPIN, 0, STEP_SPIN, 40) + if data.extra.get("mo_spin") is not None: + print(" $MOSPIN $END\n\n", file=f) + _dump_helper_section(f, data.extra["mo_spin"], FMT_SPIN, 0, STEP_SPIN, 40) diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index c1d786d9..360a0cc6 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -37,7 +37,7 @@ __all__ = [] -PATTERNS = ['*.wfx'] +PATTERNS = ["*.wfx"] def _wfx_labels() -> tuple: @@ -46,67 +46,74 @@ def _wfx_labels() -> tuple: # section labels with string data types labels_str = { - '': 'title', - '<Keywords>': 'keywords', - '<Model>': 'model_name', + "<Title>": "title", + "<Keywords>": "keywords", + "<Model>": "model_name", } # section labels with integer number data types labels_int = { - '<Number of Nuclei>': 'num_atoms', - '<Number of Occupied Molecular Orbitals>': 'num_occ_mo', - '<Number of Perturbations>': 'num_perturbations', - '<Number of Electrons>': 'num_electrons', - '<Number of Core Electrons>': 'num_core_electrons', - '<Number of Alpha Electrons>': 'num_alpha_electron', - '<Number of Beta Electrons>': 'num_beta_electron', - '<Number of Primitives>': 'num_primitives', - '<Electronic Spin Multiplicity>': 'spin_multi', + "<Number of Nuclei>": "num_atoms", + "<Number of Occupied Molecular Orbitals>": "num_occ_mo", + "<Number of Perturbations>": "num_perturbations", + "<Number of Electrons>": "num_electrons", + "<Number of Core Electrons>": "num_core_electrons", + "<Number of Alpha Electrons>": "num_alpha_electron", + "<Number of Beta Electrons>": "num_beta_electron", + "<Number of Primitives>": "num_primitives", + "<Electronic Spin Multiplicity>": "spin_multi", } # section labels with float number data types labels_float = { - '<Net Charge>': 'charge', - '<Energy = T + Vne + Vee + Vnn>': 'energy', - '<Virial Ratio (-V/T)>': 'virial_ratio', - '<Nuclear Virial of Energy-Gradient-Based Forces on Nuclei, W>': 'nuc_viral', - '<Full Virial Ratio, -(V - W)/T>': 'full_virial_ratio', + "<Net Charge>": "charge", + "<Energy = T + Vne + Vee + Vnn>": "energy", + "<Virial Ratio (-V/T)>": "virial_ratio", + "<Nuclear Virial of Energy-Gradient-Based Forces on Nuclei, W>": "nuc_viral", + "<Full Virial Ratio, -(V - W)/T>": "full_virial_ratio", } # section labels with array of integer data types labels_array_int = { - '<Atomic Numbers>': 'atnums', - '<Primitive Centers>': 'centers', - '<Primitive Types>': 'types', - '<MO Numbers>': 'mo_numbers', # This is constructed in parse_wfx. + "<Atomic Numbers>": "atnums", + "<Primitive Centers>": "centers", + "<Primitive Types>": "types", + "<MO Numbers>": "mo_numbers", # This is constructed in parse_wfx. } # section labels with array of float data types labels_array_float = { - '<Nuclear Cartesian Coordinates>': 'atcoords', - '<Nuclear Charges>': 'nuclear_charge', - '<Primitive Exponents>': 'exponents', - '<Molecular Orbital Energies>': 'mo_energies', - '<Molecular Orbital Occupation Numbers>': 'mo_occs', - '<Molecular Orbital Primitive Coefficients>': 'mo_coeffs', + "<Nuclear Cartesian Coordinates>": "atcoords", + "<Nuclear Charges>": "nuclear_charge", + "<Primitive Exponents>": "exponents", + "<Molecular Orbital Energies>": "mo_energies", + "<Molecular Orbital Occupation Numbers>": "mo_occs", + "<Molecular Orbital Primitive Coefficients>": "mo_coeffs", } # section labels with other data types labels_other = { - '<Nuclear Names>': 'nuclear_names', - '<Molecular Orbital Spin Types>': 'mo_spins', - '<Nuclear Cartesian Energy Gradients>': 'nuclear_gradient', + "<Nuclear Names>": "nuclear_names", + "<Molecular Orbital Spin Types>": "mo_spins", + "<Nuclear Cartesian Energy Gradients>": "nuclear_gradient", } # list of tags corresponding to required sections based on WFX format specifications required_tags = list(labels_str) + list(labels_int) + list(labels_float) required_tags += list(labels_array_float) + list(labels_array_int) + list(labels_other) # remove tags corresponding to optional sections - required_tags.remove('<Model>') - required_tags.remove('<Number of Core Electrons>') - required_tags.remove('<Electronic Spin Multiplicity>') - required_tags.remove('<Atomic Numbers>') - required_tags.remove('<Full Virial Ratio, -(V - W)/T>') - required_tags.remove('<Nuclear Virial of Energy-Gradient-Based Forces on Nuclei, W>') - required_tags.remove('<Nuclear Cartesian Energy Gradients>') - - return (labels_str, labels_int, labels_float, labels_array_int, labels_array_float, - labels_other, required_tags) + required_tags.remove("<Model>") + required_tags.remove("<Number of Core Electrons>") + required_tags.remove("<Electronic Spin Multiplicity>") + required_tags.remove("<Atomic Numbers>") + required_tags.remove("<Full Virial Ratio, -(V - W)/T>") + required_tags.remove("<Nuclear Virial of Energy-Gradient-Based Forces on Nuclei, W>") + required_tags.remove("<Nuclear Cartesian Energy Gradients>") + + return ( + labels_str, + labels_int, + labels_float, + labels_array_int, + labels_array_float, + labels_other, + required_tags, + ) def load_data_wfx(lit: LineIterator) -> dict: @@ -138,19 +145,19 @@ def load_data_wfx(lit: LineIterator) -> dict: warnings.warn("Not recognized section label, skip {0}".format(key)) # reshape some arrays - result['atcoords'] = result['atcoords'].reshape(-1, 3) - result['mo_coeffs'] = result['mo_coeffs'].reshape(result['num_primitives'], -1, order='F') + result["atcoords"] = result["atcoords"].reshape(-1, 3) + result["mo_coeffs"] = result["mo_coeffs"].reshape(result["num_primitives"], -1, order="F") # process nuclear gradient, if present - if 'nuclear_gradient' in result: - gradient_mix = np.array([i.split() for i in result.pop('nuclear_gradient')]).reshape(-1, 4) + if "nuclear_gradient" in result: + gradient_mix = np.array([i.split() for i in result.pop("nuclear_gradient")]).reshape(-1, 4) gradient_atoms = gradient_mix[:, 0].astype(np.unicode_) - index = [result['nuclear_names'].index(atom) for atom in gradient_atoms] - result['atgradient'] = np.full((len(result['nuclear_names']), 3), np.nan) - result['atgradient'][index] = gradient_mix[:, 1:].astype(float) + index = [result["nuclear_names"].index(atom) for atom in gradient_atoms] + result["atgradient"] = np.full((len(result["nuclear_names"]), 3), np.nan) + result["atgradient"][index] = gradient_mix[:, 1:].astype(float) # check keywords & number of perturbations - perturbation_check = {'GTO': 0, 'GIAO': 3, 'CGST': 6} - key = result['keywords'] - num = result['num_perturbations'] + perturbation_check = {"GTO": 0, "GIAO": 3, "CGST": 6} + key = result["keywords"] + num = result["num_perturbations"] if key not in perturbation_check.keys(): lit.error(f"The keywords is {key}, but it should be either GTO, GIAO or CGST") if num != perturbation_check[key]: @@ -181,7 +188,7 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: section_end = line[:1] + "/" + line[1:] # special handling of <Molecular Orbital Primitive Coefficients> section if section_start == mo_start: - data['<MO Numbers>'] = [] + data["<MO Numbers>"] = [] # check whether line is the (correct) end of the section elif section_start is not None and line.startswith("</"): # In some cases, closing tags have a different number of spaces. 8-[ @@ -190,9 +197,9 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: # reset section_start variable to signal that section ended section_start = None # handle <MO Number> line under <Molecular Orbital Primitive Coefficients> section - elif section_start == mo_start and line == '<MO Number>': + elif section_start == mo_start and line == "<MO Number>": # add MO Number to list - data['<MO Numbers>'].append(next(lit).strip()) + data["<MO Numbers>"].append(next(lit).strip()) # skip '</MO Number>' line next(lit) # add section content to the corresponding list in data dictionary @@ -206,12 +213,13 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: if required_tags is not None: for section_tag in required_tags: if section_tag not in data.keys(): - lit.error(f'Section {section_tag} is missing from loaded WFX data.') + lit.error(f"Section {section_tag} is missing from loaded WFX data.") return data -@document_load_one("WFX", ['atcoords', 'atgradient', 'atnums', 'energy', - 'extra', 'mo', 'obasis', 'title']) +@document_load_one( + "WFX", ["atcoords", "atgradient", "atnums", "energy", "extra", "mo", "obasis", "title"] +) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" # get data contained in WFX file with the proper type & shape @@ -221,7 +229,8 @@ def load_one(lit: LineIterator) -> dict: # --------------------- # build molecular basis and permutation needed to regroup shells obasis, permutation = build_obasis( - data['centers'] - 1, data['types'] - 1, data['exponents'], lit) + data["centers"] - 1, data["types"] - 1, data["exponents"], lit + ) # Build molecular orbitals # ------------------------ @@ -233,7 +242,7 @@ def load_one(lit: LineIterator) -> dict: # the px, py, pz basis functions. These shells are used by MolecularBasis (obasis) in # constructing the basis function. If that is the case for the loaded MO coefficients from WFX, # they need to be permuted to match obasis expansion of basis set (i.e. to appear in shells). - data['mo_coeffs'] = data['mo_coeffs'][permutation] + data["mo_coeffs"] = data["mo_coeffs"][permutation] # fix normalization because the loaded expansion coefficients from WFX corresponds to # un-normalized primitives for each normalized MO (which means the primitive normalization # constants has been included in the MO coefficients). However, IOData expects normalized @@ -242,20 +251,20 @@ def load_one(lit: LineIterator) -> dict: # to expansion coefficients for normalized primitives. Here, we assume primitives are # L2-normalized (as stored in obasis.primitive_normalization) which is used in scaling MO # coefficients to be stored in MolecularOrbitals instance. - data['mo_coeffs'] /= get_mocoeff_scales(obasis).reshape(-1, 1) + data["mo_coeffs"] /= get_mocoeff_scales(obasis).reshape(-1, 1) # process mo_spins and convert it into restricted or unrestricted & count alpha/beta orbitals # we do not using the <Model> section for this because it is not guaranteed to be present # check whether restricted case with "Alpha and Beta" in mo_spins - if any("and" in word for word in data['mo_spins']): + if any("and" in word for word in data["mo_spins"]): # count number of alpha & beta molecular orbitals - norbb = data['mo_spins'].count("Alpha and Beta") - norba = norbb + data['mo_spins'].count("Alpha") + norbb = data["mo_spins"].count("Alpha and Beta") + norba = norbb + data["mo_spins"].count("Alpha") # check that mo_spin list contains no surprises - if data['mo_spins'] != ["Alpha and Beta"] * norbb + ["Alpha"] * (norba - norbb): + if data["mo_spins"] != ["Alpha and Beta"] * norbb + ["Alpha"] * (norba - norbb): lit.error("Unsupported <Molecular Orbital Spin Types> values.") - if norba != data['mo_coeffs'].shape[1]: + if norba != data["mo_coeffs"].shape[1]: lit.error("Number of orbitals inconsistent with orbital spin types.") # create molecular orbitals, which requires knowing the number of alpha and beta molecular # orbitals. These are expected to be the same for 'restricted' case, however, the number of @@ -267,46 +276,63 @@ def load_one(lit: LineIterator) -> dict: # occupation numbers to identify the spin types. IOData also has different # conventions for norba and norbb, see orbitals.py for details. mo = MolecularOrbitals( - "restricted", norba, norba, # This is not a typo! - data['mo_occs'], data['mo_coeffs'], data['mo_energies']) + "restricted", + norba, + norba, # This is not a typo! + data["mo_occs"], + data["mo_coeffs"], + data["mo_energies"], + ) # unrestricted case with "Alpha" and "Beta" in mo_spins else: - norba = data['mo_spins'].count("Alpha") - norbb = data['mo_spins'].count("Beta") + norba = data["mo_spins"].count("Alpha") + norbb = data["mo_spins"].count("Beta") # check that mo_spin list contains no surprises - if data['mo_spins'] != ["Alpha"] * norba + ["Beta"] * norbb: + if data["mo_spins"] != ["Alpha"] * norba + ["Beta"] * norbb: lit.error("Unsupported molecular orbital spin types.") # check that number of orbitals match number of MO coefficients - if norba + norbb != data['mo_coeffs'].shape[1]: + if norba + norbb != data["mo_coeffs"].shape[1]: lit.error("Number of orbitals inconsistent with orbital spin types.") # Create orbitals. For unrestricted wavefunctions, IOData uses the same # conventions as WFX. mo = MolecularOrbitals( - "unrestricted", norba, norbb, - data['mo_occs'], data['mo_coeffs'], data['mo_energies']) + "unrestricted", norba, norbb, data["mo_occs"], data["mo_coeffs"], data["mo_energies"] + ) # prepare WFX-specific data for IOData - extra_labels = ['keywords', 'model_name', 'num_perturbations', 'num_core_electrons', - 'spin_multi', 'virial_ratio', 'nuc_viral', 'full_virial_ratio', 'mo_spin'] + extra_labels = [ + "keywords", + "model_name", + "num_perturbations", + "num_core_electrons", + "spin_multi", + "virial_ratio", + "nuc_viral", + "full_virial_ratio", + "mo_spin", + ] extra = {label: data.get(label, None) for label in extra_labels} extra["permutations"] = permutation return { - 'atcoords': data['atcoords'], - 'atgradient': data.get('atgradient'), - 'atnums': data['atnums'], - 'atcorenums': data['nuclear_charge'], - 'energy': data['energy'], - 'extra': extra, - 'mo': mo, - 'obasis': obasis, - 'title': data['title'], + "atcoords": data["atcoords"], + "atgradient": data.get("atgradient"), + "atnums": data["atnums"], + "atcorenums": data["nuclear_charge"], + "energy": data["energy"], + "extra": extra, + "mo": mo, + "obasis": obasis, + "title": data["title"], } -@document_dump_one("WFX", ['atcoords', 'atnums', 'atcorenums', 'mo', 'obasis', 'charge'], - ['title', 'energy', 'spinpol', 'lot', 'atgradient', 'extra']) +@document_dump_one( + "WFX", + ["atcoords", "atnums", "atcorenums", "mo", "obasis", "charge"], + ["title", "energy", "spinpol", "lot", "atgradient", "extra"], +) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # pylint: disable=too-many-branches,too-many-statements @@ -323,10 +349,13 @@ def dump_one(f: TextIO, data: IOData): for shell in data.obasis.shells: for i, (angmom, kind) in enumerate(zip(shell.angmoms, shell.kinds)): for exponent, coeff in zip(shell.exponents, shell.coeffs.T[i]): - if kind != 'c': + if kind != "c": raise ValueError("WFX can be generated only for Cartesian MolecularBasis!") - shells.append(Shell(shell.icenter, [angmom], [kind], np.array([exponent]), - coeff.reshape(-1, 1))) + shells.append( + Shell( + shell.icenter, [angmom], [kind], np.array([exponent]), coeff.reshape(-1, 1) + ) + ) # make a new instance of MolecularBasis with de-contracted basis shells; ideally for WFX we # want the primitive basis set, but IOData only supports shells. obasis = MolecularBasis(shells, data.obasis.conventions, data.obasis.primitive_normalization) @@ -342,9 +371,9 @@ def dump_one(f: TextIO, data: IOData): for shell in data.obasis.shells: for angmom, kind in zip(shell.angmoms, shell.kinds): n = len(data.obasis.conventions[angmom, kind]) - c = raw_coeffs[index_mo_old: index_mo_old + n] + c = raw_coeffs[index_mo_old : index_mo_old + n] for j in range(shell.nprim): - mo_coeffs[index_mo_new: index_mo_new + n] = c + mo_coeffs[index_mo_new : index_mo_new + n] = c index_mo_new += n index_mo_old += n # fix MO coefficients @@ -364,7 +393,7 @@ def dump_one(f: TextIO, data: IOData): mo_coeffs[:, index] *= contractions * scales # write title & keywords - _write_xml_single(tag=lbs["title"], info=data.title or '<Created with IOData>', file=f) + _write_xml_single(tag=lbs["title"], info=data.title or "<Created with IOData>", file=f) _write_xml_single(tag=lbs["keywords"], info=data.extra.get("keywords", "GTO"), file=f) # write number of nuclei & number of primitives @@ -382,8 +411,8 @@ def dump_one(f: TextIO, data: IOData): # write nuclear names, atomic numbers, and nuclear charges # add ghost atom, represented by Bq and atomic number 0 - num2sym.update({0: 'Bq'}) - nuclear_names = [f' {num2sym[num]}{index + 1}' for index, num in enumerate(data.atcorenums)] + num2sym.update({0: "Bq"}) + nuclear_names = [f" {num2sym[num]}{index + 1}" for index, num in enumerate(data.atcorenums)] _write_xml_iterator(tag=lbs["nuclear_names"], info=nuclear_names, file=f) _write_xml_iterator(tag=lbs["atnums"], info=data.atnums, file=f) _write_xml_iterator_scientific(tag=lbs["nuclear_charge"], info=data.atcorenums, file=f) @@ -391,7 +420,7 @@ def dump_one(f: TextIO, data: IOData): # write nuclear cartesian coordinates print("<Nuclear Cartesian Coordinates>", file=f) for item in data.atcoords: - print('{: ,.14E} {: ,.14E} {: ,.14E}'.format(item[0], item[1], item[2]), file=f) + print("{: ,.14E} {: ,.14E} {: ,.14E}".format(item[0], item[1], item[2]), file=f) print("</Nuclear Cartesian Coordinates>", file=f) # write net charge, number of electrons, number of alpha electrons, and number beta electrons @@ -412,26 +441,26 @@ def dump_one(f: TextIO, data: IOData): prim_centers = [shell.icenter + 1 for shell in obasis.shells for _ in range(shell.nbasis)] print("<Primitive Centers>", file=f) for j in range(0, len(prim_centers), 10): - print(' '.join(['{:d}'.format(c) for c in prim_centers[j:j + 10]]), file=f) + print(" ".join(["{:d}".format(c) for c in prim_centers[j : j + 10]]), file=f) print("</Primitive Centers>", file=f) # write primitive types angmom_prim = {} count = 1 for angmom in range(max([shell.angmoms[0] for shell in obasis.shells]) + 1): - angmom_prim[angmom] = [count + i for i in range(len(obasis.conventions[angmom, 'c']))] - count += len(obasis.conventions[angmom, 'c']) + angmom_prim[angmom] = [count + i for i in range(len(obasis.conventions[angmom, "c"]))] + count += len(obasis.conventions[angmom, "c"]) prim_types = [item for shell in obasis.shells for item in angmom_prim[shell.angmoms[0]]] print("<Primitive Types>", file=f) for j in range(0, len(prim_types), 10): - print(' '.join(['{:d}'.format(c) for c in prim_types[j:j + 10]]), file=f) + print(" ".join(["{:d}".format(c) for c in prim_types[j : j + 10]]), file=f) print("</Primitive Types>", file=f) # write primitive exponents exponents = [shell.exponents[0] for shell in obasis.shells for _ in range(shell.nbasis)] print("<Primitive Exponents>", file=f) for j in range(0, len(exponents), 4): - print(' '.join(['{: ,.14E}'.format(e) for e in exponents[j:j + 4]]), file=f) + print(" ".join(["{: ,.14E}".format(e) for e in exponents[j : j + 4]]), file=f) print("</Primitive Exponents>", file=f) # write molecular orbital occupation numbers @@ -441,10 +470,10 @@ def dump_one(f: TextIO, data: IOData): _write_xml_iterator_scientific(tag=lbs["mo_energies"], info=data.mo.energies, file=f) # write molecular orbital spin types - if data.mo.kind == 'restricted': - mo_spin = ['Alpha and Beta '] * len(data.mo.occs) + if data.mo.kind == "restricted": + mo_spin = ["Alpha and Beta "] * len(data.mo.occs) else: - mo_spin = ['Alpha'] * len(data.mo.occsa) + ['Beta'] * len(data.mo.occsb) + mo_spin = ["Alpha"] * len(data.mo.occsa) + ["Beta"] * len(data.mo.occsb) _write_xml_iterator(tag=lbs["mo_spins"], info=mo_spin, file=f) # write MO primitive coefficients @@ -454,7 +483,7 @@ def dump_one(f: TextIO, data: IOData): print(str(mo + 1), file=f) print("</MO Number>", file=f) for j in range(0, obasis.nbasis, 4): - print(' '.join(['{: ,.14E}'.format(c) for c in mo_coeffs.T[mo][j:j + 4]]), file=f) + print(" ".join(["{: ,.14E}".format(c) for c in mo_coeffs.T[mo][j : j + 4]]), file=f) print("</Molecular Orbital Primitive Coefficients>", file=f) # write energy and virial ratio; use ' NAN' when None (not available) @@ -466,8 +495,11 @@ def dump_one(f: TextIO, data: IOData): nuc_cart_energy_grad = list(zip(nuclear_names, data.atgradient)) print("<Nuclear Cartesian Energy Gradients>", file=f) for atom in nuc_cart_energy_grad: - print(atom[0], '{: ,.14E} {: ,.14E} {: ,.14E}'.format(atom[1][0], atom[1][1], - atom[1][2]), file=f) + print( + atom[0], + "{: ,.14E} {: ,.14E} {: ,.14E}".format(atom[1][0], atom[1][1], atom[1][2]), + file=f, + ) print("</Nuclear Cartesian Energy Gradients>", file=f) # nuclear virial of energy-gradient-based forces on nuclei (optional) @@ -487,14 +519,14 @@ def _write_xml_single(tag: str, info: [str, int], file: TextIO) -> None: """Write header, tail and the data between them into the file.""" print(tag, file=file) print(info, file=file) - print('</' + tag.lstrip('<'), file=file) + print("</" + tag.lstrip("<"), file=file) def _write_xml_single_scientific(tag: str, info: float, file: TextIO) -> None: """Write header, tail and the data between them into the file.""" print(tag, file=file) - print('{: ,.14E}'.format(info), file=file) - print('</' + tag.lstrip('<'), file=file) + print("{: ,.14E}".format(info), file=file) + print("</" + tag.lstrip("<"), file=file) def _write_xml_iterator(tag: str, info: Iterator, file: TextIO) -> None: @@ -502,12 +534,12 @@ def _write_xml_iterator(tag: str, info: Iterator, file: TextIO) -> None: print(tag, file=file) for info_line in info: print(info_line, file=file) - print('</' + tag.lstrip('<'), file=file) + print("</" + tag.lstrip("<"), file=file) def _write_xml_iterator_scientific(tag: str, info: Iterator, file: TextIO) -> None: """Write list of arrays to file.""" print(tag, file=file) for info_line in info: - print('{: ,.14E}'.format(info_line), file=file) - print('</' + tag.lstrip('<'), file=file) + print("{: ,.14E}".format(info_line), file=file) + print("</" + tag.lstrip("<"), file=file) diff --git a/iodata/formats/xyz.py b/iodata/formats/xyz.py index 0d1edca5..8cba51eb 100644 --- a/iodata/formats/xyz.py +++ b/iodata/formats/xyz.py @@ -57,8 +57,12 @@ import numpy as np -from ..docstrings import (document_load_one, document_load_many, document_dump_one, - document_dump_many) +from ..docstrings import ( + document_load_one, + document_load_many, + document_dump_one, + document_dump_many, +) from ..iodata import IOData from ..periodic import sym2num, num2sym from ..utils import angstrom, LineIterator @@ -67,16 +71,26 @@ __all__ = [] -PATTERNS = ['*.xyz'] +PATTERNS = ["*.xyz"] DEFAULT_ATOM_COLUMNS = [ - ("atnums", None, (), int, - (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), - (lambda atnum: "{:2s}".format(num2sym[atnum]))), - ("atcoords", None, (3,), float, - (lambda word: float(word) * angstrom), - (lambda value: "{:15.10f}".format(value / angstrom))) + ( + "atnums", + None, + (), + int, + (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), + (lambda atnum: "{:2s}".format(num2sym[atnum])), + ), + ( + "atcoords", + None, + (3,), + float, + (lambda word: float(word) * angstrom), + (lambda value: "{:15.10f}".format(value / angstrom)), + ), ] @@ -90,8 +104,7 @@ """ -@document_load_one("XYZ", ['atcoords', 'atnums', 'title'], - [], {"atom_columns": ATOM_COLUMNS_DOC}) +@document_load_one("XYZ", ["atcoords", "atnums", "title"], [], {"atom_columns": ATOM_COLUMNS_DOC}) def load_one(lit: LineIterator, atom_columns=None) -> dict: """Do not edit this docstring. It will be overwritten.""" # Load the header. @@ -99,7 +112,7 @@ def load_one(lit: LineIterator, atom_columns=None) -> dict: title = next(lit).strip() if atom_columns is None: atom_columns = DEFAULT_ATOM_COLUMNS - data = {'title': title} + data = {"title": title} # Initialize the arrays to be loaded from the XYZ file. for attrname, keyname, shapesuffix, dtype, _loadword, _dumpword in atom_columns: array = np.zeros((natom,) + shapesuffix, dtype=dtype) @@ -117,10 +130,10 @@ def load_one(lit: LineIterator, atom_columns=None) -> dict: # must be stored. if keyname is None: # The array is a normal attribute. - atom_array = data[attrname][iatom: iatom + 1] + atom_array = data[attrname][iatom : iatom + 1] else: # The array is a value of a dictionary attribute. - atom_array = data[attrname][keyname][iatom: iatom + 1] + atom_array = data[attrname][keyname][iatom : iatom + 1] # Fill in array elements with atomic properties. For each new value # to be loaded, the first element of the list words is consumed and # converted to the right format for IOData. @@ -129,8 +142,7 @@ def load_one(lit: LineIterator, atom_columns=None) -> dict: return data -@document_load_many("XYZ", ['atcoords', 'atnums', 'title'], - [], {"atom_columns": ATOM_COLUMNS_DOC}) +@document_load_many("XYZ", ["atcoords", "atnums", "title"], [], {"atom_columns": ATOM_COLUMNS_DOC}) def load_many(lit: LineIterator, atom_columns=None) -> Iterator[dict]: """Do not edit this docstring. It will be overwritten.""" # XYZ Trajectory files are a simple concatenation of individual XYZ files,' @@ -147,15 +159,14 @@ def load_many(lit: LineIterator, atom_columns=None) -> Iterator[dict]: return -@document_dump_one("XYZ", ['atcoords', 'atnums'], ['title'], - {"atom_columns": ATOM_COLUMNS_DOC}) +@document_dump_one("XYZ", ["atcoords", "atnums"], ["title"], {"atom_columns": ATOM_COLUMNS_DOC}) def dump_one(f: TextIO, data: IOData, atom_columns=None): """Do not edit this docstring. It will be overwritten.""" if atom_columns is None: atom_columns = DEFAULT_ATOM_COLUMNS # Write the header print(data.natom, file=f) - print(data.title or 'Created with IOData', file=f) + print(data.title or "Created with IOData", file=f) # Write the atom lines for iatom in range(data.natom): words = [] @@ -169,8 +180,7 @@ def dump_one(f: TextIO, data: IOData, atom_columns=None): print(" ".join(words), file=f) -@document_dump_many("XYZ", ['atcoords', 'atnums'], ['title'], - {"atom_columns": ATOM_COLUMNS_DOC}) +@document_dump_many("XYZ", ["atcoords", "atnums"], ["title"], {"atom_columns": ATOM_COLUMNS_DOC}) def dump_many(f: TextIO, datas: Iterator[IOData], atom_columns=None): """Do not edit this docstring. It will be overwritten.""" # Similar to load_many, this is relatively easy. diff --git a/iodata/inputs/common.py b/iodata/inputs/common.py index 31d978bf..25308a2f 100644 --- a/iodata/inputs/common.py +++ b/iodata/inputs/common.py @@ -25,7 +25,7 @@ from ..iodata import IOData from ..utils import angstrom -__all__ = ['populate_fields'] +__all__ = ["populate_fields"] def populate_fields(data: IOData) -> dict: @@ -35,8 +35,8 @@ def populate_fields(data: IOData) -> dict: # store atomic coordinates in angstrom fields["atcoords"] = data.atcoords / angstrom # set general defaults - fields["title"] = data.title if data.title is not None else 'Input Generated by IOData' - fields["run_type"] = data.run_type if data.run_type is not None else 'energy' + fields["title"] = data.title if data.title is not None else "Input Generated by IOData" + fields["run_type"] = data.run_type if data.run_type is not None else "energy" # convert spin polarization to multiplicity fields["spinmult"] = int(abs(np.round(data.spinpol))) + 1 if data.spinpol is not None else 1 fields["charge"] = int(data.charge) if data.charge is not None else 0 diff --git a/iodata/inputs/gaussian.py b/iodata/inputs/gaussian.py index 61c88b30..1c6a6e3c 100644 --- a/iodata/inputs/gaussian.py +++ b/iodata/inputs/gaussian.py @@ -18,7 +18,6 @@ # -- """Gaussian Input Module.""" - from typing import TextIO from .common import populate_fields @@ -42,18 +41,26 @@ """ -@document_write_input("GAUSSIAN", ['atnums', 'atcoords'], - ['title', 'run_type', 'lot', 'obasis_name', 'spinmult', 'charge']) +@document_write_input( + "GAUSSIAN", + ["atnums", "atcoords"], + ["title", "run_type", "lot", "obasis_name", "spinmult", "charge"], +) def write_input(f: TextIO, data: IOData, template: str = None, **kwargs): """Do not edit this docstring. It will be overwritten.""" # initialize a dictionary with fields to replace in the template fields = populate_fields(data) # set format-specific defaults - fields["lot"] = data.lot if data.lot is not None else 'hf' - fields["obasis_name"] = data.obasis_name if data.obasis_name is not None else 'sto-3g' + fields["lot"] = data.lot if data.lot is not None else "hf" + fields["obasis_name"] = data.obasis_name if data.obasis_name is not None else "sto-3g" # convert run type to Gaussian keywords - run_types = {"energy": "sp", "energy_force": "force", "opt": "opt", "scan": "scan", - "freq": "freq"} + run_types = { + "energy": "sp", + "energy_force": "force", + "opt": "opt", + "scan": "scan", + "freq": "freq", + } fields["run_type"] = run_types[fields["run_type"].lower()] # generate geometry (in angstrom) geometry = [] diff --git a/iodata/inputs/orca.py b/iodata/inputs/orca.py index aa4f3d1f..25f96fa3 100644 --- a/iodata/inputs/orca.py +++ b/iodata/inputs/orca.py @@ -18,7 +18,6 @@ # -- """Orca Input Module.""" - from typing import TextIO from .common import populate_fields @@ -38,15 +37,18 @@ *""" -@document_write_input("ORCA", ['atnums', 'atcoords'], - ['title', 'run_type', 'lot', 'obasis_name', 'spinmult', 'charge']) +@document_write_input( + "ORCA", + ["atnums", "atcoords"], + ["title", "run_type", "lot", "obasis_name", "spinmult", "charge"], +) def write_input(f: TextIO, data: IOData, template: str = None, **kwargs): """Do not edit this docstring. It will be overwritten.""" # initialize a dictionary with fields to replace in the template fields = populate_fields(data) # set format-specific defaults - fields["lot"] = data.lot if data.lot is not None else 'HF' - fields["obasis_name"] = data.obasis_name if data.obasis_name is not None else 'STO-3G' + fields["lot"] = data.lot if data.lot is not None else "HF" + fields["obasis_name"] = data.obasis_name if data.obasis_name is not None else "STO-3G" # convert run type to Orca keywords run_types = {"energy": "Energy", "freq": "Freq", "opt": "Opt"} fields["run_type"] = run_types[fields["run_type"].lower()] diff --git a/iodata/iodata.py b/iodata/iodata.py index 47e89086..876bab6b 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -18,7 +18,6 @@ # -- """Module for handling input/output from different file formats.""" - import attr import numpy as np @@ -28,12 +27,11 @@ from .utils import Cube -__all__ = ['IOData'] +__all__ = ["IOData"] # pylint: disable=too-many-instance-attributes -@attr.s(auto_attribs=True, slots=True, - on_setattr=[attr.setters.validate, attr.setters.convert]) +@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) class IOData: """A container class for data loaded from (or to be written to) a file. @@ -178,41 +176,61 @@ class IOData: atcharges: dict = {} atcoords: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape('natom', 3))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("natom", 3)), + ) _atcorenums: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape('natom'))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("natom")), + ) atffparams: dict = {} atfrozen: np.ndarray = attr.ib( - default=None, converter=convert_array_to(bool), - validator=attr.validators.optional(validate_shape('natom'))) + default=None, + converter=convert_array_to(bool), + validator=attr.validators.optional(validate_shape("natom")), + ) atgradient: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape('natom', 3))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("natom", 3)), + ) athessian: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, None))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape(None, None)), + ) atmasses: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape('natom'))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("natom")), + ) atnums: np.ndarray = attr.ib( - default=None, converter=convert_array_to(int), - validator=attr.validators.optional(validate_shape('natom'))) + default=None, + converter=convert_array_to(int), + validator=attr.validators.optional(validate_shape("natom")), + ) basisdef: str = None bonds: np.ndarray = attr.ib( - default=None, converter=convert_array_to(int), - validator=attr.validators.optional(validate_shape(None, 3))) + default=None, + converter=convert_array_to(int), + validator=attr.validators.optional(validate_shape(None, 3)), + ) cellvecs: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, 3))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape(None, 3)), + ) _charge: float = None core_energy: float = None cube: Cube = None energy: float = None extcharges: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, 4))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape(None, 4)), + ) extra: dict = {} g_rot: float = None lot: str = None diff --git a/iodata/orbitals.py b/iodata/orbitals.py index 5082474a..decd9741 100644 --- a/iodata/orbitals.py +++ b/iodata/orbitals.py @@ -18,14 +18,13 @@ # -- """Data structure for molecular orbitals.""" - import attr import numpy as np from .attrutils import convert_array_to, validate_shape -__all__ = ['MolecularOrbitals'] +__all__ = ["MolecularOrbitals"] def validate_norbab(mo, attribute, value): @@ -44,19 +43,20 @@ def validate_norbab(mo, attribute, value): if mo.kind == "generalized": if value is not None: raise ValueError( - f"Attribute {attribute.name} must be None in case of generalized orbitals.") + f"Attribute {attribute.name} must be None in case of generalized orbitals." + ) return if value is None: raise ValueError( - f"Attribute {attribute.name} cannot be None in case of (un)restricted orbitals.") + f"Attribute {attribute.name} cannot be None in case of (un)restricted orbitals." + ) if mo.kind == "restricted": norb_other = mo.norbb if (attribute.name == "norba") else mo.norba if value != norb_other: raise ValueError("In case of restricted orbitals, norba must be equal to norbb.") -@attr.s(auto_attribs=True, slots=True, - on_setattr=[attr.setters.validate, attr.setters.convert]) +@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) class MolecularOrbitals: """Class of Orthonormal Molecular Orbitals. @@ -94,21 +94,28 @@ class MolecularOrbitals: """ kind: str = attr.ib( - validator=attr.validators.in_(["restricted", "unrestricted", "generalized"])) + validator=attr.validators.in_(["restricted", "unrestricted", "generalized"]) + ) norba: int = attr.ib(validator=validate_norbab) norbb: int = attr.ib(validator=validate_norbab) occs: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("norb"))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("norb")), + ) coeffs: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, "norb"))) + default=None, + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape(None, "norb")), + ) energies: np.ndarray = attr.ib( - default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("norb"))) - irreps: np.ndarray = attr.ib( default=None, - validator=attr.validators.optional(validate_shape("norb"))) + converter=convert_array_to(float), + validator=attr.validators.optional(validate_shape("norb")), + ) + irreps: np.ndarray = attr.ib( + default=None, validator=attr.validators.optional(validate_shape("norb")) + ) @property def nelec(self) -> float: @@ -122,7 +129,7 @@ def nbasis(self): """Return the number of spatial basis functions.""" if self.coeffs is None: return None - if self.kind == 'generalized': + if self.kind == "generalized": return self.coeffs.shape[0] // 2 return self.coeffs.shape[0] @@ -158,7 +165,7 @@ def spinpol(self) -> float: raise NotImplementedError if self.occs is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": nbeta = np.clip(self.occs, 0, 1).sum() return abs(self.nelec - 2 * nbeta) return abs(self.occsa.sum() - self.occsb.sum()) @@ -170,9 +177,9 @@ def occsa(self): raise NotImplementedError if self.occs is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return np.clip(self.occs, 0, 1) - return self.occs[:self.norba] + return self.occs[: self.norba] @property def occsb(self): @@ -181,9 +188,9 @@ def occsb(self): raise NotImplementedError if self.occs is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.occs - np.clip(self.occs, 0, 1) - return self.occs[self.norba:] + return self.occs[self.norba :] @property def coeffsa(self): @@ -192,9 +199,9 @@ def coeffsa(self): raise NotImplementedError if self.coeffs is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.coeffs - return self.coeffs[:, :self.norba] + return self.coeffs[:, : self.norba] @property def coeffsb(self): @@ -203,9 +210,9 @@ def coeffsb(self): raise NotImplementedError if self.coeffs is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.coeffs - return self.coeffs[:, self.norba:] + return self.coeffs[:, self.norba :] @property def energiesa(self): @@ -214,9 +221,9 @@ def energiesa(self): raise NotImplementedError if self.energies is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.energies - return self.energies[:self.norba] + return self.energies[: self.norba] @property def energiesb(self): @@ -225,9 +232,9 @@ def energiesb(self): raise NotImplementedError if self.energies is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.energies - return self.energies[self.norba:] + return self.energies[self.norba :] @property def irrepsa(self): @@ -236,9 +243,9 @@ def irrepsa(self): raise NotImplementedError if self.irreps is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.irreps - return self.irreps[:self.norba] + return self.irreps[: self.norba] @property def irrepsb(self): @@ -247,6 +254,6 @@ def irrepsb(self): raise NotImplementedError if self.irreps is None: return None - if self.kind == 'restricted': + if self.kind == "restricted": return self.irreps - return self.irreps[self.norba:] + return self.irreps[self.norba :] diff --git a/iodata/overlap.py b/iodata/overlap.py index 6ae5ab58..acabdcfc 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -52,9 +52,12 @@ def factorial2(n, exact=False): # pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches -def compute_overlap(obasis0: MolecularBasis, atcoords0: np.ndarray, - obasis1: Optional[MolecularBasis] = None, - atcoords1: Optional[np.ndarray] = None,) -> np.ndarray: +def compute_overlap( + obasis0: MolecularBasis, + atcoords0: np.ndarray, + obasis1: Optional[MolecularBasis] = None, + atcoords1: Optional[np.ndarray] = None, +) -> np.ndarray: r"""Compute overlap matrix for the given molecular basis set(s). .. math:: @@ -86,9 +89,8 @@ def compute_overlap(obasis0: MolecularBasis, atcoords0: np.ndarray, The matrix with overlap integrals, ``shape=(obasis0.nbasis, obasis1.nbasis)``. """ - if obasis0.primitive_normalization != 'L2': - raise ValueError('The overlap integrals are only implemented for L2 ' - 'normalization.') + if obasis0.primitive_normalization != "L2": + raise ValueError("The overlap integrals are only implemented for L2 " "normalization.") # Get a segmented basis, for simplicity obasis0 = obasis0.get_segmented() @@ -96,18 +98,21 @@ def compute_overlap(obasis0: MolecularBasis, atcoords0: np.ndarray, # Handle optional arguments if obasis1 is None: if atcoords1 is not None: - raise TypeError("When no second basis is given, no second second " - "array of atomic coordinates is expected.") + raise TypeError( + "When no second basis is given, no second second " + "array of atomic coordinates is expected." + ) obasis1 = obasis0 atcoords1 = atcoords0 identical = True else: - if obasis1.primitive_normalization != 'L2': - raise ValueError('The overlap integrals are only implemented for L2 ' - 'normalization.') + if obasis1.primitive_normalization != "L2": + raise ValueError("The overlap integrals are only implemented for L2 " "normalization.") if atcoords1 is None: - raise TypeError("When a second basis is given, a second second " - "array of atomic coordinates is expected.") + raise TypeError( + "When a second basis is given, a second second " + "array of atomic coordinates is expected." + ) # Get a segmented basis, for simplicity obasis1 = obasis1.get_segmented() identical = False @@ -117,13 +122,13 @@ def compute_overlap(obasis0: MolecularBasis, atcoords0: np.ndarray, # Compute the normalization constants of the Cartesian primitives, with the # contraction coefficients multiplied in. - scales0 = [_compute_cart_shell_normalizations(shell) * shell.coeffs - for shell in obasis0.shells] + scales0 = [_compute_cart_shell_normalizations(shell) * shell.coeffs for shell in obasis0.shells] if identical: scales1 = scales0 else: - scales1 = [_compute_cart_shell_normalizations(shell) * shell.coeffs - for shell in obasis1.shells] + scales1 = [ + _compute_cart_shell_normalizations(shell) * shell.coeffs for shell in obasis1.shells + ] n_max = max(np.max(shell.angmoms) for shell in obasis0.shells) if not identical: @@ -198,9 +203,9 @@ def compute_overlap(obasis0: MolecularBasis, atcoords0: np.ndarray, # END of Cartesian coordinate system (if going to pure coordinates) # cart to pure - if shell0.kinds[0] == 'p': + if shell0.kinds[0] == "p": shell_overlap = np.dot(tfs[shell0.angmoms[0]], shell_overlap) - if shell1.kinds[0] == 'p': + if shell1.kinds[0] == "p": shell_overlap = np.dot(shell_overlap, tfs[shell1.angmoms[0]].T) # store lower triangular result @@ -249,12 +254,12 @@ def compute_overlap_gaussian_1d(self, x1, x2, n1, n2, two_at): pf_i = self.binomials[n1][i] * x1 ** (n1 - i) for j in range(i % 2, n2 + 1, 2): m = i + j - integ = self.facts[m] / two_at ** (m / 2) # TODO // 2 + integ = self.facts[m] / two_at ** (m / 2) # TODO // 2 value += pf_i * self.binomials[n2][j] * x2 ** (n2 - j) * integ return value -def _compute_cart_shell_normalizations(shell: 'Shell') -> np.ndarray: +def _compute_cart_shell_normalizations(shell: "Shell") -> np.ndarray: """Return normalization constants for the primitives in a given shell. Parameters @@ -269,7 +274,7 @@ def _compute_cart_shell_normalizations(shell: 'Shell') -> np.ndarray: shell is pure. """ - shell = attr.evolve(shell, kinds=['c'] * shell.ncon) + shell = attr.evolve(shell, kinds=["c"] * shell.ncon) result = [] for angmom in shell.angmoms: for exponent in shell.exponents: @@ -296,5 +301,6 @@ def gob_cart_normalization(alpha: np.ndarray, n: np.ndarray) -> np.ndarray: The normalization constant for the gaussian cartesian basis. """ - return np.sqrt((4 * alpha) ** sum(n) * (2 * alpha / np.pi) ** 1.5 - / np.prod(factorial2(2 * n - 1))) + return np.sqrt( + (4 * alpha) ** sum(n) * (2 * alpha / np.pi) ** 1.5 / np.prod(factorial2(2 * n - 1)) + ) diff --git a/iodata/periodic.py b/iodata/periodic.py index ae9f2bb0..13a86a01 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -18,11 +18,10 @@ # -- """Periodic table module.""" - from typing import Dict -__all__ = ['num2sym', 'sym2num'] +__all__ = ["num2sym", "sym2num"] num2sym: Dict[int, str] = { diff --git a/iodata/test/common.py b/iodata/test/common.py index 7c864105..86fd3ba9 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -34,8 +34,13 @@ except ImportError: from importlib.resources import as_file, files -__all__ = ['compute_mulliken_charges', 'compute_1rdm', - 'compare_mols', 'check_orthonormal', 'load_one_warning'] +__all__ = [ + "compute_mulliken_charges", + "compute_1rdm", + "compare_mols", + "check_orthonormal", + "load_one_warning", +] def compute_1rdm(iodata): @@ -57,8 +62,7 @@ def compute_mulliken_charges(iodata): basis_center.extend([shell.icenter] * shell.nbasis) basis_center = np.array(basis_center) # compute atomic populations - populations = np.array([np.sum(bp[basis_center == index]) - for index in range(iodata.natom)]) + populations = np.array([np.sum(bp[basis_center == index]) for index in range(iodata.natom)]) return iodata.atcorenums - np.array(populations) @@ -78,15 +82,14 @@ def truncated_file(fn_orig, nline, nadd, tmpdir): A temporary directory where the truncated file is stored. """ - fn_truncated = '%s/truncated_%i_%s' % ( - tmpdir, nline, os.path.basename(fn_orig)) - with open(fn_orig) as f_orig, open(fn_truncated, 'w') as f_truncated: + fn_truncated = "%s/truncated_%i_%s" % (tmpdir, nline, os.path.basename(fn_orig)) + with open(fn_orig) as f_orig, open(fn_truncated, "w") as f_truncated: for counter, line in enumerate(f_orig): if counter >= nline: break f_truncated.write(line) for _ in range(nadd): - f_truncated.write('\n') + f_truncated.write("\n") yield fn_truncated @@ -127,9 +130,9 @@ def compare_mols(mol1, mol2, atol=1.0e-8, rtol=0.0): assert_equal(mol1.mo.irreps, mol2.mo.irreps) # operators and density matrices cases = [ - ('one_ints', ['olp', 'kin_ao', 'na_ao']), - ('two_ints', ['er_ao']), - ('one_rdms', ['scf', 'scf_spin', 'post_scf_ao', 'post_scf_spin_ao']), + ("one_ints", ["olp", "kin_ao", "na_ao"]), + ("two_ints", ["er_ao"]), + ("one_rdms", ["scf", "scf_spin", "post_scf_ao", "post_scf_spin_ao"]), ] for attrname, keys in cases: d1 = getattr(mol1, attrname) @@ -162,9 +165,8 @@ def check_orthonormal(mo_coeffs, ao_overlap, atol=1e-5): # compute MO overlap & number of MO orbitals mo_overlap = np.dot(mo_coeffs.T, np.dot(ao_overlap, mo_coeffs)) mo_count = mo_coeffs.shape[1] - message = 'Molecular orbitals are not orthonormal!' - assert_allclose(mo_overlap, np.eye(mo_count), - rtol=0., atol=atol, err_msg=message) + message = "Molecular orbitals are not orthonormal!" + assert_allclose(mo_overlap, np.eye(mo_count), rtol=0.0, atol=atol, err_msg=message) def load_one_warning(filename: str, fmt: str = None, match: str = None, **kwargs): diff --git a/iodata/test/test_attrutils.py b/iodata/test/test_attrutils.py index cc1f803a..b3669934 100644 --- a/iodata/test/test_attrutils.py +++ b/iodata/test/test_attrutils.py @@ -18,7 +18,6 @@ # -- """Unit tests for iodata.attrutils.""" - import attr import numpy as np from numpy.testing import assert_allclose @@ -73,9 +72,7 @@ class Spam: def test_validate_shape_init(): # Construct a Spam instance with valid arguments. This should just work - spam = Spam( - np.zeros((1, 7, 4)), np.zeros((4, 3)), np.zeros((2, 3)), np.zeros(5), "abcde" - ) + spam = Spam(np.zeros((1, 7, 4)), np.zeros((4, 3)), np.zeros((2, 3)), np.zeros(5), "abcde") # Double check attr.validate(spam) # Call constructor with invalid arguments @@ -132,9 +129,7 @@ def test_validate_shape_init(): def test_validate_shape_assign(): # Construct a Spam instance with valid arguments. This should just work - spam = Spam( - np.zeros((1, 7, 4)), np.zeros((4, 3)), np.zeros((2, 3)), np.zeros(5), "abcde" - ) + spam = Spam(np.zeros((1, 7, 4)), np.zeros((4, 3)), np.zeros((2, 3)), np.zeros(5), "abcde") # Double check attr.validate(spam) # assign invalid attributes diff --git a/iodata/test/test_basis.py b/iodata/test/test_basis.py index 6788e7b0..12afe4e7 100644 --- a/iodata/test/test_basis.py +++ b/iodata/test/test_basis.py @@ -18,52 +18,58 @@ # -- """Unit tests for iodata.obasis.""" - import attr import numpy as np from numpy.testing import assert_equal import pytest -from ..basis import (angmom_sti, angmom_its, Shell, MolecularBasis, - convert_convention_shell, convert_conventions, - iter_cart_alphabet, HORTON2_CONVENTIONS, CCA_CONVENTIONS) +from ..basis import ( + angmom_sti, + angmom_its, + Shell, + MolecularBasis, + convert_convention_shell, + convert_conventions, + iter_cart_alphabet, + HORTON2_CONVENTIONS, + CCA_CONVENTIONS, +) from ..formats.cp2klog import CONVENTIONS as CP2K_CONVENTIONS def test_angmom_sti(): - assert angmom_sti('s') == 0 - assert angmom_sti('p') == 1 - assert angmom_sti('f') == 3 - assert angmom_sti(['s']) == [0] - assert angmom_sti(['s', 's']) == [0, 0] - assert angmom_sti(['s', 's', 's']) == [0, 0, 0] - assert angmom_sti(['p']) == [1] - assert angmom_sti(['s', 'p']) == [0, 1] - assert angmom_sti(['s', 'p', 'p']) == [0, 1, 1] - assert angmom_sti(['s', 'p', 'p', 'd', 'd', 's', 'f', 'i']) == \ - [0, 1, 1, 2, 2, 0, 3, 6] - assert angmom_sti(['e', 't', 'k']) == [24, 14, 7] + assert angmom_sti("s") == 0 + assert angmom_sti("p") == 1 + assert angmom_sti("f") == 3 + assert angmom_sti(["s"]) == [0] + assert angmom_sti(["s", "s"]) == [0, 0] + assert angmom_sti(["s", "s", "s"]) == [0, 0, 0] + assert angmom_sti(["p"]) == [1] + assert angmom_sti(["s", "p"]) == [0, 1] + assert angmom_sti(["s", "p", "p"]) == [0, 1, 1] + assert angmom_sti(["s", "p", "p", "d", "d", "s", "f", "i"]) == [0, 1, 1, 2, 2, 0, 3, 6] + assert angmom_sti(["e", "t", "k"]) == [24, 14, 7] def test_angmom_sti_uppercase(): - assert angmom_sti('S') == 0 - assert angmom_sti('D') == 2 - assert angmom_sti('g') == 4 - assert angmom_sti(['P']) == [1] - assert angmom_sti(['F', 'f']) == [3, 3] - assert angmom_sti(['n', 'N', 'N']) == [10, 10, 10] - assert angmom_sti(['D', 'O']) == [2, 11] - assert angmom_sti(['S', 'p', 'P', 'D', 's', 'I']) == [0, 1, 1, 2, 0, 6] - assert angmom_sti(['E', 'T', 'k']) == [24, 14, 7] + assert angmom_sti("S") == 0 + assert angmom_sti("D") == 2 + assert angmom_sti("g") == 4 + assert angmom_sti(["P"]) == [1] + assert angmom_sti(["F", "f"]) == [3, 3] + assert angmom_sti(["n", "N", "N"]) == [10, 10, 10] + assert angmom_sti(["D", "O"]) == [2, 11] + assert angmom_sti(["S", "p", "P", "D", "s", "I"]) == [0, 1, 1, 2, 0, 6] + assert angmom_sti(["E", "T", "k"]) == [24, 14, 7] def test_angmom_its(): - assert angmom_its(0) == 's' - assert angmom_its(1) == 'p' - assert angmom_its(2) == 'd' - assert angmom_its(3) == 'f' - assert angmom_its(24) == 'e' - assert angmom_its([0, 1, 3]) == ['s', 'p', 'f'] + assert angmom_its(0) == "s" + assert angmom_its(1) == "p" + assert angmom_its(2) == "d" + assert angmom_its(3) == "f" + assert angmom_its(24) == "e" + assert angmom_its([0, 1, 3]) == ["s", "p", "f"] with pytest.raises(ValueError): angmom_its(-1) with pytest.raises(ValueError): @@ -76,11 +82,12 @@ def test_angmom_its(): def test_shell_info_propertes(): shells = [ - Shell(0, [0], ['c'], np.zeros(6), np.zeros((6, 1))), - Shell(0, [0, 1], ['c', 'c'], np.zeros(3), np.zeros((3, 2))), - Shell(0, [0, 1], ['c', 'c'], np.zeros(1), np.zeros((1, 2))), - Shell(0, [2], ['p'], np.zeros(2), np.zeros((2, 1))), - Shell(0, [2, 3, 4], ['c', 'p', 'p'], np.zeros(1), np.zeros((1, 3)))] + Shell(0, [0], ["c"], np.zeros(6), np.zeros((6, 1))), + Shell(0, [0, 1], ["c", "c"], np.zeros(3), np.zeros((3, 2))), + Shell(0, [0, 1], ["c", "c"], np.zeros(1), np.zeros((1, 2))), + Shell(0, [2], ["p"], np.zeros(2), np.zeros((2, 1))), + Shell(0, [2, 3, 4], ["c", "p", "p"], np.zeros(1), np.zeros((1, 3))), + ] assert shells[0].nbasis == 1 assert shells[1].nbasis == 4 @@ -99,62 +106,83 @@ def test_shell_info_propertes(): assert shells[4].ncon == 3 obasis = MolecularBasis( shells, - {(0, 'c'): ['s'], - (1, 'c'): ['x', 'z', '-y'], - (2, 'p'): ['dc0', 'dc1', '-ds1', 'dc2', '-ds2']}, - 'L2') + { + (0, "c"): ["s"], + (1, "c"): ["x", "z", "-y"], + (2, "p"): ["dc0", "dc1", "-ds1", "dc2", "-ds2"], + }, + "L2", + ) assert obasis.nbasis == 1 + 4 + 4 + 5 + 6 + 7 + 9 def test_shell_validators(): # The following line constructs a Shell instance with valid arguments. # It should not raise a TypeError. - shell = Shell(0, [0, 0], ['c', 'c'], np.zeros(6), np.zeros((6, 2))) + shell = Shell(0, [0, 0], ["c", "c"], np.zeros(6), np.zeros((6, 2))) # Rerun the validators as a double check. attr.validate(shell) # Tests with invalid constructor arguments. with pytest.raises(TypeError): - Shell(0, [0, 0], ['c', 'c'], np.zeros(6), np.zeros((6, 2, 2))) + Shell(0, [0, 0], ["c", "c"], np.zeros(6), np.zeros((6, 2, 2))) with pytest.raises(TypeError): - Shell(0, [0], ['c'], np.zeros(6), np.zeros(6,)) + Shell( + 0, + [0], + ["c"], + np.zeros(6), + np.zeros( + 6, + ), + ) with pytest.raises(TypeError): - Shell(0, [0], ['c'], np.zeros((6, 2)), np.zeros((6, 1))) + Shell(0, [0], ["c"], np.zeros((6, 2)), np.zeros((6, 1))) with pytest.raises(TypeError): - Shell(0, [0, 0], ['c', 'c'], np.zeros((6, 2)), np.zeros((6, 1))) + Shell(0, [0, 0], ["c", "c"], np.zeros((6, 2)), np.zeros((6, 1))) with pytest.raises(TypeError): - Shell(0, [0], ['c', 'c'], np.zeros(6), np.zeros((6, 2))) + Shell(0, [0], ["c", "c"], np.zeros(6), np.zeros((6, 2))) with pytest.raises(TypeError): - Shell(0, [0, 0], ['c'], np.zeros(6), np.zeros((6, 2))) + Shell(0, [0, 0], ["c"], np.zeros(6), np.zeros((6, 2))) def test_shell_exceptions(): - Shell(0, [0, 0, 0], ['e', 'e', 'e'], np.zeros(6), np.zeros((6, 3))) + Shell(0, [0, 0, 0], ["e", "e", "e"], np.zeros(6), np.zeros((6, 3))) with pytest.raises(TypeError): - _ = Shell(0, [0, 0, 0], ['e', 'e', 'e'], np.zeros(6), np.zeros((6, 3))).nbasis - Shell(0, [0, 0, 0], ['p', 'p', 'p'], np.zeros(6), np.zeros((6, 3))) + _ = Shell(0, [0, 0, 0], ["e", "e", "e"], np.zeros(6), np.zeros((6, 3))).nbasis + Shell(0, [0, 0, 0], ["p", "p", "p"], np.zeros(6), np.zeros((6, 3))) with pytest.raises(TypeError): - _ = Shell(0, [0, 0, 0], ['p', 'p', 'p'], np.zeros(6), np.zeros((6, 3))).nbasis - Shell(0, [1, 1, 1], ['p', 'p', 'p'], np.zeros(6), np.zeros((6, 3))) + _ = Shell(0, [0, 0, 0], ["p", "p", "p"], np.zeros(6), np.zeros((6, 3))).nbasis + Shell(0, [1, 1, 1], ["p", "p", "p"], np.zeros(6), np.zeros((6, 3))) with pytest.raises(TypeError): - _ = Shell(0, [1, 1, 1], ['p', 'p', 'p'], np.zeros(6), np.zeros((6, 3))).nbasis + _ = Shell(0, [1, 1, 1], ["p", "p", "p"], np.zeros(6), np.zeros((6, 3))).nbasis def test_nbasis1(): - obasis = MolecularBasis([ - Shell(0, [0], ['c'], np.zeros(16), np.zeros((16, 1))), - Shell(0, [1], ['c'], np.zeros(16), np.zeros((16, 1))), - Shell(0, [2], ['p'], np.zeros(16), np.zeros((16, 1))), - ], CP2K_CONVENTIONS, 'L2') + obasis = MolecularBasis( + [ + Shell(0, [0], ["c"], np.zeros(16), np.zeros((16, 1))), + Shell(0, [1], ["c"], np.zeros(16), np.zeros((16, 1))), + Shell(0, [2], ["p"], np.zeros(16), np.zeros((16, 1))), + ], + CP2K_CONVENTIONS, + "L2", + ) assert obasis.nbasis == 9 def test_get_segmented(): - obasis0 = MolecularBasis([ - Shell(0, [0, 1], ['c', 'c'], np.random.uniform(0, 1, 5), - np.random.uniform(-1, 1, (5, 2))), - Shell(1, [2, 3], ['p', 'p'], np.random.uniform(0, 1, 7), - np.random.uniform(-1, 1, (7, 2))), - ], CP2K_CONVENTIONS, 'L2') + obasis0 = MolecularBasis( + [ + Shell( + 0, [0, 1], ["c", "c"], np.random.uniform(0, 1, 5), np.random.uniform(-1, 1, (5, 2)) + ), + Shell( + 1, [2, 3], ["p", "p"], np.random.uniform(0, 1, 7), np.random.uniform(-1, 1, (7, 2)) + ), + ], + CP2K_CONVENTIONS, + "L2", + ) assert obasis0.nbasis == 16 obasis1 = obasis0.get_segmented() assert len(obasis1.shells) == 4 @@ -163,84 +191,91 @@ def test_get_segmented(): shell0 = obasis1.shells[0] assert shell0.icenter == 0 assert_equal(shell0.angmoms, [0]) - assert shell0.kinds == ['c'] + assert shell0.kinds == ["c"] assert_equal(shell0.exponents, obasis0.shells[0].exponents) assert_equal(shell0.coeffs, obasis0.shells[0].coeffs[:, :1]) # shell 1 shell1 = obasis1.shells[1] assert shell1.icenter == 0 assert_equal(shell1.angmoms, [1]) - assert shell1.kinds == ['c'] + assert shell1.kinds == ["c"] assert_equal(shell1.exponents, obasis0.shells[0].exponents) assert_equal(shell1.coeffs, obasis0.shells[0].coeffs[:, 1:]) # shell 2 shell2 = obasis1.shells[2] assert shell2.icenter == 1 assert_equal(shell2.angmoms, [2]) - assert shell2.kinds == ['p'] + assert shell2.kinds == ["p"] assert_equal(shell2.exponents, obasis0.shells[1].exponents) assert_equal(shell2.coeffs, obasis0.shells[1].coeffs[:, :1]) # shell 0 shell3 = obasis1.shells[3] assert shell3.icenter == 1 assert_equal(shell3.angmoms, [3]) - assert shell3.kinds == ['p'] + assert shell3.kinds == ["p"] assert_equal(shell3.exponents, obasis0.shells[1].exponents) assert_equal(shell3.coeffs, obasis0.shells[1].coeffs[:, 1:]) def test_convert_convention_shell(): - assert convert_convention_shell('abc', 'cba') == ([2, 1, 0], [1, 1, 1]) - assert convert_convention_shell(['a', 'b', 'c'], ['c', 'b', 'a']) == ([2, 1, 0], [1, 1, 1]) + assert convert_convention_shell("abc", "cba") == ([2, 1, 0], [1, 1, 1]) + assert convert_convention_shell(["a", "b", "c"], ["c", "b", "a"]) == ([2, 1, 0], [1, 1, 1]) - permutation, signs = convert_convention_shell(['-a', 'b', 'c'], ['c', 'b', 'a']) + permutation, signs = convert_convention_shell(["-a", "b", "c"], ["c", "b", "a"]) assert permutation == [2, 1, 0] assert signs == [1, 1, -1] vec1 = np.array([1, 2, 3]) vec2 = np.array([3, 2, -1]) assert_equal(vec1[permutation] * signs, vec2) - permutation, signs = convert_convention_shell(['-a', 'b', 'c'], ['c', 'b', 'a'], True) + permutation, signs = convert_convention_shell(["-a", "b", "c"], ["c", "b", "a"], True) assert_equal(vec2[permutation] * signs, vec1) - permutation, signs = convert_convention_shell(['a', 'b', 'c'], ['-c', 'b', 'a']) + permutation, signs = convert_convention_shell(["a", "b", "c"], ["-c", "b", "a"]) assert permutation == [2, 1, 0] assert signs == [-1, 1, 1] vec1 = np.array([1, 2, 3]) vec2 = np.array([-3, 2, 1]) assert_equal(vec1[permutation] * signs, vec2) - permutation, signs = convert_convention_shell(['a', '-b', '-c'], ['-c', 'b', 'a']) + permutation, signs = convert_convention_shell(["a", "-b", "-c"], ["-c", "b", "a"]) assert permutation == [2, 1, 0] assert signs == [1, -1, 1] vec1 = np.array([1, 2, 3]) vec2 = np.array([3, -2, 1]) assert_equal(vec1[permutation] * signs, vec2) - permutation, signs = convert_convention_shell(['a', '-b', '-c'], ['-c', 'b', 'a'], True) + permutation, signs = convert_convention_shell(["a", "-b", "-c"], ["-c", "b", "a"], True) assert_equal(vec2[permutation] * signs, vec1) - permutation, signs = convert_convention_shell(['fo', 'ba', 'sp'], ['fo', '-sp', 'ba']) + permutation, signs = convert_convention_shell(["fo", "ba", "sp"], ["fo", "-sp", "ba"]) assert permutation == [0, 2, 1] assert signs == [1, -1, 1] vec1 = np.array([1, 2, 3]) vec2 = np.array([1, -3, 2]) assert_equal(vec1[permutation] * signs, vec2) - permutation, signs = convert_convention_shell(['fo', 'ba', 'sp'], ['fo', '-sp', 'ba'], True) + permutation, signs = convert_convention_shell(["fo", "ba", "sp"], ["fo", "-sp", "ba"], True) assert_equal(vec2[permutation] * signs, vec1) def test_convert_convention_obasis(): obasis = MolecularBasis( - [Shell(0, [0], ['c'], np.zeros(3), np.zeros((3, 1))), - Shell(0, [0, 1], ['c', 'c'], np.zeros(3), np.zeros((3, 2))), - Shell(0, [0, 1], ['c', 'c'], np.zeros(3), np.zeros((3, 2))), - Shell(0, [2], ['p'], np.zeros(3), np.zeros((3, 1)))], - {(0, 'c'): ['s'], - (1, 'c'): ['x', 'z', '-y'], - (2, 'p'): ['dc0', 'dc1', '-ds1', 'dc2', '-ds2']}, - 'L2') - new_convention = {(0, 'c'): ['-s'], - (1, 'c'): ['x', 'y', 'z'], - (2, 'p'): ['dc2', 'dc1', 'dc0', 'ds1', 'ds2']} + [ + Shell(0, [0], ["c"], np.zeros(3), np.zeros((3, 1))), + Shell(0, [0, 1], ["c", "c"], np.zeros(3), np.zeros((3, 2))), + Shell(0, [0, 1], ["c", "c"], np.zeros(3), np.zeros((3, 2))), + Shell(0, [2], ["p"], np.zeros(3), np.zeros((3, 1))), + ], + { + (0, "c"): ["s"], + (1, "c"): ["x", "z", "-y"], + (2, "p"): ["dc0", "dc1", "-ds1", "dc2", "-ds2"], + }, + "L2", + ) + new_convention = { + (0, "c"): ["-s"], + (1, "c"): ["x", "y", "z"], + (2, "p"): ["dc2", "dc1", "dc0", "ds1", "ds2"], + } permutation, signs = convert_conventions(obasis, new_convention) assert_equal(permutation, [0, 1, 2, 4, 3, 5, 6, 8, 7, 12, 10, 9, 11, 13]) assert_equal(signs, [-1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1]) @@ -253,37 +288,41 @@ def test_convert_convention_obasis(): def test_convert_exceptions(): with pytest.raises(TypeError): - convert_convention_shell('abc', 'cb') + convert_convention_shell("abc", "cb") with pytest.raises(TypeError): - convert_convention_shell('abc', 'cbb') + convert_convention_shell("abc", "cbb") with pytest.raises(TypeError): - convert_convention_shell('aba', 'cba') + convert_convention_shell("aba", "cba") with pytest.raises(TypeError): - convert_convention_shell(['a', 'b', 'c'], ['a', 'b', 'd']) + convert_convention_shell(["a", "b", "c"], ["a", "b", "d"]) with pytest.raises(TypeError): - convert_convention_shell(['a', 'b', 'c'], ['a', 'b', '-d']) + convert_convention_shell(["a", "b", "c"], ["a", "b", "-d"]) def test_iter_cart_alphabet(): assert np.array(list(iter_cart_alphabet(0))).tolist() == [[0, 0, 0]] - assert np.array(list(iter_cart_alphabet(1))).tolist() == [ - [1, 0, 0], [0, 1, 0], [0, 0, 1]] + assert np.array(list(iter_cart_alphabet(1))).tolist() == [[1, 0, 0], [0, 1, 0], [0, 0, 1]] assert np.array(list(iter_cart_alphabet(2))).tolist() == [ - [2, 0, 0], [1, 1, 0], [1, 0, 1], - [0, 2, 0], [0, 1, 1], [0, 0, 2]] + [2, 0, 0], + [1, 1, 0], + [1, 0, 1], + [0, 2, 0], + [0, 1, 1], + [0, 0, 2], + ] def test_conventions(): for angmom in range(25): - assert HORTON2_CONVENTIONS[(angmom, 'c')] == CCA_CONVENTIONS[(angmom, 'c')] - assert HORTON2_CONVENTIONS[(0, 'c')] == ['1'] - assert HORTON2_CONVENTIONS[(1, 'c')] == ['x', 'y', 'z'] - assert HORTON2_CONVENTIONS[(2, 'c')] == ['xx', 'xy', 'xz', 'yy', 'yz', 'zz'] - assert (0, 'p') not in HORTON2_CONVENTIONS - assert (0, 'p') not in CCA_CONVENTIONS - assert (1, 'p') not in HORTON2_CONVENTIONS - assert (1, 'p') not in CCA_CONVENTIONS - assert HORTON2_CONVENTIONS[(2, 'p')] == ['c0', 'c1', 's1', 'c2', 's2'] - assert CCA_CONVENTIONS[(2, 'p')] == ['s2', 's1', 'c0', 'c1', 'c2'] - assert HORTON2_CONVENTIONS[(3, 'p')] == ['c0', 'c1', 's1', 'c2', 's2', 'c3', 's3'] - assert CCA_CONVENTIONS[(3, 'p')] == ['s3', 's2', 's1', 'c0', 'c1', 'c2', 'c3'] + assert HORTON2_CONVENTIONS[(angmom, "c")] == CCA_CONVENTIONS[(angmom, "c")] + assert HORTON2_CONVENTIONS[(0, "c")] == ["1"] + assert HORTON2_CONVENTIONS[(1, "c")] == ["x", "y", "z"] + assert HORTON2_CONVENTIONS[(2, "c")] == ["xx", "xy", "xz", "yy", "yz", "zz"] + assert (0, "p") not in HORTON2_CONVENTIONS + assert (0, "p") not in CCA_CONVENTIONS + assert (1, "p") not in HORTON2_CONVENTIONS + assert (1, "p") not in CCA_CONVENTIONS + assert HORTON2_CONVENTIONS[(2, "p")] == ["c0", "c1", "s1", "c2", "s2"] + assert CCA_CONVENTIONS[(2, "p")] == ["s2", "s1", "c0", "c1", "c2"] + assert HORTON2_CONVENTIONS[(3, "p")] == ["c0", "c1", "s1", "c2", "s2", "c3", "s3"] + assert CCA_CONVENTIONS[(3, "p")] == ["s3", "s2", "s1", "c0", "c1", "c2", "c3"] diff --git a/iodata/test/test_charmm.py b/iodata/test/test_charmm.py index 179d4d40..cef174cf 100644 --- a/iodata/test/test_charmm.py +++ b/iodata/test/test_charmm.py @@ -38,14 +38,14 @@ def test_load_crambin(): assert len(mol.title) == 125 assert mol.atcoords.shape == (648, 3) assert_allclose(mol.atcoords[-1] / angstrom, [7.35403, -5.09628, 2.73659]) - assert mol.atffparams['attypes'].shape == (648,) - assert mol.atffparams['resnums'].shape == (648,) - assert mol.atffparams['resnames'].shape == (648,) - assert mol.atffparams['attypes'][-1] == 'OT2' - assert_equal(mol.atffparams['resnums'][46:48], [4, 4]) - assert mol.atffparams['resnames'][-1] == 'ASN' - assert mol.extra['segid'].shape == (648,) - assert mol.extra['resid'].shape == (648,) - assert mol.extra['segid'][-1] == 'MAIN' - assert mol.extra['resid'][-1] == 46 + assert mol.atffparams["attypes"].shape == (648,) + assert mol.atffparams["resnums"].shape == (648,) + assert mol.atffparams["resnames"].shape == (648,) + assert mol.atffparams["attypes"][-1] == "OT2" + assert_equal(mol.atffparams["resnums"][46:48], [4, 4]) + assert mol.atffparams["resnames"][-1] == "ASN" + assert mol.extra["segid"].shape == (648,) + assert mol.extra["resid"].shape == (648,) + assert mol.extra["segid"][-1] == "MAIN" + assert mol.extra["resid"][-1] == 46 assert mol.atmasses[-1] == 15.99900 * amu diff --git a/iodata/test/test_chgcar.py b/iodata/test/test_chgcar.py index 681b4874..b569e2d5 100644 --- a/iodata/test/test_chgcar.py +++ b/iodata/test/test_chgcar.py @@ -35,25 +35,25 @@ def test_load_chgcar_oxygen(): with as_file(files("iodata.test.data").joinpath("CHGCAR.oxygen")) as fn: mol = load_one(str(fn)) assert_equal(mol.atnums, 8) - assert_allclose(volume(mol.cellvecs), (10 * angstrom) ** 3, atol=1.e-10) + assert_allclose(volume(mol.cellvecs), (10 * angstrom) ** 3, atol=1.0e-10) assert_equal(mol.cube.shape, [2, 2, 2]) assert abs(mol.cube.origin).max() < 1e-10 - assert_allclose(mol.cube.axes, mol.cellvecs / 2, atol=1.e-10) + assert_allclose(mol.cube.axes, mol.cellvecs / 2, atol=1.0e-10) d = mol.cube.data - assert_allclose(d[0, 0, 0], 0.78406017013E+04 / volume(mol.cellvecs), atol=1.e-10) - assert_allclose(d[-1, -1, -1], 0.10024522914E+04 / volume(mol.cellvecs), atol=1.e-10) - assert_allclose(d[1, 0, 0], 0.76183317989E+04 / volume(mol.cellvecs), atol=1.e-10) + assert_allclose(d[0, 0, 0], 0.78406017013e04 / volume(mol.cellvecs), atol=1.0e-10) + assert_allclose(d[-1, -1, -1], 0.10024522914e04 / volume(mol.cellvecs), atol=1.0e-10) + assert_allclose(d[1, 0, 0], 0.76183317989e04 / volume(mol.cellvecs), atol=1.0e-10) def test_load_chgcar_water(): with as_file(files("iodata.test.data").joinpath("CHGCAR.water")) as fn: mol = load_one(str(fn)) - assert mol.title == 'unknown system' + assert mol.title == "unknown system" assert_equal(mol.atnums, [8, 1, 1]) coords = np.array([0.074983 * 15 + 0.903122 * 1, 0.903122 * 15, 0.000000]) - assert_allclose(mol.atcoords[1], coords, atol=1.e-7) - assert_allclose(volume(mol.cellvecs), 15 ** 3, atol=1.e-4) + assert_allclose(mol.atcoords[1], coords, atol=1.0e-7) + assert_allclose(volume(mol.cellvecs), 15**3, atol=1.0e-4) assert_equal(len(mol.cube.shape), 3) assert_equal(mol.cube.shape, (3, 3, 3)) - assert_allclose(mol.cube.axes, mol.cellvecs / 3, atol=1.e-10) + assert_allclose(mol.cube.axes, mol.cellvecs / 3, atol=1.0e-10) assert abs(mol.cube.origin).max() < 1e-10 diff --git a/iodata/test/test_cli.py b/iodata/test/test_cli.py index aced03ea..c2baa372 100644 --- a/iodata/test/test_cli.py +++ b/iodata/test/test_cli.py @@ -33,14 +33,13 @@ def _check_convert_one(myconvert, tmpdir): - outfn = os.path.join(tmpdir, 'tmp.xyz') + outfn = os.path.join(tmpdir, "tmp.xyz") with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as infn: myconvert(infn, outfn) iodata = load_one(outfn) assert iodata.natom == 2 assert_equal(iodata.atnums, [9, 1]) - assert_allclose(iodata.atcoords, - [[0.0, 0.0, 0.190484394], [0.0, 0.0, -1.71435955]]) + assert_allclose(iodata.atcoords, [[0.0, 0.0, 0.190484394], [0.0, 0.0, -1.71435955]]) def test_convert_one_autofmt(tmpdir): @@ -49,26 +48,28 @@ def test_convert_one_autofmt(tmpdir): def test_convert_one_manfmt(tmpdir): - myconvert = functools.partial(convert, many=False, infmt='fchk', outfmt='xyz') + myconvert = functools.partial(convert, many=False, infmt="fchk", outfmt="xyz") _check_convert_one(myconvert, tmpdir) def test_script_one_autofmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(['python', '-m', 'iodata.__main__', infn, outfn], - check=True) + subprocess.run(["python", "-m", "iodata.__main__", infn, outfn], check=True) + _check_convert_one(myconvert, tmpdir) def test_script_one_manfmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(['python', '-m', 'iodata.__main__', infn, outfn, - '-i', 'fchk', '-o', 'xyz'], check=True) + subprocess.run( + ["python", "-m", "iodata.__main__", infn, outfn, "-i", "fchk", "-o", "xyz"], check=True + ) + _check_convert_one(myconvert, tmpdir) def _check_convert_many(myconvert, tmpdir): - outfn = os.path.join(tmpdir, 'tmp.xyz') + outfn = os.path.join(tmpdir, "tmp.xyz") with as_file(files("iodata.test.data").joinpath("peroxide_relaxed_scan.fchk")) as infn: myconvert(infn, outfn) trj = list(load_many(outfn)) @@ -76,10 +77,8 @@ def _check_convert_many(myconvert, tmpdir): for iodata in trj: assert iodata.natom == 4 assert_equal(iodata.atnums, [8, 8, 1, 1]) - assert_allclose(trj[1].atcoords[3], - [-1.85942837, -1.70565735, 0.0], atol=1e-5) - assert_allclose(trj[5].atcoords[0], - [0.0, 1.32466211, 0.0], atol=1e-5) + assert_allclose(trj[1].atcoords[3], [-1.85942837, -1.70565735, 0.0], atol=1e-5) + assert_allclose(trj[5].atcoords[0], [0.0, 1.32466211, 0.0], atol=1e-5) def test_convert_many_autofmt(tmpdir): @@ -88,19 +87,22 @@ def test_convert_many_autofmt(tmpdir): def test_convert_many_manfmt(tmpdir): - myconvert = functools.partial(convert, many=True, infmt='fchk', outfmt='xyz') + myconvert = functools.partial(convert, many=True, infmt="fchk", outfmt="xyz") _check_convert_many(myconvert, tmpdir) def test_script_many_autofmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(['python', '-m', 'iodata.__main__', infn, outfn, '-m'], - check=True) + subprocess.run(["python", "-m", "iodata.__main__", infn, outfn, "-m"], check=True) + _check_convert_many(myconvert, tmpdir) def test_script_many_manfmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(['python', '-m', 'iodata.__main__', infn, outfn, - '-m', '-i', 'fchk', '-o', 'xyz'], check=True) + subprocess.run( + ["python", "-m", "iodata.__main__", infn, outfn, "-m", "-i", "fchk", "-o", "xyz"], + check=True, + ) + _check_convert_many(myconvert, tmpdir) diff --git a/iodata/test/test_cp2klog.py b/iodata/test/test_cp2klog.py index e3b9e275..bb1f4854 100644 --- a/iodata/test/test_cp2klog.py +++ b/iodata/test/test_cp2klog.py @@ -37,20 +37,18 @@ def test_atom_si_uks(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [14]) assert_equal(mol.atcorenums, [4]) - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert_equal(mol.mo.occsa, [1, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0]) assert_equal(mol.mo.occsb, [1, 0, 0, 0]) - assert_allclose(mol.mo.energiesa, - [-0.398761, -0.154896, -0.154896, -0.154896], atol=1.e-4) - assert_allclose(mol.mo.energiesb, - [-0.334567, -0.092237, -0.092237, -0.092237], atol=1.e-4) - assert_allclose(mol.energy, -3.761587698067, atol=1.e-10) + assert_allclose(mol.mo.energiesa, [-0.398761, -0.154896, -0.154896, -0.154896], atol=1.0e-4) + assert_allclose(mol.mo.energiesb, [-0.334567, -0.092237, -0.092237, -0.092237], atol=1.0e-4) + assert_allclose(mol.energy, -3.761587698067, atol=1.0e-10) assert len(mol.obasis.shells) == 3 - assert mol.obasis.shells[0].kinds == ['c', 'c'] + assert mol.obasis.shells[0].kinds == ["c", "c"] assert_equal(mol.obasis.shells[1].angmoms, [1, 1]) - assert mol.obasis.shells[1].kinds == ['c', 'c'] + assert mol.obasis.shells[1].kinds == ["c", "c"] assert_equal(mol.obasis.shells[2].angmoms, [2]) - assert mol.obasis.shells[2].kinds == ['p'] + assert mol.obasis.shells[2].kinds == ["p"] # check mo normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) @@ -62,17 +60,17 @@ def test_atom_o_rks(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [8]) assert_equal(mol.atcorenums, [6]) - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert_equal(mol.mo.occs, [2, 2, 2, 2]) - assert_allclose(mol.mo.energies, [0.102709, 0.606458, 0.606458, 0.606458], atol=1.e-4) - assert_allclose(mol.energy, -15.464982778766, atol=1.e-10) + assert_allclose(mol.mo.energies, [0.102709, 0.606458, 0.606458, 0.606458], atol=1.0e-4) + assert_allclose(mol.energy, -15.464982778766, atol=1.0e-10) assert_equal(mol.obasis.shells[0].angmoms, [0, 0]) assert len(mol.obasis.shells) == 3 - assert mol.obasis.shells[0].kinds == ['c', 'c'] + assert mol.obasis.shells[0].kinds == ["c", "c"] assert_equal(mol.obasis.shells[1].angmoms, [1, 1]) - assert mol.obasis.shells[1].kinds == ['c', 'c'] + assert mol.obasis.shells[1].kinds == ["c", "c"] assert_equal(mol.obasis.shells[2].angmoms, [2]) - assert mol.obasis.shells[2].kinds == ['p'] + assert mol.obasis.shells[2].kinds == ["p"] # check mo normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) @@ -83,7 +81,7 @@ def test_carbon_gs_ae_contracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert_allclose(mol.mo.occsa, [1, 1, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energiesa, [-10.058194, -0.526244, -0.214978, -0.214978, -0.214978]) assert_allclose(mol.mo.occsb, [1, 1, 0, 0, 0]) @@ -101,7 +99,7 @@ def test_carbon_gs_ae_uncontracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert_allclose(mol.mo.occsa, [1, 1, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energiesa, [-10.050076, -0.528162, -0.217626, -0.217626, -0.217626]) assert_allclose(mol.mo.occsb, [1, 1, 0, 0, 0]) @@ -118,7 +116,7 @@ def test_carbon_gs_pp_contracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert_allclose(mol.mo.occsa, [1, 2.0 / 3.0, 2.0 / 3.0, 2.0 / 3.0]) assert_allclose(mol.mo.energiesa, [-0.528007, -0.219974, -0.219974, -0.219974]) assert_allclose(mol.mo.occsb, [1, 0, 0, 0]) @@ -136,7 +134,7 @@ def test_carbon_gs_pp_uncontracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert_allclose(mol.mo.occsa, [1, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energiesa, [-0.528146, -0.219803, -0.219803, -0.219803]) assert_allclose(mol.mo.occsb, [1, 0, 0, 0]) @@ -153,7 +151,7 @@ def test_carbon_sc_ae_contracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert_allclose(mol.mo.occs, [2, 2, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energies, [-10.067251, -0.495823, -0.187878, -0.187878, -0.187878]) assert_allclose(mol.energy, -37.793939631890) @@ -168,7 +166,7 @@ def test_carbon_sc_ae_uncontracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert_allclose(mol.mo.occs, [2, 2, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energies, [-10.062206, -0.499716, -0.192580, -0.192580, -0.192580]) assert_allclose(mol.energy, -37.800453482378) @@ -182,7 +180,7 @@ def test_carbon_sc_pp_contracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert_allclose(mol.mo.occs, [2, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energies, [-0.500732, -0.193138, -0.193138, -0.193138]) assert_allclose(mol.energy, -5.350765755382) @@ -197,7 +195,7 @@ def test_carbon_sc_pp_uncontracted(): mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert_allclose(mol.mo.occs, [2, 2 / 3, 2 / 3, 2 / 3]) assert_allclose(mol.mo.energies, [-0.500238, -0.192365, -0.192365, -0.192365]) assert_allclose(mol.energy, -5.352864672201) diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 834e2a4f..b18a8967 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -33,35 +33,33 @@ def test_load_aelta(): with as_file(files("iodata.test.data").joinpath("aelta.cube")) as fn_cube: mol = load_one(str(fn_cube)) - assert mol.title == 'Some random cube for testing (sort of) useless data' + assert mol.title == "Some random cube for testing (sort of) useless data" assert_equal(mol.natom, 72) - assert_allclose(mol.atcoords[5, 0], 27.275511, atol=1.e-5) - assert_allclose(mol.atcoords[-2, 2], 26.460812, atol=1.e-5) + assert_allclose(mol.atcoords[5, 0], 27.275511, atol=1.0e-5) + assert_allclose(mol.atcoords[-2, 2], 26.460812, atol=1.0e-5) assert_equal(mol.cube.shape, (12, 12, 12)) - my_cellvecs = np.array([[1.8626, 0.1, 0.0], - [0.0, 1.8626, 0.0], - [0.0, 0.0, 1.8626]], dtype=float) * 12 - assert_allclose(mol.cellvecs, my_cellvecs, atol=1.e-5) - my_axes = np.array([[1.8626, 0.1, 0.0], - [0.0, 1.8626, 0.0], - [0.0, 0.0, 1.8626]], dtype=float) - assert_allclose(mol.cube.axes, my_axes, atol=1.e-5) - assert_allclose(mol.cube.origin, np.array([0.0, 1.2, 0.0]), atol=1.e-10) - - assert_allclose(mol.cube.data[0, 0, 0], 9.49232e-06, atol=1.e-12) - assert_allclose(mol.cube.data[-1, -1, -1], 2.09856e-04, atol=1.e-10) + my_cellvecs = ( + np.array([[1.8626, 0.1, 0.0], [0.0, 1.8626, 0.0], [0.0, 0.0, 1.8626]], dtype=float) * 12 + ) + assert_allclose(mol.cellvecs, my_cellvecs, atol=1.0e-5) + my_axes = np.array([[1.8626, 0.1, 0.0], [0.0, 1.8626, 0.0], [0.0, 0.0, 1.8626]], dtype=float) + assert_allclose(mol.cube.axes, my_axes, atol=1.0e-5) + assert_allclose(mol.cube.origin, np.array([0.0, 1.2, 0.0]), atol=1.0e-10) + + assert_allclose(mol.cube.data[0, 0, 0], 9.49232e-06, atol=1.0e-12) + assert_allclose(mol.cube.data[-1, -1, -1], 2.09856e-04, atol=1.0e-10) pn = mol.atcorenums - assert_allclose(pn[0], 1.0, atol=1.e-10) - assert_allclose(pn[1], 0.1, atol=1.e-10) - assert_allclose(pn[-2], 0.2, atol=1.e-10) - assert_allclose(pn[-1], mol.atnums[-1], atol=1.e-10) + assert_allclose(pn[0], 1.0, atol=1.0e-10) + assert_allclose(pn[1], 0.1, atol=1.0e-10) + assert_allclose(pn[-2], 0.2, atol=1.0e-10) + assert_allclose(pn[-1], mol.atnums[-1], atol=1.0e-10) def test_load_dump_load_aelta(tmpdir): with as_file(files("iodata.test.data").joinpath("aelta.cube")) as fn_cube1: mol1 = load_one(str(fn_cube1)) - fn_cube2 = '%s/%s' % (tmpdir, 'aelta.cube') + fn_cube2 = "%s/%s" % (tmpdir, "aelta.cube") dump_one(mol1, fn_cube2) mol2 = load_one(fn_cube2) @@ -75,9 +73,7 @@ def test_load_dump_load_aelta(tmpdir): assert len(line.split()) == 6 if mol2.cube.shape[2] % 6 != 0: block_line_counter = line_counter - ( - 6 - + len(mol2.atnums) - + block_counter * (mol2.cube.shape[2] // 6 + 1) + 6 + len(mol2.atnums) + block_counter * (mol2.cube.shape[2] // 6 + 1) ) if 1 <= block_line_counter <= mol2.cube.shape[2] // 6: assert len(line.split()) == 6 @@ -85,14 +81,14 @@ def test_load_dump_load_aelta(tmpdir): assert len(line.split()) == mol2.cube.shape[2] % 6 assert mol1.title == mol2.title - assert_allclose(mol1.atcoords, mol2.atcoords, atol=1.e-4) + assert_allclose(mol1.atcoords, mol2.atcoords, atol=1.0e-4) assert_equal(mol1.atnums, mol2.atnums) cube1 = mol1.cube cube2 = mol2.cube - assert_allclose(cube1.axes, cube2.axes, atol=1.e-4) + assert_allclose(cube1.axes, cube2.axes, atol=1.0e-4) assert_equal(cube1.shape, cube2.shape) - assert_allclose(mol1.cube.data, mol2.cube.data, atol=1.e-4) - assert_allclose(mol1.atcorenums, mol2.atcorenums, atol=1.e-4) + assert_allclose(mol1.cube.data, mol2.cube.data, atol=1.0e-4) + assert_allclose(mol1.atcorenums, mol2.atcorenums, atol=1.0e-4) def test_load_dump_h2o_5points(tmpdir): @@ -100,7 +96,7 @@ def test_load_dump_h2o_5points(tmpdir): with as_file(files("iodata.test.data").joinpath("cubegen_h2o_5points.cube")) as fn_cube1: mol1 = load_one(str(fn_cube1)) # write cube file in a temporary directory - fn_cube2 = tmpdir.join('iodata_h2o_5points.cube') + fn_cube2 = tmpdir.join("iodata_h2o_5points.cube") dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare with open(fn_cube1, "r") as f: @@ -114,7 +110,7 @@ def test_load_dump_ch4_6points(tmpdir): with as_file(files("iodata.test.data").joinpath("cubegen_ch4_6points.cube")) as fn_cube1: mol1 = load_one(str(fn_cube1)) # write cube file in a temporary directory - fn_cube2 = tmpdir.join('iodata_ch4_6points.cube') + fn_cube2 = tmpdir.join("iodata_ch4_6points.cube") dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare with open(fn_cube1, "r") as f: @@ -128,7 +124,7 @@ def test_load_dump_nh3_7points(tmpdir): with as_file(files("iodata.test.data").joinpath("cubegen_nh3_7points.cube")) as fn_cube1: mol1 = load_one(str(fn_cube1)) # write cube file in a temporary directory - fn_cube2 = tmpdir.join('iodata_nh3_7points.cube') + fn_cube2 = tmpdir.join("iodata_nh3_7points.cube") dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare with open(fn_cube1, "r") as f: diff --git a/iodata/test/test_extxyz.py b/iodata/test/test_extxyz.py index 04051e67..d7226958 100644 --- a/iodata/test/test_extxyz.py +++ b/iodata/test/test_extxyz.py @@ -32,18 +32,18 @@ def test_load_fcc_extended(): with as_file(files("iodata.test.data").joinpath("al_fcc.xyz")) as fn_xyz: - mol = load_one(str(fn_xyz), fmt='extxyz') - assert hasattr(mol, 'energy') + mol = load_one(str(fn_xyz), fmt="extxyz") + assert hasattr(mol, "energy") assert isinstance(mol.energy, float) assert_allclose(mol.energy, -112.846680723) - assert hasattr(mol, 'cellvecs') + assert hasattr(mol, "cellvecs") assert mol.cellvecs.dtype == float assert_allclose(mol.cellvecs, np.eye(3) * 7.6 * angstrom) - assert 'pbc' in mol.extra - assert mol.extra['pbc'].dtype == bool - assert_equal(mol.extra['pbc'], np.array([True, False, True])) - assert 'species' in mol.extra - assert_equal(mol.extra['species'], np.array(['Al'] * 32)) + assert "pbc" in mol.extra + assert mol.extra["pbc"].dtype == bool + assert_equal(mol.extra["pbc"], np.array([True, False, True])) + assert "species" in mol.extra + assert_equal(mol.extra["species"], np.array(["Al"] * 32)) assert mol.atgradient.shape == (mol.natom, 3) assert_allclose(mol.atgradient[0, 0], -0.285831) assert_allclose(mol.atgradient[2, 1], 0.268537) @@ -53,7 +53,7 @@ def test_load_fcc_extended(): def test_load_mgo(): with as_file(files("iodata.test.data").joinpath("mgo.xyz")) as fn_xyz: - mol = load_one(str(fn_xyz), fmt='extxyz') + mol = load_one(str(fn_xyz), fmt="extxyz") assert_equal(mol.atnums, [12] * 4 + [8] * 4) assert_equal(mol.atcoords[3], np.array([1, 1, 0]) * 2.10607000 * angstrom) assert_equal(mol.extra["spacegroup"], ["F", "m", "-3", "m"]) @@ -64,20 +64,21 @@ def test_load_mgo(): def test_load_many_extended(): with as_file(files("iodata.test.data").joinpath("water_extended_trajectory.xyz")) as fn_xyz: - mols = list(load_many(str(fn_xyz), fmt='extxyz')) + mols = list(load_many(str(fn_xyz), fmt="extxyz")) assert len(mols) == 3 - assert 'some_label' in mols[0].extra - assert_equal(mols[0].extra['some_label'], - np.array([[True, True], [False, True], [False, False]])) - assert 'is_true' in mols[0].extra - assert mols[0].extra['is_true'] - assert hasattr(mols[0], 'charge') + assert "some_label" in mols[0].extra + assert_equal( + mols[0].extra["some_label"], np.array([[True, True], [False, True], [False, False]]) + ) + assert "is_true" in mols[0].extra + assert mols[0].extra["is_true"] + assert hasattr(mols[0], "charge") assert_allclose(mols[0].charge, 0) - assert 'pi' in mols[1].extra - assert_equal(mols[1].extra['pi'], 3.14) + assert "pi" in mols[1].extra + assert_equal(mols[1].extra["pi"], 3.14) assert_equal(mols[1].atnums, np.array([8, 1, 1, 1])) assert_equal(mols[1].atcoords[-1, 2], -12 * angstrom) assert_equal(mols[2].atnums, np.array([8, 1, 1])) - assert hasattr(mols[2], 'atmasses') + assert hasattr(mols[2], "atmasses") assert mols[2].atmasses.dtype == float assert_allclose(mols[2].atmasses, np.array([29164.39290107, 1837.47159474, 1837.47159474])) diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index e90520bc..a8c363fb 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -51,34 +51,39 @@ def load_fchk_helper(fn_fchk): def test_load_fchk_hf_sto3g_num(): - mol = load_fchk_helper('hf_sto3g.fchk') - assert mol.title == 'hf_sto3g' - assert mol.run_type == 'energy' - assert mol.lot == 'rhf' - assert mol.obasis_name == 'sto-3g' - assert mol.mo.kind == 'restricted' + mol = load_fchk_helper("hf_sto3g.fchk") + assert mol.title == "hf_sto3g" + assert mol.run_type == "energy" + assert mol.lot == "rhf" + assert mol.obasis_name == "sto-3g" + assert mol.mo.kind == "restricted" assert mol.spinpol == 0 assert mol.obasis.nbasis == 6 assert len(mol.obasis.shells) == 3 shell0 = mol.obasis.shells[0] assert shell0.icenter == 0 assert shell0.angmoms == [0] - assert shell0.kinds == ['c'] - assert_allclose(shell0.exponents, np.array([1.66679134E+02, 3.03608123E+01, 8.21682067E+00])) - assert_allclose(shell0.coeffs, - np.array([[1.54328967E-01], [5.35328142E-01], [4.44634542E-01]])) + assert shell0.kinds == ["c"] + assert_allclose(shell0.exponents, np.array([1.66679134e02, 3.03608123e01, 8.21682067e00])) + assert_allclose(shell0.coeffs, np.array([[1.54328967e-01], [5.35328142e-01], [4.44634542e-01]])) assert shell0.nprim == 3 assert shell0.ncon == 1 assert shell0.nbasis == 1 shell1 = mol.obasis.shells[1] assert shell1.icenter == 0 assert shell1.angmoms == [0, 1] - assert shell1.kinds == ['c', 'c'] - assert_allclose(shell1.exponents, np.array([6.46480325E+00, 1.50228124E+00, 4.88588486E-01])) - assert_allclose(shell1.coeffs, - np.array([[-9.99672292E-02, 1.55916275E-01], - [3.99512826E-01, 6.07683719E-01], - [7.00115469E-01, 3.91957393E-01]])) + assert shell1.kinds == ["c", "c"] + assert_allclose(shell1.exponents, np.array([6.46480325e00, 1.50228124e00, 4.88588486e-01])) + assert_allclose( + shell1.coeffs, + np.array( + [ + [-9.99672292e-02, 1.55916275e-01], + [3.99512826e-01, 6.07683719e-01], + [7.00115469e-01, 3.91957393e-01], + ] + ), + ) assert shell1.nprim == 3 assert shell1.ncon == 2 assert shell1.nbasis == 4 @@ -86,33 +91,33 @@ def test_load_fchk_hf_sto3g_num(): assert shell2.nprim == 3 assert shell2.ncon == 1 assert shell2.nbasis == 1 - assert mol.obasis.primitive_normalization == 'L2' + assert mol.obasis.primitive_normalization == "L2" assert len(mol.atcoords) == len(mol.atnums) assert mol.atcoords.shape[1] == 3 assert len(mol.atnums) == 2 - assert_allclose(mol.energy, -9.856961609951867E+01) - assert_allclose(mol.atcharges['mulliken'], [0.45000000E+00, 4.22300000E+00]) - assert_allclose(mol.atcharges['npa'], [3.50000000E+00, 1.32000000E+00]) - assert_allclose(mol.atcharges['esp'], [0.77700000E+00, 0.66600000E+00]) + assert_allclose(mol.energy, -9.856961609951867e01) + assert_allclose(mol.atcharges["mulliken"], [0.45000000e00, 4.22300000e00]) + assert_allclose(mol.atcharges["npa"], [3.50000000e00, 1.32000000e00]) + assert_allclose(mol.atcharges["esp"], [0.77700000e00, 0.66600000e00]) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) def test_load_fchk_hf_water_atcharges(): - mol = load_fchk_helper('water_atcharges.fchk') - assert_allclose(mol.atcharges['mulliken'], [-3.91150532E-01, 1.96895396E-01, 1.94255137E-01]) - assert_allclose(mol.atcharges['npa'], [-4.98161654E-01, 2.50757174E-01, 2.47404480E-01]) - assert_allclose(mol.atcharges['esp'], [-4.47363368E-01, 2.24922518E-01, 2.22440849E-01]) - assert_allclose(mol.atcharges['mbs'], [-2.90505882E-01, 1.45850946E-01, 1.44654936E-01]) + mol = load_fchk_helper("water_atcharges.fchk") + assert_allclose(mol.atcharges["mulliken"], [-3.91150532e-01, 1.96895396e-01, 1.94255137e-01]) + assert_allclose(mol.atcharges["npa"], [-4.98161654e-01, 2.50757174e-01, 2.47404480e-01]) + assert_allclose(mol.atcharges["esp"], [-4.47363368e-01, 2.24922518e-01, 2.22440849e-01]) + assert_allclose(mol.atcharges["mbs"], [-2.90505882e-01, 1.45850946e-01, 1.44654936e-01]) # hirshfeld is under label `Type 6 charges` in fchk - assert_allclose(mol.atcharges['hirshfeld'], [-3.37450356E-01, 1.68988978E-01, 1.68461239E-01]) + assert_allclose(mol.atcharges["hirshfeld"], [-3.37450356e-01, 1.68988978e-01, 1.68461239e-01]) # cm5 is under label `Type 7 charges` in fchk - assert_allclose(mol.atcharges['cm5'], [-3.77750403E-01, 1.89459551E-01, 1.88290713E-01]) + assert_allclose(mol.atcharges["cm5"], [-3.77750403e-01, 1.89459551e-01, 1.88290713e-01]) def test_load_fchk_h_sto3g_num(): - mol = load_fchk_helper('h_sto3g.fchk') - assert mol.title == 'h_sto3g' + mol = load_fchk_helper("h_sto3g.fchk") + assert mol.title == "h_sto3g" assert len(mol.obasis.shells) == 1 assert mol.obasis.nbasis == 1 assert mol.obasis.shells[0].nprim == 3 @@ -122,27 +127,27 @@ def test_load_fchk_h_sto3g_num(): olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) check_orthonormal(mol.mo.coeffsb, olp) - assert_allclose(mol.energy, -4.665818503844346E-01) - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) - assert_allclose(mol.one_rdms['scf_spin'], mol.one_rdms['scf_spin'].T) + assert_allclose(mol.energy, -4.665818503844346e-01) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) + assert_allclose(mol.one_rdms["scf_spin"], mol.one_rdms["scf_spin"].T) def test_load_fchk_o2_cc_pvtz_pure_num(): - mol = load_fchk_helper('o2_cc_pvtz_pure.fchk') - assert mol.run_type == 'energy' - assert mol.lot == 'rhf' - assert mol.obasis_name == 'cc-pvtz' + mol = load_fchk_helper("o2_cc_pvtz_pure.fchk") + assert mol.run_type == "energy" + assert mol.lot == "rhf" + assert mol.obasis_name == "cc-pvtz" assert len(mol.obasis.shells) == 20 assert mol.obasis.nbasis == 60 assert mol.natom == 2 olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) - assert_allclose(mol.energy, -1.495944878699246E+02) - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) + assert_allclose(mol.energy, -1.495944878699246e02) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) def test_load_fchk_o2_cc_pvtz_cart_num(): - mol = load_fchk_helper('o2_cc_pvtz_cart.fchk') + mol = load_fchk_helper("o2_cc_pvtz_cart.fchk") assert len(mol.obasis.shells) == 20 assert mol.obasis.nbasis == 70 assert len(mol.atcoords) == len(mol.atnums) @@ -150,84 +155,84 @@ def test_load_fchk_o2_cc_pvtz_cart_num(): assert len(mol.atnums) == 2 olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) - assert_allclose(mol.energy, -1.495953594545721E+02) - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) + assert_allclose(mol.energy, -1.495953594545721e02) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) def test_load_fchk_water_sto3g_hf(): - mol = load_fchk_helper('water_sto3g_hf_g03.fchk') + mol = load_fchk_helper("water_sto3g_hf_g03.fchk") assert len(mol.obasis.shells) == 4 assert mol.obasis.nbasis == 7 assert len(mol.atcoords) == len(mol.atnums) assert mol.atcoords.shape[1] == 3 assert len(mol.atnums) == 3 - assert_allclose(mol.mo.energies[0], -2.02333942E+01, atol=1.e-7) - assert_allclose(mol.mo.energies[-1], 7.66134805E-01, atol=1.e-7) - assert_allclose(mol.mo.coeffs[0, 0], 0.99410, atol=1.e-4) - assert_allclose(mol.mo.coeffs[1, 0], 0.02678, atol=1.e-4) - assert_allclose(mol.mo.coeffs[-1, 2], -0.44154, atol=1.e-4) + assert_allclose(mol.mo.energies[0], -2.02333942e01, atol=1.0e-7) + assert_allclose(mol.mo.energies[-1], 7.66134805e-01, atol=1.0e-7) + assert_allclose(mol.mo.coeffs[0, 0], 0.99410, atol=1.0e-4) + assert_allclose(mol.mo.coeffs[1, 0], 0.02678, atol=1.0e-4) + assert_allclose(mol.mo.coeffs[-1, 2], -0.44154, atol=1.0e-4) assert abs(mol.mo.coeffs[3, -1]) < 1e-4 - assert_allclose(mol.mo.coeffs[4, -1], -0.82381, atol=1.e-4) + assert_allclose(mol.mo.coeffs[4, -1], -0.82381, atol=1.0e-4) assert_equal(mol.mo.occs.sum(), 10) assert_equal(mol.mo.occs.min(), 0.0) assert_equal(mol.mo.occs.max(), 2.0) - assert_allclose(mol.energy, -7.495929232844363E+01) + assert_allclose(mol.energy, -7.495929232844363e01) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) check_orthonormal(mol.mo.coeffsa, olp) - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) def test_load_fchk_water_sto3g_hf_qchem(): # test FCHK file generated by QChem-5.2.1 which misses 'Total Energy' field - mol = load_fchk_helper('water_hf_sto3g_qchem5.2.fchk') + mol = load_fchk_helper("water_hf_sto3g_qchem5.2.fchk") assert mol.energy is None assert len(mol.obasis.shells) == 4 assert mol.obasis.nbasis == 7 assert mol.atcoords.shape == (3, 3) assert_equal(mol.atnums, [8, 1, 1]) - assert_allclose(mol.mo.energies[0], -2.02531445E+01, atol=1.e-7) - assert_allclose(mol.mo.energies[-1], 5.39983862E-01, atol=1.e-7) - assert_allclose(mol.mo.coeffs[0, 0], 9.94571479E-01, atol=1.e-7) - assert_allclose(mol.mo.coeffs[1, 0], 2.30506686E-02, atol=1.e-7) - assert_allclose(mol.mo.coeffs[-1, -1], 6.71330643E-01, atol=1.e-7) - assert_equal(mol.mo.occs, [2., 2., 2., 2., 2., 0., 0.]) + assert_allclose(mol.mo.energies[0], -2.02531445e01, atol=1.0e-7) + assert_allclose(mol.mo.energies[-1], 5.39983862e-01, atol=1.0e-7) + assert_allclose(mol.mo.coeffs[0, 0], 9.94571479e-01, atol=1.0e-7) + assert_allclose(mol.mo.coeffs[1, 0], 2.30506686e-02, atol=1.0e-7) + assert_allclose(mol.mo.coeffs[-1, -1], 6.71330643e-01, atol=1.0e-7) + assert_equal(mol.mo.occs, [2.0, 2.0, 2.0, 2.0, 2.0, 0.0, 0.0]) # check molecular orbitals are orthonormal olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) check_orthonormal(mol.mo.coeffsa, olp) # check 1-RDM - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) - assert_allclose(mol.one_rdms['scf'], compute_1rdm(mol)) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) + assert_allclose(mol.one_rdms["scf"], compute_1rdm(mol)) def test_load_fchk_lih_321g_hf(): - mol = load_fchk_helper('li_h_3-21G_hf_g09.fchk') + mol = load_fchk_helper("li_h_3-21G_hf_g09.fchk") assert len(mol.obasis.shells) == 5 assert mol.obasis.nbasis == 11 assert len(mol.atcoords) == len(mol.atnums) assert mol.atcoords.shape[1] == 3 assert len(mol.atnums) == 2 - assert_allclose(mol.energy, -7.687331212191968E+00) - - assert_allclose(mol.mo.energiesa[0], (-2.76117), atol=1.e-4) - assert_allclose(mol.mo.energiesa[-1], 0.97089, atol=1.e-4) - assert_allclose(mol.mo.coeffsa[0, 0], 0.99105, atol=1.e-4) - assert_allclose(mol.mo.coeffsa[1, 0], 0.06311, atol=1.e-4) - assert mol.mo.coeffsa[3, 2] < 1.e-4 - assert_allclose(mol.mo.coeffsa[-1, 9], 0.13666, atol=1.e-4) - assert_allclose(mol.mo.coeffsa[4, -1], 0.17828, atol=1.e-4) + assert_allclose(mol.energy, -7.687331212191968e00) + + assert_allclose(mol.mo.energiesa[0], (-2.76117), atol=1.0e-4) + assert_allclose(mol.mo.energiesa[-1], 0.97089, atol=1.0e-4) + assert_allclose(mol.mo.coeffsa[0, 0], 0.99105, atol=1.0e-4) + assert_allclose(mol.mo.coeffsa[1, 0], 0.06311, atol=1.0e-4) + assert mol.mo.coeffsa[3, 2] < 1.0e-4 + assert_allclose(mol.mo.coeffsa[-1, 9], 0.13666, atol=1.0e-4) + assert_allclose(mol.mo.coeffsa[4, -1], 0.17828, atol=1.0e-4) assert_equal(mol.mo.occsa.sum(), 2) assert_equal(mol.mo.occsa.min(), 0.0) assert_equal(mol.mo.occsa.max(), 1.0) - assert_allclose(mol.mo.energiesb[0], -2.76031, atol=1.e-4) - assert_allclose(mol.mo.energiesb[-1], 1.13197, atol=1.e-4) - assert_allclose(mol.mo.coeffsb[0, 0], 0.99108, atol=1.e-4) - assert_allclose(mol.mo.coeffsb[1, 0], 0.06295, atol=1.e-4) + assert_allclose(mol.mo.energiesb[0], -2.76031, atol=1.0e-4) + assert_allclose(mol.mo.energiesb[-1], 1.13197, atol=1.0e-4) + assert_allclose(mol.mo.coeffsb[0, 0], 0.99108, atol=1.0e-4) + assert_allclose(mol.mo.coeffsb[1, 0], 0.06295, atol=1.0e-4) assert abs(mol.mo.coeffsb[3, 2]) < 1e-4 - assert_allclose(mol.mo.coeffsb[-1, 9], 0.80875, atol=1.e-4) - assert_allclose(mol.mo.coeffsb[4, -1], -0.15503, atol=1.e-4) + assert_allclose(mol.mo.coeffsb[-1, 9], 0.80875, atol=1.0e-4) + assert_allclose(mol.mo.coeffsb[4, -1], -0.15503, atol=1.0e-4) assert_equal(mol.mo.occsb.sum(), 1) assert_equal(mol.mo.occsb.min(), 0.0) assert_equal(mol.mo.occsb.max(), 1.0) @@ -237,30 +242,30 @@ def test_load_fchk_lih_321g_hf(): olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) check_orthonormal(mol.mo.coeffsb, olp) - assert_allclose(mol.one_rdms['scf'], mol.one_rdms['scf'].T) - assert_allclose(mol.one_rdms['scf_spin'], mol.one_rdms['scf_spin'].T) + assert_allclose(mol.one_rdms["scf"], mol.one_rdms["scf"].T) + assert_allclose(mol.one_rdms["scf_spin"], mol.one_rdms["scf_spin"].T) def test_load_fchk_ghost_atoms(): # Load fchk file with ghost atoms - mol = load_fchk_helper('water_dimer_ghost.fchk') + mol = load_fchk_helper("water_dimer_ghost.fchk") # There should be 3 real atoms and 3 ghost atoms assert mol.natom == 6 assert_equal(mol.atnums, [1, 8, 1, 1, 8, 1]) assert_equal(mol.atcorenums, [1.0, 8.0, 1.0, 0.0, 0.0, 0.0]) assert_equal(mol.atcoords.shape[0], 6) - assert_equal(mol.atcharges['mulliken'].shape[0], 6) + assert_equal(mol.atcharges["mulliken"].shape[0], 6) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) def test_load_fchk_ch3_rohf_g03(): - mol = load_fchk_helper('ch3_rohf_sto3g_g03.fchk') + mol = load_fchk_helper("ch3_rohf_sto3g_g03.fchk") assert_equal(mol.mo.occs.shape[0], mol.mo.coeffs.shape[0]) assert_equal(mol.mo.occs.sum(), 9.0) assert_equal(mol.mo.occs.min(), 0.0) assert_equal(mol.mo.occs.max(), 2.0) - assert 'scf' not in mol.one_rdms # It should be skipped when loading fchk. + assert "scf" not in mol.one_rdms # It should be skipped when loading fchk. olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) check_orthonormal(mol.mo.coeffsb, olp) @@ -268,9 +273,9 @@ def test_load_fchk_ch3_rohf_g03(): def check_load_azirine(key, numbers): """Perform some basic checks on a azirine fchk file.""" - mol = load_fchk_helper('2h-azirine-{}.fchk'.format(key)) + mol = load_fchk_helper("2h-azirine-{}.fchk".format(key)) assert mol.obasis.nbasis == 33 - dm = mol.one_rdms['post_scf_ao'] + dm = mol.one_rdms["post_scf_ao"] assert_equal(dm[0, 0], numbers[0]) assert_equal(dm[32, 32], numbers[1]) olp = compute_overlap(mol.obasis, mol.atcoords) @@ -278,108 +283,114 @@ def check_load_azirine(key, numbers): def test_load_azirine_cc(): - check_load_azirine('cc', [2.08221382E+00, 1.03516466E-01]) + check_load_azirine("cc", [2.08221382e00, 1.03516466e-01]) def test_load_azirine_ci(): - check_load_azirine('ci', [2.08058265E+00, 6.12011064E-02]) + check_load_azirine("ci", [2.08058265e00, 6.12011064e-02]) def test_load_azirine_mp2(): - check_load_azirine('mp2', [2.08253448E+00, 1.09305208E-01]) + check_load_azirine("mp2", [2.08253448e00, 1.09305208e-01]) def test_load_azirine_mp3(): - check_load_azirine('mp3', [2.08243417E+00, 1.02590815E-01]) + check_load_azirine("mp3", [2.08243417e00, 1.02590815e-01]) def check_load_nitrogen(key, numbers, numbers_spin): """Perform some basic checks on a nitrogen fchk file.""" - mol = load_fchk_helper('nitrogen-{}.fchk'.format(key)) + mol = load_fchk_helper("nitrogen-{}.fchk".format(key)) assert mol.obasis.nbasis == 9 - dm = mol.one_rdms['post_scf_ao'] + dm = mol.one_rdms["post_scf_ao"] assert_equal(dm[0, 0], numbers[0]) assert_equal(dm[8, 8], numbers[1]) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) check_orthonormal(mol.mo.coeffsb, olp) check_dm(dm, olp, eps=1e-3, occ_max=2) - assert_allclose(np.einsum('ab,ba', olp, dm), 7.0, atol=1.e-7, rtol=0) - dm_spin = mol.one_rdms['post_scf_spin_ao'] + assert_allclose(np.einsum("ab,ba", olp, dm), 7.0, atol=1.0e-7, rtol=0) + dm_spin = mol.one_rdms["post_scf_spin_ao"] assert_equal(dm_spin[0, 0], numbers_spin[0]) assert_equal(dm_spin[8, 8], numbers_spin[1]) def test_load_nitrogen_cc(): - check_load_nitrogen('cc', [2.08709209E+00, 3.74723580E-01], [7.25882619E-04, -1.38368575E-02]) + check_load_nitrogen("cc", [2.08709209e00, 3.74723580e-01], [7.25882619e-04, -1.38368575e-02]) def test_load_nitrogen_ci(): - check_load_nitrogen('ci', [2.08741410E+00, 2.09292886E-01], [7.41998558E-04, -6.67582215E-03]) + check_load_nitrogen("ci", [2.08741410e00, 2.09292886e-01], [7.41998558e-04, -6.67582215e-03]) def test_load_nitrogen_mp2(): - check_load_nitrogen('mp2', [2.08710027E+00, 4.86472609E-01], [7.31802950E-04, -2.00028488E-02]) + check_load_nitrogen("mp2", [2.08710027e00, 4.86472609e-01], [7.31802950e-04, -2.00028488e-02]) def test_load_nitrogen_mp3(): - check_load_nitrogen('mp3', [2.08674302E+00, 4.91149023E-01], [7.06941101E-04, -1.96276763E-02]) + check_load_nitrogen("mp3", [2.08674302e00, 4.91149023e-01], [7.06941101e-04, -1.96276763e-02]) def check_normalization_dm_azirine(key): """Perform some basic checks on a 2h-azirine fchk file.""" - mol = load_fchk_helper('2h-azirine-{}.fchk'.format(key)) + mol = load_fchk_helper("2h-azirine-{}.fchk".format(key)) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) - dm = mol.one_rdms['post_scf_ao'] + dm = mol.one_rdms["post_scf_ao"] check_dm(dm, olp, eps=1e-2, occ_max=2) - assert_allclose(np.einsum('ab,ba', olp, dm), 22.0, atol=1.e-8, rtol=0) + assert_allclose(np.einsum("ab,ba", olp, dm), 22.0, atol=1.0e-8, rtol=0) def test_normalization_dm_azirine_cc(): - check_normalization_dm_azirine('cc') + check_normalization_dm_azirine("cc") def test_normalization_dm_azirine_ci(): - check_normalization_dm_azirine('ci') + check_normalization_dm_azirine("ci") def test_normalization_dm_azirine_mp2(): - check_normalization_dm_azirine('mp2') + check_normalization_dm_azirine("mp2") def test_normalization_dm_azirine_mp3(): - check_normalization_dm_azirine('mp3') + check_normalization_dm_azirine("mp3") def test_load_water_hfs_321g(): - mol = load_fchk_helper('water_hfs_321g.fchk') - pol = mol.extra['polarizability_tensor'] - assert_allclose(pol[0, 0], 7.23806684E+00) - assert_allclose(pol[1, 1], 8.04213953E+00) - assert_allclose(pol[1, 2], 1.20021770E-10) - assert_allclose(mol.moments[(1, 'c')], - [-5.82654324E-17, 0.00000000E+00, -8.60777067E-01]) - assert_allclose(mol.moments[(2, 'c')], - [-8.89536026E-01, # xx - 8.28408371E-17, # xy - 4.89353090E-17, # xz - 1.14114241E+00, # yy - -5.47382213E-48, # yz - -2.51606382E-01]) # zz + mol = load_fchk_helper("water_hfs_321g.fchk") + pol = mol.extra["polarizability_tensor"] + assert_allclose(pol[0, 0], 7.23806684e00) + assert_allclose(pol[1, 1], 8.04213953e00) + assert_allclose(pol[1, 2], 1.20021770e-10) + assert_allclose(mol.moments[(1, "c")], [-5.82654324e-17, 0.00000000e00, -8.60777067e-01]) + assert_allclose( + mol.moments[(2, "c")], + [ + -8.89536026e-01, # xx + 8.28408371e-17, # xy + 4.89353090e-17, # xz + 1.14114241e00, # yy + -5.47382213e-48, # yz + -2.51606382e-01, + ], + ) # zz def test_load_monosilicic_acid_hf_lan(): - mol = load_fchk_helper('monosilicic_acid_hf_lan.fchk') - assert_allclose(mol.moments[(1, 'c')], - [-6.05823053E-01, -9.39656399E-03, 4.18948869E-01]) - assert_allclose(mol.moments[(2, 'c')], - [2.73609152E+00, # xx - -6.65787832E-02, # xy - 2.11973730E-01, # xz - 8.97029351E-01, # yy - -1.38159653E-02, # yz - -3.63312087E+00]) # zz + mol = load_fchk_helper("monosilicic_acid_hf_lan.fchk") + assert_allclose(mol.moments[(1, "c")], [-6.05823053e-01, -9.39656399e-03, 4.18948869e-01]) + assert_allclose( + mol.moments[(2, "c")], + [ + 2.73609152e00, # xx + -6.65787832e-02, # xy + 2.11973730e-01, # xz + 8.97029351e-01, # yy + -1.38159653e-02, # yz + -3.63312087e00, + ], + ) # zz assert_allclose(mol.atgradient[0], [0.0, 0.0, 0.0]) olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) @@ -400,134 +411,118 @@ def check_trj_basics(trj, nsteps, title, irc): for ipoint, nstep in enumerate(nsteps): for istep in range(nstep): mol = trj.pop(0) - assert mol.extra['ipoint'] == ipoint - assert mol.extra['npoint'] == len(nsteps) - assert mol.extra['istep'] == istep - assert mol.extra['nstep'] == nstep + assert mol.extra["ipoint"] == ipoint + assert mol.extra["npoint"] == len(nsteps) + assert mol.extra["istep"] == istep + assert mol.extra["nstep"] == nstep assert mol.natom == natom - assert mol.atnums.shape == (natom, ) + assert mol.atnums.shape == (natom,) assert_equal(mol.atnums, [8, 8, 1, 1]) - assert mol.atcorenums.shape == (natom, ) + assert mol.atcorenums.shape == (natom,) assert mol.atcoords.shape == (natom, 3) assert mol.atgradient.shape == (natom, 3) assert mol.title == title assert mol.energy is not None - assert ('reaction_coordinate' in mol.extra) ^ (not irc) + assert ("reaction_coordinate" in mol.extra) ^ (not irc) def test_peroxide_opt(): trj = load_fchk_trj_helper("peroxide_opt.fchk") - check_trj_basics(trj, [5], 'opt', False) - assert_allclose(trj[0].energy, -1.48759755E+02) - assert_allclose(trj[1].energy, -1.48763504E+02) - assert_allclose(trj[-1].energy, -1.48764883E+02) - assert_allclose(trj[0].atcoords[1], - [9.02056208E-17, -1.37317707E+00, 0.00000000E+00]) - assert_allclose(trj[-1].atcoords[-1], - [-1.85970174E+00, -1.64631025E+00, 0.00000000E+00]) - assert_allclose(trj[2].atgradient[0], - [-5.19698814E-03, -1.17503170E-03, -1.06165077E-15]) - assert_allclose(trj[3].atgradient[2], - [-8.70435823E-04, 1.44609443E-03, -3.79091290E-16]) + check_trj_basics(trj, [5], "opt", False) + assert_allclose(trj[0].energy, -1.48759755e02) + assert_allclose(trj[1].energy, -1.48763504e02) + assert_allclose(trj[-1].energy, -1.48764883e02) + assert_allclose(trj[0].atcoords[1], [9.02056208e-17, -1.37317707e00, 0.00000000e00]) + assert_allclose(trj[-1].atcoords[-1], [-1.85970174e00, -1.64631025e00, 0.00000000e00]) + assert_allclose(trj[2].atgradient[0], [-5.19698814e-03, -1.17503170e-03, -1.06165077e-15]) + assert_allclose(trj[3].atgradient[2], [-8.70435823e-04, 1.44609443e-03, -3.79091290e-16]) def test_peroxide_tsopt(): trj = load_fchk_trj_helper("peroxide_tsopt.fchk") - check_trj_basics(trj, [3], 'tsopt', False) - assert_allclose(trj[0].energy, -1.48741996E+02) - assert_allclose(trj[1].energy, -1.48750392E+02) - assert_allclose(trj[2].energy, -1.48750432E+02) - assert_allclose(trj[0].atcoords[3], - [-2.40150648E-01, -1.58431001E+00, 1.61489448E+00]) - assert_allclose(trj[2].atcoords[2], - [1.26945011E-03, 1.81554334E+00, 1.62426250E+00]) - assert_allclose(trj[1].atgradient[1], - [-8.38752120E-04, 3.46889422E-03, 1.96559245E-03]) - assert_allclose(trj[-1].atgradient[0], - [2.77986102E-05, -1.74709101E-05, 2.45875530E-05]) + check_trj_basics(trj, [3], "tsopt", False) + assert_allclose(trj[0].energy, -1.48741996e02) + assert_allclose(trj[1].energy, -1.48750392e02) + assert_allclose(trj[2].energy, -1.48750432e02) + assert_allclose(trj[0].atcoords[3], [-2.40150648e-01, -1.58431001e00, 1.61489448e00]) + assert_allclose(trj[2].atcoords[2], [1.26945011e-03, 1.81554334e00, 1.62426250e00]) + assert_allclose(trj[1].atgradient[1], [-8.38752120e-04, 3.46889422e-03, 1.96559245e-03]) + assert_allclose(trj[-1].atgradient[0], [2.77986102e-05, -1.74709101e-05, 2.45875530e-05]) def test_peroxide_relaxed_scan(): trj = load_fchk_trj_helper("peroxide_relaxed_scan.fchk") - check_trj_basics(trj, [6, 1, 1, 1, 2, 2], 'relaxed scan', False) - assert_allclose(trj[0].energy, -1.48759755E+02) - assert_allclose(trj[10].energy, -1.48764896E+02) - assert_allclose(trj[-1].energy, -1.48764905E+02) - assert_allclose(trj[1].atcoords[3], - [-1.85942837E+00, -1.70565735E+00, -1.11022302E-16]) - assert_allclose(trj[5].atcoords[0], - [-1.21430643E-16, 1.32466211E+00, 3.46944695E-17]) - assert_allclose(trj[8].atgradient[1], - [2.46088230E-04, -4.46299289E-04, -3.21529658E-05]) - assert_allclose(trj[9].atgradient[2], - [-1.02574260E-04, -3.33214833E-04, 5.27406641E-05]) + check_trj_basics(trj, [6, 1, 1, 1, 2, 2], "relaxed scan", False) + assert_allclose(trj[0].energy, -1.48759755e02) + assert_allclose(trj[10].energy, -1.48764896e02) + assert_allclose(trj[-1].energy, -1.48764905e02) + assert_allclose(trj[1].atcoords[3], [-1.85942837e00, -1.70565735e00, -1.11022302e-16]) + assert_allclose(trj[5].atcoords[0], [-1.21430643e-16, 1.32466211e00, 3.46944695e-17]) + assert_allclose(trj[8].atgradient[1], [2.46088230e-04, -4.46299289e-04, -3.21529658e-05]) + assert_allclose(trj[9].atgradient[2], [-1.02574260e-04, -3.33214833e-04, 5.27406641e-05]) def test_peroxide_irc(): trj = load_fchk_trj_helper("peroxide_irc.fchk") - check_trj_basics(trj, [21], 'irc', True) - assert_allclose(trj[0].energy, -1.48750432E+02) - assert_allclose(trj[5].energy, -1.48752713E+02) - assert_allclose(trj[-1].energy, -1.48757803E+02) - assert trj[0].extra['reaction_coordinate'] == 0.0 - assert_allclose(trj[1].extra['reaction_coordinate'], 1.05689581E-01) - assert_allclose(trj[10].extra['reaction_coordinate'], 1.05686037E+00) - assert_allclose(trj[-1].extra['reaction_coordinate'], -1.05685760E+00) - assert_allclose(trj[0].atcoords[2], - [-1.94749866E+00, -5.22905491E-01, -1.47814774E+00]) - assert_allclose(trj[10].atcoords[1], - [1.31447798E+00, 1.55994117E-01, -5.02320861E-02]) - assert_allclose(trj[15].atgradient[3], - [4.73066407E-04, -5.36135653E-03, 2.16301508E-04]) - assert_allclose(trj[-1].atgradient[0], - [-1.27710420E-03, -6.90543903E-03, 4.49870405E-03]) + check_trj_basics(trj, [21], "irc", True) + assert_allclose(trj[0].energy, -1.48750432e02) + assert_allclose(trj[5].energy, -1.48752713e02) + assert_allclose(trj[-1].energy, -1.48757803e02) + assert trj[0].extra["reaction_coordinate"] == 0.0 + assert_allclose(trj[1].extra["reaction_coordinate"], 1.05689581e-01) + assert_allclose(trj[10].extra["reaction_coordinate"], 1.05686037e00) + assert_allclose(trj[-1].extra["reaction_coordinate"], -1.05685760e00) + assert_allclose(trj[0].atcoords[2], [-1.94749866e00, -5.22905491e-01, -1.47814774e00]) + assert_allclose(trj[10].atcoords[1], [1.31447798e00, 1.55994117e-01, -5.02320861e-02]) + assert_allclose(trj[15].atgradient[3], [4.73066407e-04, -5.36135653e-03, 2.16301508e-04]) + assert_allclose(trj[-1].atgradient[0], [-1.27710420e-03, -6.90543903e-03, 4.49870405e-03]) def test_atgradient(): - mol = load_fchk_helper('peroxide_tsopt.fchk') - assert_allclose(mol.atgradient[0], [2.77986102E-05, -1.74709101E-05, 2.45875530E-05]) - assert_allclose(mol.atgradient[-1], [2.03469628E-05, 1.49353694E-05, -2.45875530E-05]) + mol = load_fchk_helper("peroxide_tsopt.fchk") + assert_allclose(mol.atgradient[0], [2.77986102e-05, -1.74709101e-05, 2.45875530e-05]) + assert_allclose(mol.atgradient[-1], [2.03469628e-05, 1.49353694e-05, -2.45875530e-05]) def test_athessian(): - mol = load_fchk_helper('peroxide_tsopt.fchk') - assert mol.run_type == 'freq' - assert mol.lot == 'rhf' - assert mol.obasis_name == 'sto-3g' - assert_allclose(mol.athessian[0, 0], -1.49799052E-02) - assert_allclose(mol.athessian[-1, -1], 5.83032386E-01) - assert_allclose(mol.athessian[0, 1], 5.07295215E-05) - assert_allclose(mol.athessian[1, 0], 5.07295215E-05) + mol = load_fchk_helper("peroxide_tsopt.fchk") + assert mol.run_type == "freq" + assert mol.lot == "rhf" + assert mol.obasis_name == "sto-3g" + assert_allclose(mol.athessian[0, 0], -1.49799052e-02) + assert_allclose(mol.athessian[-1, -1], 5.83032386e-01) + assert_allclose(mol.athessian[0, 1], 5.07295215e-05) + assert_allclose(mol.athessian[1, 0], 5.07295215e-05) assert mol.athessian.shape == (3 * mol.natom, 3 * mol.natom) def test_atfrozen(): - mol = load_fchk_helper('peroxide_tsopt.fchk') + mol = load_fchk_helper("peroxide_tsopt.fchk") assert_equal(mol.atfrozen, [False, False, False, True]) def test_atmasses(): - mol = load_fchk_helper('peroxide_tsopt.fchk') + mol = load_fchk_helper("peroxide_tsopt.fchk") assert_allclose(mol.atmasses[0], 29156.94, atol=0.1) assert_allclose(mol.atmasses[-1], 1837.15, atol=0.1) def test_spinpol(): - mol1 = load_fchk_helper('ch3_rohf_sto3g_g03.fchk') - assert mol1.mo.kind == 'restricted' + mol1 = load_fchk_helper("ch3_rohf_sto3g_g03.fchk") + assert mol1.mo.kind == "restricted" assert mol1.spinpol == 1 - mol2 = load_fchk_helper('li_h_3-21G_hf_g09.fchk') - assert mol2.mo.kind == 'unrestricted' + mol2 = load_fchk_helper("li_h_3-21G_hf_g09.fchk") + assert mol2.mo.kind == "unrestricted" assert mol2.spinpol == 1 with pytest.raises(TypeError): mol2.spinpol = 2 def test_nelec_charge(): - mol1 = load_fchk_helper('ch3_rohf_sto3g_g03.fchk') + mol1 = load_fchk_helper("ch3_rohf_sto3g_g03.fchk") assert mol1.nelec == 9 assert mol1.charge == 0 - mol2 = load_fchk_helper('li_h_3-21G_hf_g09.fchk') + mol2 = load_fchk_helper("li_h_3-21G_hf_g09.fchk") assert mol2.nelec == 3 assert mol2.charge == 1 with pytest.raises(TypeError): @@ -538,11 +533,11 @@ def test_nelec_charge(): def test_load_nbasis_indep(tmpdir): # Normal case - mol1 = load_fchk_helper('li2_g09_nbasis_indep.fchk') + mol1 = load_fchk_helper("li2_g09_nbasis_indep.fchk") assert mol1.mo.coeffs.shape == (38, 37) # Fake an old g03 fchk file by rewriting one line with as_file(files("iodata.test.data").joinpath("li2_g09_nbasis_indep.fchk")) as fnin: - fnout = os.path.join(tmpdir, 'tmpg03.fchk') + fnout = os.path.join(tmpdir, "tmpg03.fchk") with open(fnin) as fin, open(fnout, "w") as fout: for line in fin: fout.write(line.replace("independent", "independant")) @@ -565,174 +560,174 @@ def check_load_dump_consistency(tmpdir: str, fn: str, match: str = None): """ mol1 = load_one_warning(fn, match=match) - fn_tmp = os.path.join(tmpdir, 'foo.bar') - dump_one(mol1, fn_tmp, fmt='fchk') - mol2 = load_one(fn_tmp, fmt='fchk') + fn_tmp = os.path.join(tmpdir, "foo.bar") + dump_one(mol1, fn_tmp, fmt="fchk") + mol2 = load_one(fn_tmp, fmt="fchk") # compare molecules - if fn.endswith('fchk'): + if fn.endswith("fchk"): compare_mols(mol1, mol2) else: compare_mols_diff_formats(mol1, mol2) def test_dump_fchk_from_fchk_hf(tmpdir): - check_load_dump_consistency(tmpdir, 'hf_sto3g.fchk') + check_load_dump_consistency(tmpdir, "hf_sto3g.fchk") def test_dump_fchk_from_fchk_h2o(tmpdir): - check_load_dump_consistency(tmpdir, 'h2o_sto3g.fchk') + check_load_dump_consistency(tmpdir, "h2o_sto3g.fchk") def test_dump_fchk_from_fchk_water_atcharges(tmpdir): - check_load_dump_consistency(tmpdir, 'water_atcharges.fchk') + check_load_dump_consistency(tmpdir, "water_atcharges.fchk") def test_dump_fchk_from_fchk_h2o_qchem(tmpdir): # test FCHK file generated by QChem-5.2.1 which misses 'Total Energy' field - check_load_dump_consistency(tmpdir, 'water_hf_sto3g_qchem5.2.fchk') + check_load_dump_consistency(tmpdir, "water_hf_sto3g_qchem5.2.fchk") def test_dump_fchk_from_fchk_peroxide_irc(tmpdir): - check_load_dump_consistency(tmpdir, 'peroxide_irc.fchk') + check_load_dump_consistency(tmpdir, "peroxide_irc.fchk") def test_dump_fchk_from_fchk_he(tmpdir): - check_load_dump_consistency(tmpdir, 'he_s_orbital.fchk') - check_load_dump_consistency(tmpdir, 'he_sp_orbital.fchk') - check_load_dump_consistency(tmpdir, 'he_spd_orbital.fchk') - check_load_dump_consistency(tmpdir, 'he_spdf_orbital.fchk') - check_load_dump_consistency(tmpdir, 'he_spdfgh_orbital.fchk') - check_load_dump_consistency(tmpdir, 'he_s_virtual.fchk') - check_load_dump_consistency(tmpdir, 'he_spdfgh_virtual.fchk') + check_load_dump_consistency(tmpdir, "he_s_orbital.fchk") + check_load_dump_consistency(tmpdir, "he_sp_orbital.fchk") + check_load_dump_consistency(tmpdir, "he_spd_orbital.fchk") + check_load_dump_consistency(tmpdir, "he_spdf_orbital.fchk") + check_load_dump_consistency(tmpdir, "he_spdfgh_orbital.fchk") + check_load_dump_consistency(tmpdir, "he_s_virtual.fchk") + check_load_dump_consistency(tmpdir, "he_spdfgh_virtual.fchk") def test_dump_fchk_from_fchk_o2(tmpdir): - check_load_dump_consistency(tmpdir, 'o2_cc_pvtz_cart.fchk') - check_load_dump_consistency(tmpdir, 'o2_cc_pvtz_pure.fchk') + check_load_dump_consistency(tmpdir, "o2_cc_pvtz_cart.fchk") + check_load_dump_consistency(tmpdir, "o2_cc_pvtz_pure.fchk") def test_dump_fchk_from_fchk_water_dimer_ghost(tmpdir): - check_load_dump_consistency(tmpdir, 'water_dimer_ghost.fchk') + check_load_dump_consistency(tmpdir, "water_dimer_ghost.fchk") def test_dump_fchk_from_molden_f(tmpdir): - check_load_dump_consistency(tmpdir, 'F.molden', "PSI4") + check_load_dump_consistency(tmpdir, "F.molden", "PSI4") def test_dump_fchk_from_molden_ne(tmpdir): - check_load_dump_consistency(tmpdir, 'neon_turbomole_def2-qzvp.molden', "Turbomole") + check_load_dump_consistency(tmpdir, "neon_turbomole_def2-qzvp.molden", "Turbomole") def test_dump_fchk_from_molden_he2(tmpdir): - check_load_dump_consistency(tmpdir, 'he2_ghost_psi4_1.0.molden') + check_load_dump_consistency(tmpdir, "he2_ghost_psi4_1.0.molden") def test_dump_fchk_from_molden_nh3(tmpdir): - check_load_dump_consistency(tmpdir, 'nh3_orca.molden', "ORCA") - check_load_dump_consistency(tmpdir, 'nh3_psi4.molden', "PSI4") - check_load_dump_consistency(tmpdir, 'nh3_psi4_1.0.molden', "unnormalized") - check_load_dump_consistency(tmpdir, 'nh3_molpro2012.molden') - check_load_dump_consistency(tmpdir, 'nh3_molden_cart.molden') - check_load_dump_consistency(tmpdir, 'nh3_molden_pure.molden') - check_load_dump_consistency(tmpdir, 'nh3_turbomole.molden', "Turbomole") + check_load_dump_consistency(tmpdir, "nh3_orca.molden", "ORCA") + check_load_dump_consistency(tmpdir, "nh3_psi4.molden", "PSI4") + check_load_dump_consistency(tmpdir, "nh3_psi4_1.0.molden", "unnormalized") + check_load_dump_consistency(tmpdir, "nh3_molpro2012.molden") + check_load_dump_consistency(tmpdir, "nh3_molden_cart.molden") + check_load_dump_consistency(tmpdir, "nh3_molden_pure.molden") + check_load_dump_consistency(tmpdir, "nh3_turbomole.molden", "Turbomole") def test_dump_fchk_from_wfn_he(tmpdir): - check_load_dump_consistency(tmpdir, 'he_s_virtual.wfn') - check_load_dump_consistency(tmpdir, 'he_s_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_p_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_d_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_sp_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_spd_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_spdf_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_spdfgh_orbital.wfn') - check_load_dump_consistency(tmpdir, 'he_spdfgh_virtual.wfn') + check_load_dump_consistency(tmpdir, "he_s_virtual.wfn") + check_load_dump_consistency(tmpdir, "he_s_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_p_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_d_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_sp_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_spd_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_spdf_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_spdfgh_orbital.wfn") + check_load_dump_consistency(tmpdir, "he_spdfgh_virtual.wfn") def test_dump_fchk_from_wfn_li(tmpdir): - check_load_dump_consistency(tmpdir, 'li_sp_virtual.wfn') - check_load_dump_consistency(tmpdir, 'li_sp_orbital.wfn') + check_load_dump_consistency(tmpdir, "li_sp_virtual.wfn") + check_load_dump_consistency(tmpdir, "li_sp_orbital.wfn") def test_dump_fchk_from_wfn_lih_cation(tmpdir): - check_load_dump_consistency(tmpdir, 'lih_cation_uhf.wfn') - check_load_dump_consistency(tmpdir, 'lih_cation_rohf.wfn') + check_load_dump_consistency(tmpdir, "lih_cation_uhf.wfn") + check_load_dump_consistency(tmpdir, "lih_cation_rohf.wfn") def test_dump_fchk_from_wfn_cisd_lih_cation(tmpdir): - check_load_dump_consistency(tmpdir, 'lih_cation_cisd.wfn') + check_load_dump_consistency(tmpdir, "lih_cation_cisd.wfn") def test_dump_fchk_from_wfn_fci_lih_cation(tmpdir): # Fractional occupations are not supported in FCHK and we have no # alternative for solution for this yet. with pytest.raises(ValueError): - check_load_dump_consistency(tmpdir, 'lih_cation_fci.wfn') + check_load_dump_consistency(tmpdir, "lih_cation_fci.wfn") def test_dump_fchk_from_wfn_fci_lif(tmpdir): # Fractional occupations are not supported in FCHK and we have no # alternative for solution for this yet. with pytest.raises(ValueError): - check_load_dump_consistency(tmpdir, 'lif_fci.wfn') + check_load_dump_consistency(tmpdir, "lif_fci.wfn") def test_dump_fchk_from_wfn_h2(tmpdir): - check_load_dump_consistency(tmpdir, 'h2_ccpvqz.wfn') + check_load_dump_consistency(tmpdir, "h2_ccpvqz.wfn") def test_dump_fchk_from_wfn_o2(tmpdir): - check_load_dump_consistency(tmpdir, 'o2_uhf_virtual.wfn') - check_load_dump_consistency(tmpdir, 'o2_uhf.wfn') + check_load_dump_consistency(tmpdir, "o2_uhf_virtual.wfn") + check_load_dump_consistency(tmpdir, "o2_uhf.wfn") def test_dump_fchk_from_wfn_h2o(tmpdir): - check_load_dump_consistency(tmpdir, 'h2o_sto3g.wfn') - check_load_dump_consistency(tmpdir, 'h2o_sto3g_decontracted.wfn') + check_load_dump_consistency(tmpdir, "h2o_sto3g.wfn") + check_load_dump_consistency(tmpdir, "h2o_sto3g_decontracted.wfn") def test_dump_fchk_from_wfn_ch3(tmpdir): - check_load_dump_consistency(tmpdir, 'ch3_rohf_sto3g_g03.fchk') + check_load_dump_consistency(tmpdir, "ch3_rohf_sto3g_g03.fchk") def test_dump_fchk_from_wfn_cah110(tmpdir): - check_load_dump_consistency(tmpdir, 'cah110_hf_sto3g_g09.wfn') + check_load_dump_consistency(tmpdir, "cah110_hf_sto3g_g09.wfn") def test_dump_fchk_from_wfx_h2(tmpdir): - check_load_dump_consistency(tmpdir, 'h2_ub3lyp_ccpvtz.wfx') + check_load_dump_consistency(tmpdir, "h2_ub3lyp_ccpvtz.wfx") def test_dump_fchk_from_wfx_water(tmpdir): - check_load_dump_consistency(tmpdir, 'water_sto3g_hf.wfx') + check_load_dump_consistency(tmpdir, "water_sto3g_hf.wfx") def test_dump_fchk_from_wfx_lih_cation(tmpdir): - check_load_dump_consistency(tmpdir, 'lih_cation_uhf.wfx') - check_load_dump_consistency(tmpdir, 'lih_cation_rohf.wfx') + check_load_dump_consistency(tmpdir, "lih_cation_uhf.wfx") + check_load_dump_consistency(tmpdir, "lih_cation_rohf.wfx") def test_dump_fchk_from_wfx_lih_cisd_cation(tmpdir): # Fractional occupations are not supported in FCHK and we have no # alternative for solution for this yet. with pytest.raises(ValueError): - check_load_dump_consistency(tmpdir, 'lih_cation_cisd.wfx') + check_load_dump_consistency(tmpdir, "lih_cation_cisd.wfx") def test_dump_fchk_from_wfx_cah110(tmpdir): - check_load_dump_consistency(tmpdir, 'cah110_hf_sto3g_g09.wfx') + check_load_dump_consistency(tmpdir, "cah110_hf_sto3g_g09.wfx") def test_dump_fchk_from_molekel_h2(tmpdir): - check_load_dump_consistency(tmpdir, 'h2_sto3g.mkl', "ORCA") + check_load_dump_consistency(tmpdir, "h2_sto3g.mkl", "ORCA") def test_dump_fchk_from_molekel_ethanol(tmpdir): - check_load_dump_consistency(tmpdir, 'ethanol.mkl', "ORCA") + check_load_dump_consistency(tmpdir, "ethanol.mkl", "ORCA") def test_dump_fchk_from_molekel_li2(tmpdir): - check_load_dump_consistency(tmpdir, 'li2.mkl', "ORCA") + check_load_dump_consistency(tmpdir, "li2.mkl", "ORCA") def test_dump_fchk_rdms_cc_nitrogen(tmpdir): diff --git a/iodata/test/test_fcidump.py b/iodata/test/test_fcidump.py index 2623f03e..537bae6a 100644 --- a/iodata/test/test_fcidump.py +++ b/iodata/test/test_fcidump.py @@ -33,55 +33,55 @@ def test_load_fcidump_psi4_h2(): with as_file(files("iodata.test.data").joinpath("FCIDUMP.psi4.h2")) as fn: mol = load_one(str(fn)) - assert_allclose(mol.core_energy, 0.7151043364864863E+00) + assert_allclose(mol.core_energy, 0.7151043364864863e00) assert_equal(mol.nelec, 2) assert_equal(mol.spinpol, 0) - core_mo = mol.one_ints['core_mo'] + core_mo = mol.one_ints["core_mo"] assert_equal(core_mo.shape, (10, 10)) - assert_allclose(core_mo[0, 0], -0.1251399119550580E+01) - assert_allclose(core_mo[2, 1], 0.9292454365115077E-01) - assert_allclose(core_mo[1, 2], 0.9292454365115077E-01) - assert_allclose(core_mo[9, 9], 0.9035054979531029E+00) - two_mo = mol.two_ints['two_mo'] + assert_allclose(core_mo[0, 0], -0.1251399119550580e01) + assert_allclose(core_mo[2, 1], 0.9292454365115077e-01) + assert_allclose(core_mo[1, 2], 0.9292454365115077e-01) + assert_allclose(core_mo[9, 9], 0.9035054979531029e00) + two_mo = mol.two_ints["two_mo"] assert_allclose(two_mo.shape, (10, 10, 10, 10)) - assert_allclose(two_mo[0, 0, 0, 0], 0.6589928924251115E+00) + assert_allclose(two_mo[0, 0, 0, 0], 0.6589928924251115e00) # Check physicist's notation and symmetry - assert_allclose(two_mo[6, 1, 5, 0], 0.5335846565304321E-01) - assert_allclose(two_mo[5, 1, 6, 0], 0.5335846565304321E-01) - assert_allclose(two_mo[6, 0, 5, 1], 0.5335846565304321E-01) - assert_allclose(two_mo[5, 0, 6, 1], 0.5335846565304321E-01) - assert_allclose(two_mo[1, 6, 0, 5], 0.5335846565304321E-01) - assert_allclose(two_mo[1, 5, 0, 6], 0.5335846565304321E-01) - assert_allclose(two_mo[0, 6, 1, 5], 0.5335846565304321E-01) - assert_allclose(two_mo[0, 5, 1, 6], 0.5335846565304321E-01) - assert_allclose(two_mo[9, 9, 9, 9], 0.6273759381091796E+00) + assert_allclose(two_mo[6, 1, 5, 0], 0.5335846565304321e-01) + assert_allclose(two_mo[5, 1, 6, 0], 0.5335846565304321e-01) + assert_allclose(two_mo[6, 0, 5, 1], 0.5335846565304321e-01) + assert_allclose(two_mo[5, 0, 6, 1], 0.5335846565304321e-01) + assert_allclose(two_mo[1, 6, 0, 5], 0.5335846565304321e-01) + assert_allclose(two_mo[1, 5, 0, 6], 0.5335846565304321e-01) + assert_allclose(two_mo[0, 6, 1, 5], 0.5335846565304321e-01) + assert_allclose(two_mo[0, 5, 1, 6], 0.5335846565304321e-01) + assert_allclose(two_mo[9, 9, 9, 9], 0.6273759381091796e00) def test_load_fcidump_molpro_h2(): with as_file(files("iodata.test.data").joinpath("FCIDUMP.molpro.h2")) as fn: mol = load_one(str(fn)) - assert_allclose(mol.core_energy, 0.7151043364864863E+00) + assert_allclose(mol.core_energy, 0.7151043364864863e00) assert_equal(mol.nelec, 2) assert_equal(mol.spinpol, 0) - core_mo = mol.one_ints['core_mo'] + core_mo = mol.one_ints["core_mo"] assert_equal(core_mo.shape, (4, 4)) - assert_allclose(core_mo[0, 0], -0.1245406261597530E+01) - assert_allclose(core_mo[0, 1], -0.1666402467335385E+00) - assert_allclose(core_mo[1, 0], -0.1666402467335385E+00) - assert_allclose(core_mo[3, 3], 0.3216193420753873E+00) - two_mo = mol.two_ints['two_mo'] + assert_allclose(core_mo[0, 0], -0.1245406261597530e01) + assert_allclose(core_mo[0, 1], -0.1666402467335385e00) + assert_allclose(core_mo[1, 0], -0.1666402467335385e00) + assert_allclose(core_mo[3, 3], 0.3216193420753873e00) + two_mo = mol.two_ints["two_mo"] assert_allclose(two_mo.shape, (4, 4, 4, 4)) - assert_allclose(two_mo[0, 0, 0, 0], 0.6527679278914691E+00) + assert_allclose(two_mo[0, 0, 0, 0], 0.6527679278914691e00) # Check physicist's notation and symmetry - assert_allclose(two_mo[3, 0, 2, 1], 0.7756042287284058E-01) - assert_allclose(two_mo[2, 0, 3, 1], 0.7756042287284058E-01) - assert_allclose(two_mo[3, 1, 2, 0], 0.7756042287284058E-01) - assert_allclose(two_mo[2, 1, 3, 0], 0.7756042287284058E-01) - assert_allclose(two_mo[0, 3, 1, 2], 0.7756042287284058E-01) - assert_allclose(two_mo[0, 2, 1, 3], 0.7756042287284058E-01) - assert_allclose(two_mo[1, 3, 0, 2], 0.7756042287284058E-01) - assert_allclose(two_mo[1, 2, 0, 3], 0.7756042287284058E-01) - assert_allclose(two_mo[3, 3, 3, 3], 0.7484308847738417E+00) + assert_allclose(two_mo[3, 0, 2, 1], 0.7756042287284058e-01) + assert_allclose(two_mo[2, 0, 3, 1], 0.7756042287284058e-01) + assert_allclose(two_mo[3, 1, 2, 0], 0.7756042287284058e-01) + assert_allclose(two_mo[2, 1, 3, 0], 0.7756042287284058e-01) + assert_allclose(two_mo[0, 3, 1, 2], 0.7756042287284058e-01) + assert_allclose(two_mo[0, 2, 1, 3], 0.7756042287284058e-01) + assert_allclose(two_mo[1, 3, 0, 2], 0.7756042287284058e-01) + assert_allclose(two_mo[1, 2, 0, 3], 0.7756042287284058e-01) + assert_allclose(two_mo[3, 3, 3, 3], 0.7484308847738417e00) def test_dump_load_fcidimp_consistency_ao(tmpdir): @@ -91,17 +91,17 @@ def test_dump_load_fcidimp_consistency_ao(tmpdir): mol0.nelec = 10 mol0.spinpol = 0 with as_file(files("iodata.test.data").joinpath("psi4_h2_one.npy")) as fn: - mol0.one_ints = {'core_mo': np.load(str(fn))} + mol0.one_ints = {"core_mo": np.load(str(fn))} with as_file(files("iodata.test.data").joinpath("psi4_h2_two.npy")) as fn: - mol0.two_ints = {'two_mo': np.load(str(fn))} + mol0.two_ints = {"two_mo": np.load(str(fn))} # Dump to a file and load it again - fn_tmp = os.path.join(tmpdir, 'FCIDUMP') + fn_tmp = os.path.join(tmpdir, "FCIDUMP") dump_one(mol0, fn_tmp) mol1 = load_one(fn_tmp) # Compare results assert_equal(mol0.nelec, mol1.nelec) assert_equal(mol0.spinpol, mol1.spinpol) - assert_allclose(mol0.one_ints['core_mo'], mol1.one_ints['core_mo']) - assert_allclose(mol0.two_ints['two_mo'], mol1.two_ints['two_mo']) + assert_allclose(mol0.one_ints["core_mo"], mol1.one_ints["core_mo"]) + assert_allclose(mol0.two_ints["two_mo"], mol1.two_ints["two_mo"]) diff --git a/iodata/test/test_gamess.py b/iodata/test/test_gamess.py index e0fb633c..2e5e64d6 100644 --- a/iodata/test/test_gamess.py +++ b/iodata/test/test_gamess.py @@ -45,15 +45,15 @@ def test_load_one_gamess_punch(): assert_allclose(data.atcoords[-1, 0] / angstrom, 3.8608437748) assert_allclose(data.energy, -959.9675629527) assert_equal(data.atgradient.shape, (N, 3)) - assert data.atgradient[0, 1] - 1.5314677838E-05 < 1e-10 - assert abs(data.atgradient[3, -1] - 8.5221217336E-06) < 1e-10 - assert abs(data.atgradient[-1, 0] - 2.1211421041E-05) < 1e-10 + assert data.atgradient[0, 1] - 1.5314677838e-05 < 1e-10 + assert abs(data.atgradient[3, -1] - 8.5221217336e-06) < 1e-10 + assert abs(data.atgradient[-1, 0] - 2.1211421041e-05) < 1e-10 assert_equal(data.athessian.shape, (3 * N, 3 * N)) assert abs(data.athessian - data.athessian.transpose()).max() < 1e-10 - assert abs(data.athessian[0, 0] - 2.51645239E-02) < 1e-10 - assert abs(data.athessian[0, -1] - -1.27201108E-04) < 1e-10 - assert abs(data.athessian[-1, 0] - -1.27201108E-04) < 1e-10 - assert abs(data.athessian[-1, -1] - 7.34538698E-03) < 1e-10 + assert abs(data.athessian[0, 0] - 2.51645239e-02) < 1e-10 + assert abs(data.athessian[0, -1] - -1.27201108e-04) < 1e-10 + assert abs(data.athessian[-1, 0] - -1.27201108e-04) < 1e-10 + assert abs(data.athessian[-1, -1] - 7.34538698e-03) < 1e-10 assert_equal(data.atmasses.shape, (N,)) assert_allclose(data.atmasses[0], 34.96885) assert_allclose(data.atmasses[3], 1.00782) diff --git a/iodata/test/test_gaussianinput.py b/iodata/test/test_gaussianinput.py index 6ed5031e..866747bc 100644 --- a/iodata/test/test_gaussianinput.py +++ b/iodata/test/test_gaussianinput.py @@ -34,35 +34,35 @@ def test_load_water_com(): # test .com with Link 0 section with as_file(files("iodata.test.data").joinpath("water.com")) as fn: mol = load_one(str(fn)) - check_water(mol, 'water') + check_water(mol, "water") def test_load_water_gjf(): # test .com without Link 0 section with as_file(files("iodata.test.data").joinpath("water.gjf")) as fn: mol = load_one(str(fn)) - check_water(mol, 'water') + check_water(mol, "water") def test_load_multi_link(): # test .com with multiple #link 0 contents with as_file(files("iodata.test.data").joinpath("water_multi_link.com")) as fn: mol = load_one(str(fn)) - check_water(mol, 'water') + check_water(mol, "water") def test_load_multi_route(): # test .com with multiple route contents with as_file(files("iodata.test.data").joinpath("water_multi_route.com")) as fn: mol = load_one(str(fn)) - check_water(mol, 'water') + check_water(mol, "water") def test_load_multi_title(): # test .com with multiple title and concatenate with as_file(files("iodata.test.data").joinpath("water_multi_title.com")) as fn: mol = load_one(str(fn)) - check_water(mol, 'water water') + check_water(mol, "water water") def test_load_error(): @@ -77,9 +77,12 @@ def check_water(mol, title): assert mol.title == title assert_equal(mol.atnums, [1, 8, 1]) # check bond length - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.960, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.960, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.e-3) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.960, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.960, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.0e-3 + ) diff --git a/iodata/test/test_gaussianlog.py b/iodata/test/test_gaussianlog.py index b5551211..5cd8e378 100644 --- a/iodata/test/test_gaussianlog.py +++ b/iodata/test/test_gaussianlog.py @@ -36,12 +36,12 @@ def load_log_helper(fn_log): def test_load_operators_water_sto3g_hf_g03(): eps = 1e-5 - mol = load_log_helper('water_sto3g_hf_g03.log') + mol = load_log_helper("water_sto3g_hf_g03.log") - olp = mol.one_ints['olp'] - kin_ao = mol.one_ints['kin_ao'] - na_ao = mol.one_ints['na_ao'] - er_ao = mol.two_ints['er_ao'] + olp = mol.one_ints["olp"] + kin_ao = mol.one_ints["kin_ao"] + na_ao = mol.one_ints["na_ao"] + er_ao = mol.two_ints["er_ao"] assert_equal(olp.shape, (7, 7)) assert_equal(kin_ao.shape, (7, 7)) @@ -71,12 +71,12 @@ def test_load_operators_water_sto3g_hf_g03(): def test_load_operators_water_ccpvdz_pure_hf_g03(): eps = 1e-5 - mol = load_log_helper('water_ccpvdz_pure_hf_g03.log') + mol = load_log_helper("water_ccpvdz_pure_hf_g03.log") - olp = mol.one_ints['olp'] - kin_ao = mol.one_ints['kin_ao'] - na_ao = mol.one_ints['na_ao'] - er_ao = mol.two_ints['er_ao'] + olp = mol.one_ints["olp"] + kin_ao = mol.one_ints["kin_ao"] + na_ao = mol.one_ints["na_ao"] + er_ao = mol.two_ints["er_ao"] assert_equal(olp.shape, (24, 24)) assert_equal(kin_ao.shape, (24, 24)) diff --git a/iodata/test/test_gromacs.py b/iodata/test/test_gromacs.py index 9d0382c5..4274af09 100644 --- a/iodata/test/test_gromacs.py +++ b/iodata/test/test_gromacs.py @@ -39,15 +39,15 @@ def test_load_water(): def check_water(mol): """Test some things on a water file.""" - assert mol.title == 'MD of 2 waters' + assert mol.title == "MD of 2 waters" assert mol.atcoords.shape == (6, 3) assert_allclose(mol.atcoords[-1] / nanometer, [1.326, 0.120, 0.568]) - assert mol.atffparams['attypes'][2] == 'HW3' - assert mol.atffparams['resnames'][-1] == 'WATER' - assert_equal(mol.atffparams['resnums'][2:4], [1, 2]) - assert_allclose(mol.cellvecs[0][0], 1.82060 * nanometer, atol=1.e-5) - assert mol.extra['velocities'].shape == (6, 3) - vel = mol.extra['velocities'][-1] + assert mol.atffparams["attypes"][2] == "HW3" + assert mol.atffparams["resnames"][-1] == "WATER" + assert_equal(mol.atffparams["resnums"][2:4], [1, 2]) + assert_allclose(mol.cellvecs[0][0], 1.82060 * nanometer, atol=1.0e-5) + assert mol.extra["velocities"].shape == (6, 3) + vel = mol.extra["velocities"][-1] assert_allclose(vel * (picosecond / nanometer), [1.9427, -0.8216, -0.0244]) @@ -55,7 +55,7 @@ def test_load_many(): with as_file(files("iodata.test.data").joinpath("water2.gro")) as fn_gro: mols = list(load_many(str(fn_gro))) assert len(mols) == 2 - assert mols[0].extra['time'] == 0.0 * picosecond - assert mols[1].extra['time'] == 1.0 * picosecond + assert mols[0].extra["time"] == 0.0 * picosecond + assert mols[1].extra["time"] == 1.0 * picosecond for mol in mols: check_water(mol) diff --git a/iodata/test/test_inputs.py b/iodata/test/test_inputs.py index 8c8c03db..137850e2 100644 --- a/iodata/test/test_inputs.py +++ b/iodata/test/test_inputs.py @@ -42,9 +42,9 @@ def check_load_input_and_compare(fname: str, fname_expected: str): Path to expected input file to load. """ - with open(fname, 'r') as ifn: + with open(fname, "r") as ifn: content = "".join(ifn.readlines()) - with open(fname_expected, 'r') as efn: + with open(fname_expected, "r") as efn: expected = "".join(efn.readlines()) assert content == expected @@ -54,10 +54,10 @@ def test_input_gaussian_from_xyz(tmpdir): with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn: mol = load_one(fn) mol.nelec = 10 - mol.lot = 'ub3lyp' - mol.obasis_name = '6-31g*' + mol.lot = "ub3lyp" + mol.obasis_name = "6-31g*" # write input in a temporary folder using the user-template - fname = os.path.join(tmpdir, 'input_from_xyz.com') + fname = os.path.join(tmpdir, "input_from_xyz.com") template = """\ %chk=gaussian.chk %mem=3500MB @@ -79,7 +79,7 @@ def test_input_gaussian_from_xyz(tmpdir): """ - write_input(mol, fname, fmt='gaussian', template=template, extra_cmd="nosymmetry") + write_input(mol, fname, fmt="gaussian", template=template, extra_cmd="nosymmetry") # compare saved input to expected input source = files("iodata.test.data").joinpath("input_gaussian_h2o_opt_ub3lyp.txt") with as_file(source) as fname_expected: @@ -88,12 +88,17 @@ def test_input_gaussian_from_xyz(tmpdir): def test_input_gaussian_from_iodata(tmpdir): # make an instance of IOData for HCl anion - data = {"atcoords": np.array([[0.0, 0.0, 0.0], [angstrom, 0.0, 0.0]]), - "atnums": np.array([1, 17]), "nelec": 19, "run_type": 'opt', "spinpol": 1} + data = { + "atcoords": np.array([[0.0, 0.0, 0.0], [angstrom, 0.0, 0.0]]), + "atnums": np.array([1, 17]), + "nelec": 19, + "run_type": "opt", + "spinpol": 1, + } mol = IOData(**data) # write input in a temporary file - fname = os.path.join(tmpdir, 'input_from_iodata.com') - write_input(mol, fname, fmt='gaussian') + fname = os.path.join(tmpdir, "input_from_iodata.com") + write_input(mol, fname, fmt="gaussian") # compare saved input to expected input source = files("iodata.test.data").joinpath("input_gaussian_hcl_anion_opt_hf.txt") with as_file(source) as fname_expected: @@ -105,8 +110,8 @@ def test_input_gaussian_from_fchk(tmpdir): with as_file(files("iodata.test.data").joinpath("water_hfs_321g.fchk")) as fn: mol = load_one(fn) # write input in a temporary file - fname = os.path.join(tmpdir, 'input_from_fchk.in') - write_input(mol, fname, fmt='gaussian') + fname = os.path.join(tmpdir, "input_from_fchk.in") + write_input(mol, fname, fmt="gaussian") # compare saved input to expected input source = files("iodata.test.data").joinpath("input_gaussian_hcl_sp_rhf.txt") with as_file(source) as fname_expected: @@ -118,10 +123,10 @@ def test_input_orca_from_xyz(tmpdir): with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn: mol = load_one(fn) mol.nelec = 10 - mol.lot = 'B3LYP' - mol.obasis_name = 'def2-SVP' + mol.lot = "B3LYP" + mol.obasis_name = "def2-SVP" # write input in a temporary folder using the user-template - fname = os.path.join(tmpdir, 'input_from_xyz.com') + fname = os.path.join(tmpdir, "input_from_xyz.com") template = """\ ! {lot} {obasis_name} {grid_stuff} KeepDens # {title} @@ -138,7 +143,7 @@ def test_input_orca_from_xyz(tmpdir): end """ grid_stuff = "Grid4 TightSCF NOFINALGRID" - write_input(mol, fname, fmt='orca', template=template, grid_stuff=grid_stuff) + write_input(mol, fname, fmt="orca", template=template, grid_stuff=grid_stuff) # compare saved input to expected input source = files("iodata.test.data").joinpath("input_orca_h2o_sp_b3lyp.txt") with as_file(source) as fname_expected: @@ -147,12 +152,17 @@ def test_input_orca_from_xyz(tmpdir): def test_input_orca_from_iodata(tmpdir): # make an instance of IOData for HCl anion - data = {"atcoords": np.array([[0.0, 0.0, 0.0], [angstrom, 0.0, 0.0]]), - "atnums": np.array([1, 17]), "nelec": 19, "run_type": 'opt', "spinpol": 1} + data = { + "atcoords": np.array([[0.0, 0.0, 0.0], [angstrom, 0.0, 0.0]]), + "atnums": np.array([1, 17]), + "nelec": 19, + "run_type": "opt", + "spinpol": 1, + } mol = IOData(**data) # write input in a temporary file - fname = os.path.join(tmpdir, 'input_from_iodata.com') - write_input(mol, fname, fmt='orca') + fname = os.path.join(tmpdir, "input_from_iodata.com") + write_input(mol, fname, fmt="orca") # compare saved input to expected input source = files("iodata.test.data").joinpath("input_orca_hcl_anion_opt_hf.txt") with as_file(source) as fname_expected: @@ -164,8 +174,8 @@ def test_input_orca_from_molden(tmpdir): with as_file(files("iodata.test.data").joinpath("nh3_orca.molden")) as fn: mol = load_one(fn) # write input in a temporary file - fname = os.path.join(tmpdir, 'input_from_molden.in') - write_input(mol, fname, fmt='orca') + fname = os.path.join(tmpdir, "input_from_molden.in") + write_input(mol, fname, fmt="orca") # compare saved input to expected input source = files("iodata.test.data").joinpath("input_orca_nh3_sp_hf.txt") with as_file(source) as fname_expected: diff --git a/iodata/test/test_iodata.py b/iodata/test/test_iodata.py index 46ee887b..18e297a4 100644 --- a/iodata/test/test_iodata.py +++ b/iodata/test/test_iodata.py @@ -36,8 +36,11 @@ def test_typecheck(): m = IOData(atcoords=np.array([[1, 2, 3], [2, 3, 1]])) assert np.issubdtype(m.atcoords.dtype, np.floating) assert m.atnums is None - m = IOData(atnums=np.array([2.0, 3.0]), atcorenums=np.array([1, 1]), - atcoords=np.array([[1, 2, 3], [2, 3, 1]])) + m = IOData( + atnums=np.array([2.0, 3.0]), + atcorenums=np.array([1, 1]), + atcoords=np.array([[1, 2, 3], [2, 3, 1]]), + ) assert np.issubdtype(m.atnums.dtype, np.integer) assert np.issubdtype(m.atcorenums.dtype, np.floating) assert m.atnums is not None @@ -50,41 +53,39 @@ def test_typecheck_raises(): pytest.raises(TypeError, IOData, atcoords=np.array([[1, 2], [2, 3]])) pytest.raises(TypeError, IOData, atnums=np.array([[1, 2], [2, 3]])) # check inconsistency between various attributes - atnums, atcorenums, atcoords = np.array( - [2, 3]), np.array([1]), np.array([[1, 2, 3]]) - pytest.raises(TypeError, IOData, atnums=atnums, - atcorenums=atcorenums) + atnums, atcorenums, atcoords = np.array([2, 3]), np.array([1]), np.array([[1, 2, 3]]) + pytest.raises(TypeError, IOData, atnums=atnums, atcorenums=atcorenums) pytest.raises(TypeError, IOData, atnums=atnums, atcoords=atcoords) def test_unknown_format(): - pytest.raises(ValueError, load_one, 'foo.unknown_file_extension') + pytest.raises(ValueError, load_one, "foo.unknown_file_extension") def test_dm_water_sto3g_hf(): with as_file(files("iodata.test.data").joinpath("water_sto3g_hf_g03.fchk")) as fn_fchk: mol = load_one(str(fn_fchk)) - dm = mol.one_rdms['scf'] - assert_allclose(dm[0, 0], 2.10503807, atol=1.e-7) - assert_allclose(dm[0, 1], -0.439115917, atol=1.e-7) - assert_allclose(dm[1, 1], 1.93312061, atol=1.e-7) + dm = mol.one_rdms["scf"] + assert_allclose(dm[0, 0], 2.10503807, atol=1.0e-7) + assert_allclose(dm[0, 1], -0.439115917, atol=1.0e-7) + assert_allclose(dm[1, 1], 1.93312061, atol=1.0e-7) def test_dm_lih_sto3g_hf(): with as_file(files("iodata.test.data").joinpath("li_h_3-21G_hf_g09.fchk")) as fn_fchk: mol = load_one(str(fn_fchk)) - dm = mol.one_rdms['scf'] - assert_allclose(dm[0, 0], 1.96589709, atol=1.e-7) - assert_allclose(dm[0, 1], 0.122114249, atol=1.e-7) - assert_allclose(dm[1, 1], 0.0133112081, atol=1.e-7) - assert_allclose(dm[10, 10], 4.23924688E-01, atol=1.e-7) + dm = mol.one_rdms["scf"] + assert_allclose(dm[0, 0], 1.96589709, atol=1.0e-7) + assert_allclose(dm[0, 1], 0.122114249, atol=1.0e-7) + assert_allclose(dm[1, 1], 0.0133112081, atol=1.0e-7) + assert_allclose(dm[10, 10], 4.23924688e-01, atol=1.0e-7) - dm_spin = mol.one_rdms['scf_spin'] - assert_allclose(dm_spin[0, 0], 1.40210760E-03, atol=1.e-9) - assert_allclose(dm_spin[0, 1], -2.65370873E-03, atol=1.e-9) - assert_allclose(dm_spin[1, 1], 5.38701212E-03, atol=1.e-9) - assert_allclose(dm_spin[10, 10], 4.23889148E-01, atol=1.e-7) + dm_spin = mol.one_rdms["scf_spin"] + assert_allclose(dm_spin[0, 0], 1.40210760e-03, atol=1.0e-9) + assert_allclose(dm_spin[0, 1], -2.65370873e-03, atol=1.0e-9) + assert_allclose(dm_spin[1, 1], 5.38701212e-03, atol=1.0e-9) + assert_allclose(dm_spin[10, 10], 4.23889148e-01, atol=1.0e-7) def test_dm_ch3_rohf_g03(): @@ -92,7 +93,7 @@ def test_dm_ch3_rohf_g03(): mol = load_one(str(fn_fchk)) olp = compute_overlap(mol.obasis, mol.atcoords) dm = compute_1rdm(mol) - assert_allclose(np.einsum('ab,ba', olp, dm), 9, atol=1.e-6) + assert_allclose(np.einsum("ab,ba", olp, dm), 9, atol=1.0e-6) def test_charge_nelec1(): diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 65bda672..47c6713d 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -47,7 +47,7 @@ ] ), "H2O": np.array([[0.0, 0.0, -0.1295], [0.0, -1.4942, 1.0274], [0.0, 1.4942, 1.0274]]), - "H2O_MP2": np.array([[0.0, 0.0, -0.1294], [0.0, -1.4941, 1.0274], [0.0, 1.4941, 1.0274]]) + "H2O_MP2": np.array([[0.0, 0.0, -0.1294], [0.0, -1.4941, 1.0274], [0.0, 1.4941, 1.0274]]), } # These molecule examples were manually generated for testing # MOL_FILES: (filename, atnums, charge, spinpol, geometry) @@ -178,7 +178,7 @@ def test_inout_qcschema_molecule(tmpdir, filename, nwarn): assert len(record) == nwarn mol1 = json.loads(qcschema_molecule.read_bytes()) - fn_tmp = os.path.join(tmpdir, 'test_qcschema_mol.json') + fn_tmp = os.path.join(tmpdir, "test_qcschema_mol.json") dump_one(mol, fn_tmp) with open(fn_tmp, "r") as mol2_in: @@ -208,7 +208,7 @@ def test_inout_molssi_qcschema_molecule(tmpdir, filename): mol1_preproc = json.loads(qcschema_molecule.read_bytes()) assert len(record) == 1 - fn_tmp = os.path.join(tmpdir, 'test_qcschema_mol.json') + fn_tmp = os.path.join(tmpdir, "test_qcschema_mol.json") dump_one(mol, fn_tmp) with open(fn_tmp, "r") as mol2_in: @@ -242,7 +242,7 @@ def test_ghost(tmpdir): with as_file(source) as qcschema_molecule: mol = load_one(str(qcschema_molecule)) np.testing.assert_allclose(mol.atcorenums, [8, 1, 1, 0, 0, 0, 0, 0, 0]) - fn_tmp = os.path.join(tmpdir, 'test_ghost.json') + fn_tmp = os.path.join(tmpdir, "test_ghost.json") dump_one(mol, fn_tmp) with open(fn_tmp, "r") as mol2_in: mol2 = json.load(mol2_in) @@ -255,7 +255,7 @@ def test_ghost(tmpdir): ("LiCl_string_STO4G_input.json", False, "B3LYP", "Def2TZVP", None, GEOMS["LiCl"]), ("LiCl_explicit_STO4G_input.json", True, "HF", None, None, GEOMS["LiCl"]), ("LiCl_STO4G_Gaussian_input.json", False, "HF", "STO-4G", "freq", GEOMS["LiCl"]), - ("water_mp2_input.json", False, "MP2", "cc-pVDZ", None, GEOMS["H2O_MP2"]) + ("water_mp2_input.json", False, "MP2", "cc-pVDZ", None, GEOMS["H2O_MP2"]), ] @@ -317,7 +317,7 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): assert len(record) == nwarn mol1 = json.loads(qcschema_input.read_bytes()) - fn_tmp = os.path.join(tmpdir, 'test_input_mol.json') + fn_tmp = os.path.join(tmpdir, "test_input_mol.json") dump_one(mol, fn_tmp) with open(fn_tmp, "r") as mol2_in: @@ -389,7 +389,7 @@ def test_inout_qcschema_output(tmpdir, filename): mol = load_one(str(qcschema_input)) mol1 = json.loads(qcschema_input.read_bytes()) - fn_tmp = os.path.join(tmpdir, 'test_input_mol.json') + fn_tmp = os.path.join(tmpdir, "test_input_mol.json") dump_one(mol, fn_tmp) with open(fn_tmp, "r") as mol2_in: diff --git a/iodata/test/test_locpot.py b/iodata/test/test_locpot.py index 55e4e8f6..06f197cc 100644 --- a/iodata/test/test_locpot.py +++ b/iodata/test/test_locpot.py @@ -33,14 +33,14 @@ def test_load_locpot_oxygen(): with as_file(files("iodata.test.data").joinpath("LOCPOT.oxygen")) as fn: mol = load_one(str(fn)) - assert mol.title == 'O atom in a box' + assert mol.title == "O atom in a box" assert_equal(mol.atnums[0], 8) - assert_allclose(volume(mol.cellvecs), (10 * angstrom) ** 3, atol=1.e-10) + assert_allclose(volume(mol.cellvecs), (10 * angstrom) ** 3, atol=1.0e-10) assert_equal(len(mol.cube.shape), 3) assert_equal(mol.cube.shape, [1, 4, 2]) assert abs(mol.cube.origin).max() < 1e-10 d = mol.cube.data - assert_allclose(d[0, 0, 0] / electronvolt, 0.35046350435E+01, 1.e-10) - assert_allclose(d[0, 1, 0] / electronvolt, 0.213732132354E+01, 1.e-10) - assert_allclose(d[0, 2, 0] / electronvolt, -.65465465497E+01, 1.e-10) - assert_allclose(d[0, 2, 1] / electronvolt, -.546876467887E+01, 1.e-10) + assert_allclose(d[0, 0, 0] / electronvolt, 0.35046350435e01, 1.0e-10) + assert_allclose(d[0, 1, 0] / electronvolt, 0.213732132354e01, 1.0e-10) + assert_allclose(d[0, 2, 0] / electronvolt, -0.65465465497e01, 1.0e-10) + assert_allclose(d[0, 2, 1] / electronvolt, -0.546876467887e01, 1.0e-10) diff --git a/iodata/test/test_mol2.py b/iodata/test/test_mol2.py index 57d33957..12116e15 100644 --- a/iodata/test/test_mol2.py +++ b/iodata/test/test_mol2.py @@ -63,19 +63,20 @@ def test_bondtypes_benzene(): def check_example(mol): """Test some things on example file.""" - assert mol.title == 'ZINC00001084' + assert mol.title == "ZINC00001084" assert_equal(mol.natom, 24) - assert_equal(mol.atnums, [6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) - assert mol.atffparams['attypes'][0] == 'C.3' + assert_equal( + mol.atnums, [6, 7, 6, 7, 6, 6, 6, 8, 7, 6, 8, 7, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ) + assert mol.atffparams["attypes"][0] == "C.3" # check coordinates atcoords_ang = mol.atcoords / angstrom assert_allclose(atcoords_ang[0], [-0.0178, 1.4608, 0.0101]) assert_allclose(atcoords_ang[1], [0.0021, -0.0041, 0.0020]) assert_allclose(atcoords_ang[22], [0.5971, -2.2951, 5.2627]) assert_allclose(atcoords_ang[23], [0.5705, -0.5340, 5.0055]) - assert_allclose(mol.atcharges['mol2charges'][0], 0.0684) - assert_allclose(mol.atcharges['mol2charges'][23], 0.0949) + assert_allclose(mol.atcharges["mol2charges"][0], 0.0684) + assert_allclose(mol.atcharges["mol2charges"][23], 0.0949) bonds = mol.bonds assert len(bonds) == 25 assert_equal(bonds[0], [0, 1, bond2num["1"]]) @@ -88,13 +89,13 @@ def check_load_dump_consistency(tmpdir, fn): """Check if dumping and loading an MOL2 file results in the same data.""" mol0 = load_one(str(fn)) # write mol2 file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test.mol2') - dump_one(mol0, fn_tmp, fmt='mol2') + fn_tmp = os.path.join(tmpdir, "test.mol2") + dump_one(mol0, fn_tmp, fmt="mol2") mol1 = load_one(fn_tmp) # check two mol2 files assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) assert_equal(mol0.bonds, mol1.bonds) @@ -110,7 +111,7 @@ def test_load_many(): mols = list(load_many(str(fn_mol2))) assert len(mols) == 2 check_example(mols[0]) - assert mols[1].title == 'ZINC00001085' + assert mols[1].title == "ZINC00001085" assert mols[1].natom == 24 assert_allclose(mols[0].atcoords[0] / angstrom, [-0.0178, 1.4608, 0.0101]) assert_allclose(mols[1].atcoords[0] / angstrom, [-0.0100, 1.5608, 0.0201]) @@ -120,14 +121,14 @@ def test_load_dump_many_consistency(tmpdir): with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_mol2: mols0 = list(load_many(str(fn_mol2))) # write mol2 file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test') - dump_many(mols0, fn_tmp, fmt='mol2') - mols1 = list(load_many(fn_tmp, fmt='mol2')) + fn_tmp = os.path.join(tmpdir, "test") + dump_many(mols0, fn_tmp, fmt="mol2") + mols1 = list(load_many(fn_tmp, fmt="mol2")) assert len(mols0) == len(mols1) for mol0, mol1 in zip(mols0, mols1): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) assert_equal(mol0.bonds, mol1.bonds) @@ -135,7 +136,7 @@ def test_load_dump_wrong_bond_num(tmpdir): with as_file(files("iodata.test.data").joinpath("silioh3.mol2")) as fn_mol: mol = load_one(str(fn_mol)) mol.bonds[0][2] = -1 - fn_tmp = os.path.join(tmpdir, 'test.mol2') + fn_tmp = os.path.join(tmpdir, "test.mol2") dump_one(mol, fn_tmp) mol2 = load_one(fn_tmp) assert mol2.bonds[0][2] == bond2num["un"] diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index ccb56dda..0df15abb 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -48,15 +48,15 @@ def test_load_molden_li2_orca(): assert "ORCA" in record[0].message.args[0] # Checkt title - assert mol.title == 'Molden file created by orca_2mkl for BaseName=li2' + assert mol.title == "Molden file created by orca_2mkl for BaseName=li2" # Check geometry assert_equal(mol.atnums, [3, 3]) assert_allclose(mol.mo.occsa[:4], [1, 1, 1, 0]) assert_allclose(mol.mo.occsb[:4], [1, 1, 0, 0]) - assert_equal(mol.mo.irreps, ['1a'] * mol.mo.norb) - assert_equal(mol.mo.irrepsa, ['1a'] * mol.mo.norba) - assert_equal(mol.mo.irrepsb, ['1a'] * mol.mo.norbb) + assert_equal(mol.mo.irreps, ["1a"] * mol.mo.norb) + assert_equal(mol.mo.irrepsa, ["1a"] * mol.mo.norba) + assert_equal(mol.mo.irrepsb, ["1a"] * mol.mo.norbb) assert_allclose(mol.atcoords[1], [5.2912331750, 0.0, 0.0]) # Check normalization @@ -67,7 +67,7 @@ def test_load_molden_li2_orca(): # Check Mulliken charges charges = compute_mulliken_charges(mol) expected_charges = np.array([0.5, 0.5]) - assert_allclose(charges, expected_charges, atol=1.e-5) + assert_allclose(charges, expected_charges, atol=1.0e-5) def test_load_molden_li2_orca_huge_threshold(): @@ -86,12 +86,12 @@ def test_load_molden_h2o_orca(): assert "ORCA" in record[0].message.args[0] # Checkt title - assert mol.title == 'Molden file created by orca_2mkl for BaseName=h2o' + assert mol.title == "Molden file created by orca_2mkl for BaseName=h2o" # Check geometry assert_equal(mol.atnums, [8, 1, 1]) assert_allclose(mol.mo.occs[:6], [2, 2, 2, 2, 2, 0]) - assert_equal(mol.mo.irreps, ['1a'] * mol.mo.norb) + assert_equal(mol.mo.irreps, ["1a"] * mol.mo.norb) assert_allclose(mol.atcoords[2], [0.0, -0.1808833432, 1.9123825806]) # Check normalization @@ -101,7 +101,7 @@ def test_load_molden_h2o_orca(): # Check Mulliken charges charges = compute_mulliken_charges(mol) expected_charges = np.array([-0.816308, 0.408154, 0.408154]) - assert_allclose(charges, expected_charges, atol=1.e-5) + assert_allclose(charges, expected_charges, atol=1.0e-5) def test_load_molden_nh3_molden_pure(): @@ -122,18 +122,18 @@ def test_load_molden_nh3_molden_pure(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.0381, -0.2742, 0.0121, 0.2242]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_low_nh3_molden_cart(): with as_file(files("iodata.test.data").joinpath("nh3_molden_cart.molden")) as fn_molden: lit = LineIterator(str(fn_molden)) data = _load_low(lit) - obasis = data['obasis'] + obasis = data["obasis"] assert obasis.nbasis == 52 assert len(obasis.shells) == 24 for shell in obasis.shells: - assert shell.kinds == ['c'] + assert shell.kinds == ["c"] assert shell.ncon == 1 for ishell in [0, 1, 2, 3, 9, 10, 11, 14, 15, 16, 19, 20, 21]: shell = obasis.shells[ishell] @@ -155,40 +155,91 @@ def test_load_molden_low_nh3_molden_cart(): shell0 = obasis.shells[0] assert shell0.nprim == 8 - assert shell0.exponents.shape == (8, ) - assert_allclose(shell0.exponents[4], 0.2856000000E+02) + assert shell0.exponents.shape == (8,) + assert_allclose(shell0.exponents[4], 0.2856000000e02) assert shell0.coeffs.shape == (8, 1) - assert_allclose(shell0.coeffs[4, 0], 0.2785706633E+00) + assert_allclose(shell0.coeffs[4, 0], 0.2785706633e00) shell7 = obasis.shells[7] assert shell7.nprim == 1 - assert shell7.exponents.shape == (1, ) - assert_allclose(shell7.exponents, [0.8170000000E+00]) + assert shell7.exponents.shape == (1,) + assert_allclose(shell7.exponents, [0.8170000000e00]) assert_allclose(shell7.coeffs, [[1.0]]) assert shell7.coeffs.shape == (1, 1) shell19 = obasis.shells[19] assert shell19.nprim == 3 - assert shell19.exponents.shape == (3, ) - assert_allclose(shell19.exponents, [ - 0.1301000000E+02, 0.1962000000E+01, 0.4446000000E+00]) - assert_allclose(shell19.coeffs, [ - [0.3349872639E-01], [0.2348008012E+00], [0.8136829579E+00]]) + assert shell19.exponents.shape == (3,) + assert_allclose(shell19.exponents, [0.1301000000e02, 0.1962000000e01, 0.4446000000e00]) + assert_allclose(shell19.coeffs, [[0.3349872639e-01], [0.2348008012e00], [0.8136829579e00]]) assert shell19.coeffs.shape == (3, 1) - assert data['mo'].coeffs.shape == (52, 52) - assert_allclose(data['mo'].coeffs[:2, 0], [1.002730, 0.005420]) - assert_allclose(data['mo'].coeffs[-2:, 1], [0.003310, -0.011620]) - assert_allclose(data['mo'].coeffs[-4:-2, -1], [-0.116400, 0.098220]) + assert data["mo"].coeffs.shape == (52, 52) + assert_allclose(data["mo"].coeffs[:2, 0], [1.002730, 0.005420]) + assert_allclose(data["mo"].coeffs[-2:, 1], [0.003310, -0.011620]) + assert_allclose(data["mo"].coeffs[-4:-2, -1], [-0.116400, 0.098220]) permutation, signs = convert_conventions(obasis, OVERLAP_CONVENTIONS) - assert_equal(permutation, [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 16, 17, 14, 18, 15, 19, - 22, 23, 20, 24, 21, 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]) + assert_equal( + permutation, + [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 16, + 17, + 14, + 18, + 15, + 19, + 22, + 23, + 20, + 24, + 21, + 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, + ], + ) assert_equal(signs, [1] * 52) # Check normalization - olp = compute_overlap(obasis, data['atcoords']) - check_orthonormal(data['mo'].coeffs, olp, atol=1e-4) # low precision in file + olp = compute_overlap(obasis, data["atcoords"]) + check_orthonormal(data["mo"].coeffs, olp, atol=1e-4) # low precision in file def test_load_molden_nh3_molden_cart(): @@ -205,23 +256,24 @@ def test_load_molden_nh3_molden_cart(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.3138, -0.4300, -0.0667, 0.1829]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_cfour(): # The file tested here is created with CFOUR 2.1. file_list = [ - 'h_sonly_sph_cfour.molden', - 'h_ponly_sph_cfour.molden', - 'h_donly_sph_cfour.molden', - 'h_fonly_sph_cfour.molden', - 'h_gonly_sph_cfour.molden', - 'h_sonly_cart_cfour.molden', - 'h_ponly_cart_cfour.molden', - 'h_donly_cart_cfour.molden', - 'h_fonly_cart_cfour.molden', - 'h_gonly_cart_cfour.molden', - 'h2o_ccpvdz_cfour.molden'] + "h_sonly_sph_cfour.molden", + "h_ponly_sph_cfour.molden", + "h_donly_sph_cfour.molden", + "h_fonly_sph_cfour.molden", + "h_gonly_sph_cfour.molden", + "h_sonly_cart_cfour.molden", + "h_ponly_cart_cfour.molden", + "h_donly_cart_cfour.molden", + "h_fonly_cart_cfour.molden", + "h_gonly_cart_cfour.molden", + "h2o_ccpvdz_cfour.molden", + ] for i in file_list: with as_file(files("iodata.test.data").joinpath(i)) as fn_molden: @@ -250,7 +302,7 @@ def test_load_molden_nh3_orca(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.0381, -0.2742, 0.0121, 0.2242]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_nh3_psi4(): @@ -270,7 +322,7 @@ def test_load_molden_nh3_psi4(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.0381, -0.2742, 0.0121, 0.2242]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_nh3_psi4_1(): @@ -290,7 +342,7 @@ def test_load_molden_nh3_psi4_1(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.0381, -0.2742, 0.0121, 0.2242]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) @pytest.mark.parametrize("case", ["zn", "mn", "cuh"]) @@ -367,7 +419,7 @@ def test_load_molden_h2o_6_31g_d_cart_psi4(): # Comparison with numbers from PSI4 output. charges = compute_mulliken_charges(mol) molden_charges = np.array([-0.86514, 0.43227, 0.43288]) - assert_allclose(charges, molden_charges, atol=1.e-5) + assert_allclose(charges, molden_charges, atol=1.0e-5) def test_load_molden_nh3_aug_cc_pvqz_cart_psi4(): @@ -388,7 +440,7 @@ def test_load_molden_nh3_aug_cc_pvqz_cart_psi4(): # Comparison with numbers from PSI4 output. charges = compute_mulliken_charges(mol) molden_charges = np.array([-0.74507, 0.35743, 0.24197, 0.14567]) - assert_allclose(charges, molden_charges, atol=1.e-5) + assert_allclose(charges, molden_charges, atol=1.0e-5) def test_load_molden_nh3_molpro2012(): @@ -404,7 +456,7 @@ def test_load_molden_nh3_molpro2012(): # Comparison with numbers from the Molden program output. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.0381, -0.2742, 0.0121, 0.2242]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_neon_turbomole(): @@ -443,7 +495,7 @@ def test_load_molden_nh3_turbomole(): # Cartesian functions. charges = compute_mulliken_charges(mol) molden_charges = np.array([0.03801, -0.27428, 0.01206, 0.22421]) - assert_allclose(charges, molden_charges, atol=1.e-3) + assert_allclose(charges, molden_charges, atol=1.0e-3) def test_load_molden_f(): @@ -460,22 +512,25 @@ def test_load_molden_f(): assert_allclose(mol.mo.occsa[:6], [1, 1, 1, 1, 1, 0]) assert_allclose(mol.mo.occsb[:6], [1, 1, 1, 1, 0, 0]) - assert_equal(mol.mo.irrepsa[:6], ['Ag', 'Ag', 'B3u', 'B2u', 'B1u', 'B3u']) - assert_equal(mol.mo.irrepsb[:6], ['Ag', 'Ag', 'B3u', 'B2u', 'B1u', 'B3u']) - - -@pytest.mark.parametrize("fn,match", [ - ("h2o.molden.input", "ORCA"), - ("li2.molden.input", "ORCA"), - ("F.molden", "PSI4"), - ("nh3_molden_pure.molden", None), - ("nh3_molden_cart.molden", None), - ("he2_ghost_psi4_1.0.molden", None), - ("psi4_cuh_cc_pvqz_pure.molden", "unnormalized"), - ("hf_sto3g.fchk", None), - ("h_sto3g.fchk", None), - ("ch3_rohf_sto3g_g03.fchk", None), -]) + assert_equal(mol.mo.irrepsa[:6], ["Ag", "Ag", "B3u", "B2u", "B1u", "B3u"]) + assert_equal(mol.mo.irrepsb[:6], ["Ag", "Ag", "B3u", "B2u", "B1u", "B3u"]) + + +@pytest.mark.parametrize( + "fn,match", + [ + ("h2o.molden.input", "ORCA"), + ("li2.molden.input", "ORCA"), + ("F.molden", "PSI4"), + ("nh3_molden_pure.molden", None), + ("nh3_molden_cart.molden", None), + ("he2_ghost_psi4_1.0.molden", None), + ("psi4_cuh_cc_pvqz_pure.molden", "unnormalized"), + ("hf_sto3g.fchk", None), + ("h_sto3g.fchk", None), + ("ch3_rohf_sto3g_g03.fchk", None), + ], +) def test_load_dump_consistency(tmpdir, fn, match): with as_file(files("iodata.test.data").joinpath(fn)) as file_name: if match is None: @@ -483,16 +538,16 @@ def test_load_dump_consistency(tmpdir, fn, match): else: with pytest.warns(FileFormatWarning, match=match): mol1 = load_one(str(file_name)) - fn_tmp = os.path.join(tmpdir, 'foo.bar') - dump_one(mol1, fn_tmp, fmt='molden') - mol2 = load_one(fn_tmp, fmt='molden') + fn_tmp = os.path.join(tmpdir, "foo.bar") + dump_one(mol1, fn_tmp, fmt="molden") + mol2 = load_one(fn_tmp, fmt="molden") # Remove and or fix some things in mol1 to make it compatible with what # can be read from a Molden file: # - Change basis of mol1 to segmented. mol1.obasis = mol1.obasis.get_segmented() # - Set default irreps in mol1, if not present. if mol1.mo.irreps is None: - mol1.mo = attr.evolve(mol1.mo, irreps=['1a'] * mol1.mo.norb) + mol1.mo = attr.evolve(mol1.mo, irreps=["1a"] * mol1.mo.norb) # - Remove the one_rdms from mol1. mol1.one_rdms = {} compare_mols(mol1, mol2) diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index f910cbc5..1b90e024 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -24,8 +24,7 @@ from numpy.testing import assert_equal, assert_allclose -from .common import (check_orthonormal, compare_mols, compute_mulliken_charges, - load_one_warning) +from .common import check_orthonormal, compare_mols, compute_mulliken_charges, load_one_warning from ..basis import convert_conventions from ..api import load_one, dump_one from ..overlap import compute_overlap @@ -72,36 +71,36 @@ def check_load_dump_consistency(fn: str, tmpdir: str, match: str = None): """ mol1 = load_one_warning(fn, match=match) - fn_tmp = os.path.join(tmpdir, 'foo.bar') - dump_one(mol1, fn_tmp, fmt='molekel') - mol2 = load_one(fn_tmp, fmt='molekel') - form = fn.split('.') - if 'molden' in form: + fn_tmp = os.path.join(tmpdir, "foo.bar") + dump_one(mol1, fn_tmp, fmt="molekel") + mol2 = load_one(fn_tmp, fmt="molekel") + form = fn.split(".") + if "molden" in form: compare_mols_diff_formats(mol1, mol2) - elif 'fchk' in form: + elif "fchk" in form: compare_mols_diff_formats(mol1, mol2) else: compare_mols(mol1, mol2) def test_load_dump_consistency_h2(tmpdir): - check_load_dump_consistency('h2_sto3g.mkl', tmpdir, match="ORCA") + check_load_dump_consistency("h2_sto3g.mkl", tmpdir, match="ORCA") def test_load_dump_consistency_ethanol(tmpdir): - check_load_dump_consistency('ethanol.mkl', tmpdir, match="ORCA") + check_load_dump_consistency("ethanol.mkl", tmpdir, match="ORCA") def test_load_dump_consistency_li2(tmpdir): - check_load_dump_consistency('li2.mkl', tmpdir, match="ORCA") + check_load_dump_consistency("li2.mkl", tmpdir, match="ORCA") def test_load_molden_dump_molekel_li2(tmpdir): - check_load_dump_consistency('li2.molden.input', tmpdir, match="ORCA") + check_load_dump_consistency("li2.molden.input", tmpdir, match="ORCA") def test_load_fchk_dump_molekel_li2(tmpdir): - check_load_dump_consistency('li2_g09_nbasis_indep.fchk', tmpdir) + check_load_dump_consistency("li2_g09_nbasis_indep.fchk", tmpdir) def test_load_mkl_ethanol(): @@ -112,11 +111,11 @@ def test_load_mkl_ethanol(): assert_equal(mol.atnums[0], 1) assert_equal(mol.atnums[4], 6) assert_equal(mol.atcoords.shape, (9, 3)) - assert_allclose(mol.atcoords[2, 1] / angstrom, 2.239037, atol=1.e-5) - assert_allclose(mol.atcoords[5, 2] / angstrom, 0.948420, atol=1.e-5) - assert_equal(mol.atcharges['mulliken'].shape, (9,)) + assert_allclose(mol.atcoords[2, 1] / angstrom, 2.239037, atol=1.0e-5) + assert_allclose(mol.atcoords[5, 2] / angstrom, 0.948420, atol=1.0e-5) + assert_equal(mol.atcharges["mulliken"].shape, (9,)) q = [0.143316, -0.445861, 0.173045, 0.173021, 0.024542, 0.143066, 0.143080, -0.754230, 0.400021] - assert_allclose(mol.atcharges['mulliken'], q) + assert_allclose(mol.atcharges["mulliken"], q) assert mol.obasis.nbasis == 39 assert_allclose(mol.obasis.shells[0].exponents[0], 18.731137000) assert_allclose(mol.obasis.shells[4].exponents[0], 7.868272400) @@ -143,8 +142,8 @@ def test_load_mkl_ethanol(): def test_load_mkl_li2(): mol = load_one_warning("li2.mkl", match="ORCA") - assert_equal(mol.atcharges['mulliken'].shape, (2,)) - assert_allclose(mol.atcharges['mulliken'], [0.5, 0.5]) + assert_equal(mol.atcharges["mulliken"].shape, (2,)) + assert_allclose(mol.atcharges["mulliken"], [0.5, 0.5]) # check mo normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp) @@ -153,8 +152,8 @@ def test_load_mkl_li2(): def test_load_mkl_h2(): mol = load_one_warning("h2_sto3g.mkl", match="ORCA") - assert_equal(mol.atcharges['mulliken'].shape, (2,)) - assert_allclose(mol.atcharges['mulliken'], [0, 0]) + assert_equal(mol.atcharges["mulliken"].shape, (2,)) + assert_allclose(mol.atcharges["mulliken"], [0, 0]) # check mo normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) diff --git a/iodata/test/test_mwfn.py b/iodata/test/test_mwfn.py index a72657ec..d6e53bae 100644 --- a/iodata/test/test_mwfn.py +++ b/iodata/test/test_mwfn.py @@ -38,36 +38,36 @@ def load_helper(fn): # pylint: disable=too-many-statements def test_load_mwfn_ch3_rohf_g03(): - mol = load_helper('ch3_rohf_sto3g_g03_fchk_multiwfn3.7.mwfn') + mol = load_helper("ch3_rohf_sto3g_g03_fchk_multiwfn3.7.mwfn") assert_equal(mol.mo.occs.shape[0], mol.mo.coeffs.shape[1]) assert_equal(mol.mo.occs.min(), 0.0) assert_equal(mol.mo.occs.max(), 2.0) - assert_equal(mol.extra['full_virial_ratio'], 2.00174844) - assert_equal(mol.extra['nindbasis'], 8) + assert_equal(mol.extra["full_virial_ratio"], 2.00174844) + assert_equal(mol.extra["nindbasis"], 8) assert_equal(np.sum([shell.nprim * shell.nbasis for shell in mol.obasis.shells]), 24) assert_equal(len(mol.obasis.shells), 6) assert_equal(np.sum([shell.nprim for shell in mol.obasis.shells]), 18) assert_equal(mol.charge, 0.0) assert_equal(mol.nelec, 9) assert_equal(mol.natom, 4) - assert_equal(mol.energy, -3.90732095E+01) + assert_equal(mol.energy, -3.90732095e01) assert_allclose([shell.angmoms[0] for shell in mol.obasis.shells], [0, 0, 1, 0, 0, 0]) assert_allclose([shell.icenter for shell in mol.obasis.shells], [0, 0, 0, 1, 2, 3]) assert_allclose([shell.nprim for shell in mol.obasis.shells], [3, 3, 3, 3, 3, 3]) - exponents1 = np.array([7.16168373E+01, 1.30450963E+01, 3.53051216E+00]) - exponents2 = np.array([2.94124936E+00, 6.83483096E-01, 2.22289916E-01]) - exponents3 = np.array([2.94124936E+00, 6.83483096E-01, 2.22289916E-01]) - exponents4 = np.array([3.42525091E+00, 6.23913730E-01, 1.68855404E-01]) + exponents1 = np.array([7.16168373e01, 1.30450963e01, 3.53051216e00]) + exponents2 = np.array([2.94124936e00, 6.83483096e-01, 2.22289916e-01]) + exponents3 = np.array([2.94124936e00, 6.83483096e-01, 2.22289916e-01]) + exponents4 = np.array([3.42525091e00, 6.23913730e-01, 1.68855404e-01]) assert_allclose(mol.obasis.shells[0].exponents, exponents1) assert_allclose(mol.obasis.shells[1].exponents, exponents2) assert_allclose(mol.obasis.shells[2].exponents, exponents3) assert_allclose(mol.obasis.shells[3].exponents, exponents4) assert_allclose(mol.obasis.shells[4].exponents, exponents4) assert_allclose(mol.obasis.shells[5].exponents, exponents4) - coeffs1 = np.array([[1.54328967E-01], [5.35328142E-01], [4.44634542E-01]]) - coeffs2 = np.array([[-9.99672292E-02], [3.99512826E-01], [7.00115469E-01]]) - coeffs3 = np.array([[1.55916275E-01], [6.07683719E-01], [3.91957393E-01]]) - coeffs4 = np.array([[1.54328967E-01], [5.35328142E-01], [4.44634542E-01]]) + coeffs1 = np.array([[1.54328967e-01], [5.35328142e-01], [4.44634542e-01]]) + coeffs2 = np.array([[-9.99672292e-02], [3.99512826e-01], [7.00115469e-01]]) + coeffs3 = np.array([[1.55916275e-01], [6.07683719e-01], [3.91957393e-01]]) + coeffs4 = np.array([[1.54328967e-01], [5.35328142e-01], [4.44634542e-01]]) assert_allclose(mol.obasis.shells[0].coeffs, coeffs1) assert_allclose(mol.obasis.shells[1].coeffs, coeffs2) assert_allclose(mol.obasis.shells[2].coeffs, coeffs3) @@ -75,101 +75,215 @@ def test_load_mwfn_ch3_rohf_g03(): assert_allclose(mol.obasis.shells[4].coeffs, coeffs4) assert_allclose(mol.obasis.shells[5].coeffs, coeffs4) # test first molecular orbital information - coeff = np.array([9.92532359E-01, 3.42148679E-02, 3.30477771E-06, - 1.97321450E-03, - 0.00000000E+00, -6.94439001E-03, - 6.94439001E-03, - 6.94539905E-03]) + coeff = np.array( + [ + 9.92532359e-01, + 3.42148679e-02, + 3.30477771e-06, + -1.97321450e-03, + 0.00000000e00, + -6.94439001e-03, + -6.94439001e-03, + -6.94539905e-03, + ] + ) assert_equal(mol.mo.coeffs[:, 0], coeff) - mo_energies = np.array([-1.09902284E+01, -8.36918686E-01, -5.24254982E-01, -5.23802785E-01, - -1.26686819E-02, 6.64707810E-01, 7.68278159E-01, 7.69362712E-01]) + mo_energies = np.array( + [ + -1.09902284e01, + -8.36918686e-01, + -5.24254982e-01, + -5.23802785e-01, + -1.26686819e-02, + 6.64707810e-01, + 7.68278159e-01, + 7.69362712e-01, + ] + ) assert_allclose(mol.mo.energies, mo_energies) assert_equal(mol.mo.occs[0], 2.000000) - assert_equal(mol.extra['mo_sym'][0], '?') + assert_equal(mol.extra["mo_sym"][0], "?") # test that for the same molecule fchk and mwfn generate the same objects. olp = compute_overlap(mol.obasis, mol.atcoords) - mol2 = load_helper('ch3_rohf_sto3g_g03.fchk') + mol2 = load_helper("ch3_rohf_sto3g_g03.fchk") olp_fchk = compute_overlap(mol2.obasis, mol2.atcoords) - assert_allclose(mol.atcoords, mol2.atcoords, atol=1E-7, rtol=1E-7) + assert_allclose(mol.atcoords, mol2.atcoords, atol=1e-7, rtol=1e-7) assert_allclose(mol2.obasis.shells[0].coeffs, coeffs1) # Mind the gap, I mean... the SP contraction assert_allclose(mol2.obasis.shells[1].coeffs[:, 0], np.squeeze(coeffs2.T)) assert_allclose(mol2.obasis.shells[1].coeffs[:, 1], np.squeeze(coeffs3.T)) assert_allclose(mol2.obasis.shells[3].coeffs, coeffs4) assert_allclose(mol2.obasis.shells[4].coeffs, coeffs4) - assert_allclose(olp, olp_fchk, atol=1E-7, rtol=1E-7) + assert_allclose(olp, olp_fchk, atol=1e-7, rtol=1e-7) def test_load_mwfn_ch3_hf_g03(): - mol = load_helper('ch3_hf_sto3g_fchk_multiwfn3.7.mwfn') + mol = load_helper("ch3_hf_sto3g_fchk_multiwfn3.7.mwfn") assert_equal(mol.mo.occs.shape[0], mol.mo.coeffs.shape[1]) - assert_equal(mol.extra['wfntype'], 1) + assert_equal(mol.extra["wfntype"], 1) # test first molecular orbital information - coeff = np.array([9.91912304E-01, 3.68365244E-02, 9.23239012E-04, 9.05953703E-04, - 9.05953703E-04, -7.36810756E-03, - 7.36810756E-03, - 7.36919429E-03]) + coeff = np.array( + [ + 9.91912304e-01, + 3.68365244e-02, + 9.23239012e-04, + 9.05953703e-04, + 9.05953703e-04, + -7.36810756e-03, + -7.36810756e-03, + -7.36919429e-03, + ] + ) assert_equal(mol.mo.coeffs[:, 0], coeff) - mo_energies = np.array([-1.10094534E+01, -9.07622407E-01, -5.37709620E-01, -5.37273275E-01, - -3.63936540E-01, 6.48361367E-01, 7.58140704E-01, 7.59223157E-01, - -1.09780991E+01, -8.01569083E-01, -5.19454722E-01, -5.18988806E-01, - 3.28562907E-01, 7.04456296E-01, 7.88139770E-01, 7.89228899E-01]) + mo_energies = np.array( + [ + -1.10094534e01, + -9.07622407e-01, + -5.37709620e-01, + -5.37273275e-01, + -3.63936540e-01, + 6.48361367e-01, + 7.58140704e-01, + 7.59223157e-01, + -1.09780991e01, + -8.01569083e-01, + -5.19454722e-01, + -5.18988806e-01, + 3.28562907e-01, + 7.04456296e-01, + 7.88139770e-01, + 7.89228899e-01, + ] + ) assert_allclose(mol.mo.energies, mo_energies) assert_equal(mol.mo.occs[0], 1.000000) - assert_equal(mol.extra['mo_sym'][0], '?') + assert_equal(mol.extra["mo_sym"][0], "?") # test that for the same molecule fchk and mwfn generate the same objects. olp = compute_overlap(mol.obasis, mol.atcoords) - mol2 = load_helper('ch3_hf_sto3g.fchk') + mol2 = load_helper("ch3_hf_sto3g.fchk") olp_fchk = compute_overlap(mol2.obasis, mol2.atcoords) - assert_allclose(mol.atcoords, mol2.atcoords, atol=1E-7, rtol=1E-7) - assert_allclose(olp, olp_fchk, atol=1E-7, rtol=1E-7) + assert_allclose(mol.atcoords, mol2.atcoords, atol=1e-7, rtol=1e-7) + assert_allclose(olp, olp_fchk, atol=1e-7, rtol=1e-7) def test_nelec_charge(): - mol1 = load_helper('ch3_rohf_sto3g_g03_fchk_multiwfn3.7.mwfn') + mol1 = load_helper("ch3_rohf_sto3g_g03_fchk_multiwfn3.7.mwfn") assert mol1.nelec == 9 assert mol1.charge == 0 - mol2 = load_helper('he_spdfgh_virtual_fchk_multiwfn3.7.mwfn') + mol2 = load_helper("he_spdfgh_virtual_fchk_multiwfn3.7.mwfn") assert mol2.nelec == 2 assert mol2.charge == 0 - mol3 = load_helper('ch3_hf_sto3g_fchk_multiwfn3.7.mwfn') + mol3 = load_helper("ch3_hf_sto3g_fchk_multiwfn3.7.mwfn") assert mol3.nelec == 9 assert mol3.charge == 0 def test_load_mwfn_he_spdfgh_g03(): - mol = load_helper('he_spdfgh_virtual_fchk_multiwfn3.7.mwfn') + mol = load_helper("he_spdfgh_virtual_fchk_multiwfn3.7.mwfn") assert_equal(mol.mo.occs.shape[0], mol.mo.coeffs.shape[1]) - assert_equal(mol.extra['wfntype'], 0) + assert_equal(mol.extra["wfntype"], 0) # test first molecular orbital information - coeff = np.array([ - 8.17125208E-01, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 1.58772965E-02, - 1.58772965E-02, 1.58772965E-02, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 7.73667846E-02, 0.00000000E+00, 4.53013505E-02, 0.00000000E+00, 7.73667846E-02, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 4.53013505E-02, - 0.00000000E+00, 4.53013505E-02, 0.00000000E+00, 0.00000000E+00, 7.73667846E-02, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, 0.00000000E+00, - 0.00000000E+00]) + coeff = np.array( + [ + 8.17125208e-01, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 1.58772965e-02, + 1.58772965e-02, + 1.58772965e-02, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 7.73667846e-02, + 0.00000000e00, + 4.53013505e-02, + 0.00000000e00, + 7.73667846e-02, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 4.53013505e-02, + 0.00000000e00, + 4.53013505e-02, + 0.00000000e00, + 0.00000000e00, + 7.73667846e-02, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + 0.00000000e00, + ] + ) assert_equal(mol.mo.coeffs[:, 0], coeff) - mo_energies = np.array([-3.83109139E-01, 6.72890652E-02, 6.72890652E-02, 6.72890652E-02, - 3.33282755E-01, 5.51389775E-01, 5.51389775E-01, 5.51389775E-01, - 5.51389775E-01, 5.51389775E-01, 8.85311032E-01, 8.85311032E-01, - 8.85311032E-01, 1.19945800E+00, 1.37176438E+00, 1.37176438E+00, - 1.37176438E+00, 1.37176438E+00, 1.37176438E+00, 1.37176438E+00, - 1.37176438E+00, 1.89666973E+00, 1.89666973E+00, 1.89666973E+00, - ]) + mo_energies = np.array( + [ + -3.83109139e-01, + 6.72890652e-02, + 6.72890652e-02, + 6.72890652e-02, + 3.33282755e-01, + 5.51389775e-01, + 5.51389775e-01, + 5.51389775e-01, + 5.51389775e-01, + 5.51389775e-01, + 8.85311032e-01, + 8.85311032e-01, + 8.85311032e-01, + 1.19945800e00, + 1.37176438e00, + 1.37176438e00, + 1.37176438e00, + 1.37176438e00, + 1.37176438e00, + 1.37176438e00, + 1.37176438e00, + 1.89666973e00, + 1.89666973e00, + 1.89666973e00, + ] + ) assert_allclose(mol.mo.energies[:24], mo_energies) # energies were truncated at 24 entries, this checks the last energy entry - assert mol.mo.energies[55] == 6.12473238E+00 + assert mol.mo.energies[55] == 6.12473238e00 assert_equal(mol.mo.occs[0], 2.000000) - assert_equal(mol.extra['mo_sym'][0], '?') + assert_equal(mol.extra["mo_sym"][0], "?") # this tests thhe last of the molecular orbital entries assert_equal(mol.mo.occs[55], 0.000000) - assert_equal(mol.extra['mo_sym'][55], '?') + assert_equal(mol.extra["mo_sym"][55], "?") # test that for the same molecule fchk and mwfn generate the same objects. olp = compute_overlap(mol.obasis, mol.atcoords) - mol2 = load_helper('he_spdfgh_virtual.fchk') + mol2 = load_helper("he_spdfgh_virtual.fchk") olp_fchk = compute_overlap(mol2.obasis, mol2.atcoords) - assert_allclose(mol.atcoords, mol2.atcoords, atol=1E-7, rtol=1E-7) - assert_allclose(olp, olp_fchk, atol=1E-7, rtol=1E-7) + assert_allclose(mol.atcoords, mol2.atcoords, atol=1e-7, rtol=1e-7) + assert_allclose(olp, olp_fchk, atol=1e-7, rtol=1e-7) diff --git a/iodata/test/test_orbitals.py b/iodata/test/test_orbitals.py index f1adcf71..bbb627b6 100644 --- a/iodata/test/test_orbitals.py +++ b/iodata/test/test_orbitals.py @@ -19,7 +19,6 @@ # pylint: disable=pointless-statement """Unit tests for iodata.orbitals.""" - import pytest import numpy as np from numpy.testing import assert_equal diff --git a/iodata/test/test_orcalog.py b/iodata/test/test_orcalog.py index 7c3aa59d..b252dee6 100644 --- a/iodata/test/test_orcalog.py +++ b/iodata/test/test_orcalog.py @@ -38,16 +38,19 @@ def test_load_water_number(): assert mol.natom == 3 assert_equal(mol.atnums, [8, 1, 1]) # check bond length - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.9500, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[2]) / angstrom, 0.9500, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[1] - mol.atcoords[2]) / angstrom, 1.5513, atol=1.e-4) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.9500, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[2]) / angstrom, 0.9500, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[1] - mol.atcoords[2]) / angstrom, 1.5513, atol=1.0e-4 + ) # check energies of scf cycles energies = np.array([-76.34739931, -76.34740001, -76.34740005, -76.34740029]) - assert_allclose(mol.extra['scf_energies'], energies) + assert_allclose(mol.extra["scf_energies"], energies) # check scf energy assert_allclose(mol.energy, -76.347791524303, atol=1e-8) # check dipole moment - assert_allclose(mol.moments[(1, 'c')], [0.76499, 0.00000, 0.54230]) + assert_allclose(mol.moments[(1, "c")], [0.76499, 0.00000, 0.54230]) diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index 9091eae2..ec3a3cbe 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -36,10 +36,10 @@ def test_normalization_basics_segmented(): for angmom in range(7): - shells = [Shell(0, [angmom], ['c'], np.array([0.23]), np.array([[1.0]]))] + shells = [Shell(0, [angmom], ["c"], np.array([0.23]), np.array([[1.0]]))] if angmom >= 2: - shells.append(Shell(0, [angmom], ['p'], np.array([0.23]), np.array([[1.0]]))) - obasis = MolecularBasis(shells, OVERLAP_CONVENTIONS, 'L2') + shells.append(Shell(0, [angmom], ["p"], np.array([0.23]), np.array([[1.0]]))) + obasis = MolecularBasis(shells, OVERLAP_CONVENTIONS, "L2") atcoords = np.zeros((1, 3)) overlap = compute_overlap(obasis, atcoords) assert_allclose(np.diag(overlap), np.ones(obasis.nbasis)) @@ -47,8 +47,8 @@ def test_normalization_basics_segmented(): def test_normalization_basics_generalized(): for angmom in range(2, 7): - shells = [Shell(0, [angmom] * 2, ['c', 'p'], np.array([0.23]), np.array([[1.0, 1.0]]))] - obasis = MolecularBasis(shells, OVERLAP_CONVENTIONS, 'L2') + shells = [Shell(0, [angmom] * 2, ["c", "p"], np.array([0.23]), np.array([[1.0, 1.0]]))] + obasis = MolecularBasis(shells, OVERLAP_CONVENTIONS, "L2") atcoords = np.zeros((1, 3)) overlap = compute_overlap(obasis, atcoords) assert_allclose(np.diag(overlap), np.ones(obasis.nbasis)) @@ -59,7 +59,7 @@ def test_load_fchk_hf_sto3g_num(): ref = np.load(str(fn_npy)) with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as fn_fchk: data = load_one(fn_fchk) - assert_allclose(ref, compute_overlap(data.obasis, data.atcoords), rtol=1.e-5, atol=1.e-8) + assert_allclose(ref, compute_overlap(data.obasis, data.atcoords), rtol=1.0e-5, atol=1.0e-8) def test_load_fchk_o2_cc_pvtz_pure_num(): @@ -68,7 +68,7 @@ def test_load_fchk_o2_cc_pvtz_pure_num(): ref = np.load(str(fn_npy)) with as_file(files("iodata.test.data").joinpath("o2_cc_pvtz_pure.fchk")) as fn_fchk: data = load_one(fn_fchk) - assert_allclose(ref, compute_overlap(data.obasis, data.atcoords), rtol=1.e-5, atol=1.e-8) + assert_allclose(ref, compute_overlap(data.obasis, data.atcoords), rtol=1.0e-5, atol=1.0e-8) def test_load_fchk_o2_cc_pvtz_cart_num(): @@ -78,15 +78,15 @@ def test_load_fchk_o2_cc_pvtz_cart_num(): with as_file(files("iodata.test.data").joinpath("o2_cc_pvtz_cart.fchk")) as fn_fchk: data = load_one(fn_fchk) obasis = attr.evolve(data.obasis, conventions=OVERLAP_CONVENTIONS) - assert_allclose(ref, compute_overlap(obasis, data.atcoords), rtol=1.e-5, atol=1.e-8) + assert_allclose(ref, compute_overlap(obasis, data.atcoords), rtol=1.0e-5, atol=1.0e-8) def test_overlap_l1(): - dbasis = MolecularBasis([], {}, 'L1') + dbasis = MolecularBasis([], {}, "L1") atcoords = np.zeros((1, 3)) with pytest.raises(ValueError): _ = compute_overlap(dbasis, atcoords) - obasis = MolecularBasis([], {}, 'L2') + obasis = MolecularBasis([], {}, "L2") with pytest.raises(ValueError): _ = compute_overlap(obasis, atcoords, dbasis, atcoords) @@ -104,7 +104,7 @@ def test_overlap_two_basis_exceptions(): "h_sto3g.fchk", "hf_sto3g.fchk", "2h-azirine-cc.fchk", - "water_ccpvdz_pure_hf_g03.fchk" + "water_ccpvdz_pure_hf_g03.fchk", ] @@ -129,13 +129,12 @@ def test_overlap_two_basis_different(fn0, fn1): # overlap matrix. atcoords = np.concatenate([mol0.atcoords, mol1.atcoords]) shells = mol0.obasis.shells + [ - attr.evolve(shell, icenter=shell.icenter + mol0.natom) - for shell in mol1.obasis.shells + attr.evolve(shell, icenter=shell.icenter + mol0.natom) for shell in mol1.obasis.shells ] obasis = MolecularBasis(shells, OVERLAP_CONVENTIONS, "L2") olp_big = compute_overlap(obasis, atcoords) # Get the off-diagonal block and reorder. - olp_b = olp_big[:olp_a.shape[0], olp_a.shape[0]:] + olp_b = olp_big[: olp_a.shape[0], olp_a.shape[0] :] assert olp_a.shape == olp_b.shape permutation0, signs0 = convert_conventions(mol0.obasis, OVERLAP_CONVENTIONS, reverse=True) olp_b = olp_b[permutation0] * signs0.reshape(-1, 1) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index ae5c4cdb..f4a48a50 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -35,14 +35,14 @@ @pytest.mark.parametrize("case", ["single", "single_model"]) def test_load_water(case): # test pdb of water - with as_file(files("iodata.test.data").joinpath(f'water_{case}.pdb')) as fn_pdb: + with as_file(files("iodata.test.data").joinpath(f"water_{case}.pdb")) as fn_pdb: mol = load_one(str(fn_pdb)) check_water(mol) def test_load_water_no_end(): # test pdb of water - with as_file(files("iodata.test.data").joinpath('water_single_no_end.pdb')) as fn_pdb: + with as_file(files("iodata.test.data").joinpath("water_single_no_end.pdb")) as fn_pdb: with pytest.warns(FileFormatWarning, match="The END is not found"): mol = load_one(str(fn_pdb)) check_water(mol) @@ -53,24 +53,30 @@ def check_water(mol): assert mol.title == "water" assert_equal(mol.atnums, [1, 8, 1]) # check bond length - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.9599, atol=1.e-4) - assert_allclose(np.linalg.norm( - mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.9599, atol=1.e-4) - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.e-3) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.9599, atol=1.0e-4 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.9599, atol=1.0e-4 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.0e-3 + ) assert_equal(mol.bonds[:, :2], [[0, 1], [1, 2]]) -@pytest.mark.parametrize("fn_base,should_warn", [ - ("water_single.pdb", False), - ("water_single_model.pdb", False), - ("ch5plus.pdb", False), - ("2luv.pdb", True), - ("2bcw.pdb", False), -]) +@pytest.mark.parametrize( + "fn_base,should_warn", + [ + ("water_single.pdb", False), + ("water_single_model.pdb", False), + ("ch5plus.pdb", False), + ("2luv.pdb", True), + ("2bcw.pdb", False), + ], +) def test_load_dump_consistency(fn_base, should_warn, tmpdir): - with as_file(files('iodata.test.data').joinpath(fn_base)) as fn_pdb: + with as_file(files("iodata.test.data").joinpath(fn_base)) as fn_pdb: if should_warn: with pytest.warns(FileFormatWarning) as record: mol0 = load_one(str(fn_pdb)) @@ -79,21 +85,21 @@ def test_load_dump_consistency(fn_base, should_warn, tmpdir): mol0 = load_one(str(fn_pdb)) # write pdb file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test.pdb') + fn_tmp = os.path.join(tmpdir, "test.pdb") dump_one(mol0, fn_tmp) mol1 = load_one(fn_tmp) # check two pdb files assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) if mol0.atffparams is not None: - assert_equal(mol0.atffparams.get('attypes'), mol1.atffparams.get('attypes')) - assert_equal(mol0.atffparams.get('restypes'), mol1.atffparams.get('restypes')) - assert_equal(mol0.atffparams.get('resnums'), mol1.atffparams.get('resnums')) + assert_equal(mol0.atffparams.get("attypes"), mol1.atffparams.get("attypes")) + assert_equal(mol0.atffparams.get("restypes"), mol1.atffparams.get("restypes")) + assert_equal(mol0.atffparams.get("resnums"), mol1.atffparams.get("resnums")) if mol0.extra is not None: - assert_equal(mol0.extra.get('occupancies'), mol1.extra.get('occupancies')) - assert_equal(mol0.extra.get('bfactors'), mol1.extra.get('bfactors')) - assert_equal(mol0.extra.get('chainids'), mol1.extra.get('chainids')) + assert_equal(mol0.extra.get("occupancies"), mol1.extra.get("occupancies")) + assert_equal(mol0.extra.get("bfactors"), mol1.extra.get("bfactors")) + assert_equal(mol0.extra.get("chainids"), mol1.extra.get("chainids")) if mol0.bonds is None: assert mol1.bonds is None else: @@ -105,21 +111,21 @@ def test_load_dump_xyz_consistency(tmpdir): mol0 = load_one(str(fn_xyz)) # write xyz file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test.pdb') + fn_tmp = os.path.join(tmpdir, "test.pdb") dump_one(mol0, fn_tmp) mol1 = load_one(fn_tmp) # check two molecule classes to be the same assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-2) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-2) # check if the general restype and attype are correct - restypes = mol1.atffparams.get('restypes') - attypes = mol1.atffparams.get('attypes') + restypes = mol1.atffparams.get("restypes") + attypes = mol1.atffparams.get("attypes") assert restypes[0] == "XXX" assert attypes[0] == "H1" assert mol1.extra.get("chainids") is None # check if resnums are correct - resnums = mol1.atffparams.get('resnums') + resnums = mol1.atffparams.get("resnums") assert_equal(resnums[0], -1) # There should be no bonds assert mol1.bonds is None @@ -133,21 +139,21 @@ def test_load_peptide_2luv(): assert len(record) == 271 assert mol.title.startswith("INTEGRIN") assert_equal(len(mol.atnums), 547) - restypes = mol.atffparams.get('restypes') + restypes = mol.atffparams.get("restypes") assert restypes[0] == "LYS" assert restypes[-1] == "LYS" - attypes = mol.atffparams.get('attypes') + attypes = mol.atffparams.get("attypes") assert attypes[0] == "N" assert attypes[-1] == "O" - resnums = mol.atffparams.get('resnums') + resnums = mol.atffparams.get("resnums") assert_equal(resnums[0], 1) assert_equal(resnums[-1], 35) - assert_allclose(mol.extra.get('occupancies'), np.ones(mol.natom)) - assert_allclose(mol.extra.get('bfactors'), np.zeros(mol.natom)) - assert_equal(mol.extra.get('chainids'), ['A'] * mol.natom) + assert_allclose(mol.extra.get("occupancies"), np.ones(mol.natom)) + assert_allclose(mol.extra.get("bfactors"), np.zeros(mol.natom)) + assert_equal(mol.extra.get("chainids"), ["A"] * mol.natom) -@pytest.mark.parametrize("case", ['trajectory', 'trajectory_no_model']) +@pytest.mark.parametrize("case", ["trajectory", "trajectory_no_model"]) def test_load_many(case): with as_file(files("iodata.test.data").joinpath(f"water_{case}.pdb")) as fn_pdb: mols = list(load_many(str(fn_pdb))) @@ -155,39 +161,44 @@ def test_load_many(case): for mol in mols: assert_equal(mol.atnums, [8, 1, 1]) assert mol.atcoords.shape == (3, 3) - assert mol.extra.get('chainids') is None - assert_allclose(mol.extra.get('occupancies'), np.ones(3)) - assert_allclose(mol.extra.get('bfactors'), np.zeros(3)) + assert mol.extra.get("chainids") is None + assert_allclose(mol.extra.get("occupancies"), np.ones(3)) + assert_allclose(mol.extra.get("bfactors"), np.zeros(3)) assert_allclose(mols[0].atcoords[2] / angstrom, [2.864, 0.114, 3.364]) assert_allclose(mols[2].atcoords[0] / angstrom, [-0.233, -0.790, -3.248]) assert_allclose(mols[-1].atcoords[1] / angstrom, [-2.123, -3.355, -3.354]) -@pytest.mark.parametrize("case", ['trajectory', 'trajectory_no_model']) +@pytest.mark.parametrize("case", ["trajectory", "trajectory_no_model"]) def test_load_dump_many_consistency(case, tmpdir): with as_file(files("iodata.test.data").joinpath(f"water_{case}.pdb")) as fn_pdb: mols0 = list(load_many(str(fn_pdb))) # write pdb file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test') - dump_many(mols0, fn_tmp, fmt='pdb') - mols1 = list(load_many(fn_tmp, fmt='pdb')) + fn_tmp = os.path.join(tmpdir, "test") + dump_many(mols0, fn_tmp, fmt="pdb") + mols1 = list(load_many(fn_tmp, fmt="pdb")) assert len(mols0) == len(mols1) for mol0, mol1 in zip(mols0, mols1): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) def test_load_2bcw(): # test pdb with multiple chains with as_file(files("iodata.test.data").joinpath("2bcw.pdb")) as fn_pdb: mol = load_one(fn_pdb) - assert mol.title == """\ + assert ( + mol.title + == """\ COORDINATES OF THE N-TERMINAL DOMAIN OF RIBOSOMAL PROTEIN L11,C- TERMINAL DOMAIN OF RIBOSOMAL PROTEIN L7/L12 AND A PORTION OF THE G' DOMAIN OF ELONGATION FACTOR G, AS FITTED INTO CRYO-EM MAP OF AN ESCHERICHIA COLI 70S*EF-G*GDP*FUSIDIC ACID COMPLEX""" - assert mol.extra["compound"] == """\ + ) + assert ( + mol.extra["compound"] + == """\ MOL_ID: 1; MOLECULE: 50S RIBOSOMAL PROTEIN L11; CHAIN: A; @@ -202,14 +213,15 @@ def test_load_2bcw(): CHAIN: C; FRAGMENT: A PORTION OF G' DOMAIN'; SYNONYM: EF-G""" + ) assert mol.natom == 191 assert (mol.atnums == 6).all() assert (mol.atffparams["attypes"] == ["CA"] * mol.natom).all() - assert (mol.atffparams["restypes"][:3] == ['GLN', 'ILE', 'LYS']).all() - assert (mol.atffparams["restypes"][-4:] == ['LYS', 'ILE', 'THR', 'PRO']).all() + assert (mol.atffparams["restypes"][:3] == ["GLN", "ILE", "LYS"]).all() + assert (mol.atffparams["restypes"][-4:] == ["LYS", "ILE", "THR", "PRO"]).all() assert_allclose(mol.atcoords[0, 2] / angstrom, -86.956) assert_allclose(mol.atcoords[190, 0] / angstrom, -24.547) - assert_allclose(mol.extra.get('occupancies'), np.ones(mol.natom)) + assert_allclose(mol.extra.get("occupancies"), np.ones(mol.natom)) assert (mol.extra["chainids"] == ["A"] * 65 + ["B"] * 68 + ["C"] * 58).all() diff --git a/iodata/test/test_poscar.py b/iodata/test/test_poscar.py index 6eda9585..d66ff650 100644 --- a/iodata/test/test_poscar.py +++ b/iodata/test/test_poscar.py @@ -35,48 +35,50 @@ def test_load_poscar_water(): with as_file(files("iodata.test.data").joinpath("POSCAR.water")) as fn: mol = load_one(str(fn)) - assert mol.title == 'Water molecule in a box' + assert mol.title == "Water molecule in a box" assert_equal(mol.atnums, [8, 1, 1]) coords = np.array([0.074983 * 15, 0.903122 * 15, 0.000000]) assert_allclose(mol.atcoords[1], coords, atol=1e-7) - assert_allclose(volume(mol.cellvecs), 15 ** 3, atol=1.e-4) + assert_allclose(volume(mol.cellvecs), 15**3, atol=1.0e-4) def test_load_poscar_cubicbn_cartesian(): with as_file(files("iodata.test.data").joinpath("POSCAR.cubicbn_cartesian")) as fn: mol = load_one(str(fn)) - assert mol.title == 'Cubic BN' + assert mol.title == "Cubic BN" assert_equal(mol.atnums, [5, 7]) - assert_allclose(mol.atcoords[1], - np.array([0.25] * 3) * 3.57 * angstrom, atol=1.e-10) - assert_allclose(volume(mol.cellvecs), (3.57 * angstrom) ** 3 / 4, atol=1.e-10) + assert_allclose(mol.atcoords[1], np.array([0.25] * 3) * 3.57 * angstrom, atol=1.0e-10) + assert_allclose(volume(mol.cellvecs), (3.57 * angstrom) ** 3 / 4, atol=1.0e-10) def test_load_poscar_cubicbn_direct(): with as_file(files("iodata.test.data").joinpath("POSCAR.cubicbn_direct")) as fn: mol = load_one(str(fn)) - assert mol.title == 'Cubic BN' + assert mol.title == "Cubic BN" assert_equal(mol.atnums, [5, 7]) - assert_allclose(mol.atcoords[1], - np.array([0.25] * 3) * 3.57 * angstrom, atol=1.e-10) - assert_allclose(volume(mol.cellvecs), (3.57 * angstrom) ** 3 / 4, 1.e-10) + assert_allclose(mol.atcoords[1], np.array([0.25] * 3) * 3.57 * angstrom, atol=1.0e-10) + assert_allclose(volume(mol.cellvecs), (3.57 * angstrom) ** 3 / 4, 1.0e-10) def test_load_dump_consistency(tmpdir): with as_file(files("iodata.test.data").joinpath("water_element.xyz")) as fn: mol0 = load_one(str(fn)) # random matrix generated from a uniform distribution on [0., 5.0) - mol0.cellvecs = np.array([[2.05278155, 0.23284023, 1.59024118], - [4.96430141, 4.73044423, 4.67590975], - [3.48374425, 0.67931228, 0.66281160]]) + mol0.cellvecs = np.array( + [ + [2.05278155, 0.23284023, 1.59024118], + [4.96430141, 4.73044423, 4.67590975], + [3.48374425, 0.67931228, 0.66281160], + ] + ) - fn_tmp = os.path.join(tmpdir, 'POSCAR') + fn_tmp = os.path.join(tmpdir, "POSCAR") dump_one(mol0, fn_tmp) mol1 = load_one(fn_tmp) assert mol0.title == mol1.title assert_equal(mol1.atnums, [8, 1, 1]) - assert_allclose(mol0.atcoords[1], mol1.atcoords[0], atol=1.e-10) - assert_allclose(mol0.atcoords[0], mol1.atcoords[1], atol=1.e-10) - assert_allclose(mol0.atcoords[2], mol1.atcoords[2], atol=1.e-10) - assert_allclose(mol0.cellvecs, mol1.cellvecs, atol=1.e-10) + assert_allclose(mol0.atcoords[1], mol1.atcoords[0], atol=1.0e-10) + assert_allclose(mol0.atcoords[0], mol1.atcoords[1], atol=1.0e-10) + assert_allclose(mol0.atcoords[2], mol1.atcoords[2], atol=1.0e-10) + assert_allclose(mol0.cellvecs, mol1.cellvecs, atol=1.0e-10) diff --git a/iodata/test/test_qchemlog.py b/iodata/test/test_qchemlog.py index 2fab1bf8..597c4d6d 100644 --- a/iodata/test/test_qchemlog.py +++ b/iodata/test/test_qchemlog.py @@ -37,140 +37,432 @@ def test_load_qchemlog_low_h2o(): data = load_qchemlog_low(LineIterator(str(fq))) # check loaded data - assert data['run_type'] == 'freq' - assert data['lot'] == 'hf' - assert data['obasis_name'] == 'cc-pvtz' - assert data['unrestricted'] == 1 - assert data['symm'] == 0 - assert data['g_rot'] == 1 - assert data['alpha_elec'] == 5 - assert data['beta_elec'] == 5 - assert_allclose(data['nuclear_repulsion_energy'], 9.19775748) - assert_allclose(data['energy'], -76.0571936393) - assert data['norba'] == 58 - assert data['norbb'] == 58 - assert data['dipole_tol'] == 2.0231 - assert_allclose(data['enthalpy_dict']['trans_enthalpy'], 0.889) - assert_allclose(data['enthalpy_dict']['rot_enthalpy'], 0.889) - assert_allclose(data['enthalpy_dict']['vib_enthalpy'], 13.883) - assert_allclose(data['enthalpy_dict']['enthalpy_total'], 16.253) - assert_allclose(data['entropy_dict']['trans_entropy'], 34.608) - assert_allclose(data['entropy_dict']['rot_entropy'], 11.82) - assert_allclose(data['entropy_dict']['vib_entropy'], 0.003) - assert_allclose(data['entropy_dict']['entropy_total'], 46.432) - assert data['imaginary_freq'] == 0 - assert_allclose(data['vib_energy'], 13.882) - assert_equal(data['atnums'], np.array([8, 1, 1])) - assert_equal(data['atmasses'], [15.99491, 1.00783, 1.00783]) - atcoords = np.array([[0.00575, 0.00426, -0.00301], - [0.27588, 0.88612, 0.25191], - [0.60257, -0.23578, -0.7114]]) * angstrom - assert_equal(data['atcoords'], atcoords) - assert_equal(data['mo_a_occ'], np.array([-20.5546, -1.3458, -0.7102, -0.5776, -0.5045])) - assert_equal(data['mo_b_occ'], np.array([-20.5546, -1.3458, -0.7102, -0.5776, -0.5045])) - alpha_mo_unoccupied = np.array([0.1423, 0.2041, 0.5445, 0.6021, 0.6682, 0.7874, 0.8014, - 0.8052, 0.861, 0.9557, 1.1314, 1.197, 1.5276, 1.5667, - 2.0366, 2.052, 2.0664, 2.1712, 2.2342, 2.591, 2.9639, - 3.3568, 3.4919, 3.5814, 3.6562, 3.8012, 3.8795, 3.8849, - 3.9617, 4.0196, 4.0768, 4.1932, 4.3149, 4.39, 4.5839, - 4.6857, 4.8666, 5.1595, 5.2529, 5.5288, 6.0522, 6.5707, - 6.9264, 6.9442, 7.0027, 7.0224, 7.068, 7.1668, 7.2377, - 7.4574, 7.7953, 8.2906, 12.8843]) - assert_allclose(data['mo_a_vir'], alpha_mo_unoccupied) - beta_mo_unoccupied = np.array([0.1423, 0.2041, 0.5445, 0.6021, 0.6682, 0.7874, 0.8014, - 0.8052, 0.861, 0.9557, 1.1314, 1.197, 1.5276, 1.5667, - 2.0366, 2.052, 2.0664, 2.1712, 2.2342, 2.591, 2.9639, - 3.3568, 3.4919, 3.5814, 3.6562, 3.8012, 3.8795, 3.8849, - 3.9617, 4.0196, 4.0768, 4.1932, 4.3149, 4.39, 4.5839, - 4.6857, 4.8666, 5.1595, 5.2529, 5.5288, 6.0522, 6.5707, - 6.9264, 6.9442, 7.0027, 7.0224, 7.068, 7.1668, 7.2377, - 7.4574, 7.7953, 8.2906, 12.8843]) - assert_allclose(data['mo_b_vir'], beta_mo_unoccupied) - assert_allclose(data['mulliken_charges'], np.array([-0.482641, 0.241321, 0.241321])) - assert_allclose(data['dipole'], np.array([1.4989, 1.1097, -0.784])) - assert_allclose(data['quadrupole'], [-6.1922, 0.2058, -5.0469, -0.9308, 1.1096, -5.762]) - assert_allclose(data['polarizability_tensor'], [[-6.1256608, -0.1911917, 0.8593603], - [-0.1911917, -7.180854, -1.0224452], - [0.8593603, -1.0224452, -6.52088]]) - hessian = np.array([[3.162861e-01, 8.366060e-02, -2.326701e-01, -8.253820e-02, - -1.226155e-01, -2.676000e-03, -2.337479e-01, 3.895480e-02, 2.353461e-01], - [8.366060e-02, 5.460341e-01, 2.252114e-01, -1.647100e-01, - -4.652302e-01, -1.071603e-01, 8.104940e-02, -8.080390e-02, -1.180510e-01], - [-2.326701e-01, 2.252114e-01, 3.738573e-01, -2.713570e-02, - -1.472865e-01, -7.031900e-02, 2.598057e-01, -7.792490e-02, -3.035382e-01], - [-8.253820e-02, -1.647100e-01, -2.713570e-02, 7.455040e-02, - 1.315365e-01, 1.474740e-02, 7.987800e-03, 3.317350e-02, 1.238830e-02], - [-1.226155e-01, -4.652302e-01, -1.472865e-01, 1.315365e-01, - 4.787640e-01, 1.470895e-01, -8.921000e-03, -1.353380e-02, 1.970000e-04], - [-2.676000e-03, -1.071603e-01, -7.031900e-02, 1.474740e-02, - 1.470895e-01, 8.125140e-02, -1.207140e-02, -3.992910e-02, -1.093230e-02], - [-2.337479e-01, 8.104940e-02, 2.598057e-01, 7.987800e-03, - -8.921000e-03, -1.207140e-02, 2.257601e-01, -7.212840e-02, -2.477343e-01], - [3.895480e-02, -8.080390e-02, -7.792490e-02, 3.317350e-02, - -1.353380e-02, -3.992910e-02, -7.212840e-02, 9.433770e-02, 1.178541e-01], - [2.353461e-01, -1.180510e-01, -3.035382e-01, 1.238830e-02, - 1.970000e-04, -1.093230e-02, -2.477343e-01, 1.178541e-01, 3.144706e-01]]) - assert_allclose(data['athessian'], hessian) + assert data["run_type"] == "freq" + assert data["lot"] == "hf" + assert data["obasis_name"] == "cc-pvtz" + assert data["unrestricted"] == 1 + assert data["symm"] == 0 + assert data["g_rot"] == 1 + assert data["alpha_elec"] == 5 + assert data["beta_elec"] == 5 + assert_allclose(data["nuclear_repulsion_energy"], 9.19775748) + assert_allclose(data["energy"], -76.0571936393) + assert data["norba"] == 58 + assert data["norbb"] == 58 + assert data["dipole_tol"] == 2.0231 + assert_allclose(data["enthalpy_dict"]["trans_enthalpy"], 0.889) + assert_allclose(data["enthalpy_dict"]["rot_enthalpy"], 0.889) + assert_allclose(data["enthalpy_dict"]["vib_enthalpy"], 13.883) + assert_allclose(data["enthalpy_dict"]["enthalpy_total"], 16.253) + assert_allclose(data["entropy_dict"]["trans_entropy"], 34.608) + assert_allclose(data["entropy_dict"]["rot_entropy"], 11.82) + assert_allclose(data["entropy_dict"]["vib_entropy"], 0.003) + assert_allclose(data["entropy_dict"]["entropy_total"], 46.432) + assert data["imaginary_freq"] == 0 + assert_allclose(data["vib_energy"], 13.882) + assert_equal(data["atnums"], np.array([8, 1, 1])) + assert_equal(data["atmasses"], [15.99491, 1.00783, 1.00783]) + atcoords = ( + np.array( + [ + [0.00575, 0.00426, -0.00301], + [0.27588, 0.88612, 0.25191], + [0.60257, -0.23578, -0.7114], + ] + ) + * angstrom + ) + assert_equal(data["atcoords"], atcoords) + assert_equal(data["mo_a_occ"], np.array([-20.5546, -1.3458, -0.7102, -0.5776, -0.5045])) + assert_equal(data["mo_b_occ"], np.array([-20.5546, -1.3458, -0.7102, -0.5776, -0.5045])) + alpha_mo_unoccupied = np.array( + [ + 0.1423, + 0.2041, + 0.5445, + 0.6021, + 0.6682, + 0.7874, + 0.8014, + 0.8052, + 0.861, + 0.9557, + 1.1314, + 1.197, + 1.5276, + 1.5667, + 2.0366, + 2.052, + 2.0664, + 2.1712, + 2.2342, + 2.591, + 2.9639, + 3.3568, + 3.4919, + 3.5814, + 3.6562, + 3.8012, + 3.8795, + 3.8849, + 3.9617, + 4.0196, + 4.0768, + 4.1932, + 4.3149, + 4.39, + 4.5839, + 4.6857, + 4.8666, + 5.1595, + 5.2529, + 5.5288, + 6.0522, + 6.5707, + 6.9264, + 6.9442, + 7.0027, + 7.0224, + 7.068, + 7.1668, + 7.2377, + 7.4574, + 7.7953, + 8.2906, + 12.8843, + ] + ) + assert_allclose(data["mo_a_vir"], alpha_mo_unoccupied) + beta_mo_unoccupied = np.array( + [ + 0.1423, + 0.2041, + 0.5445, + 0.6021, + 0.6682, + 0.7874, + 0.8014, + 0.8052, + 0.861, + 0.9557, + 1.1314, + 1.197, + 1.5276, + 1.5667, + 2.0366, + 2.052, + 2.0664, + 2.1712, + 2.2342, + 2.591, + 2.9639, + 3.3568, + 3.4919, + 3.5814, + 3.6562, + 3.8012, + 3.8795, + 3.8849, + 3.9617, + 4.0196, + 4.0768, + 4.1932, + 4.3149, + 4.39, + 4.5839, + 4.6857, + 4.8666, + 5.1595, + 5.2529, + 5.5288, + 6.0522, + 6.5707, + 6.9264, + 6.9442, + 7.0027, + 7.0224, + 7.068, + 7.1668, + 7.2377, + 7.4574, + 7.7953, + 8.2906, + 12.8843, + ] + ) + assert_allclose(data["mo_b_vir"], beta_mo_unoccupied) + assert_allclose(data["mulliken_charges"], np.array([-0.482641, 0.241321, 0.241321])) + assert_allclose(data["dipole"], np.array([1.4989, 1.1097, -0.784])) + assert_allclose(data["quadrupole"], [-6.1922, 0.2058, -5.0469, -0.9308, 1.1096, -5.762]) + assert_allclose( + data["polarizability_tensor"], + [ + [-6.1256608, -0.1911917, 0.8593603], + [-0.1911917, -7.180854, -1.0224452], + [0.8593603, -1.0224452, -6.52088], + ], + ) + hessian = np.array( + [ + [ + 3.162861e-01, + 8.366060e-02, + -2.326701e-01, + -8.253820e-02, + -1.226155e-01, + -2.676000e-03, + -2.337479e-01, + 3.895480e-02, + 2.353461e-01, + ], + [ + 8.366060e-02, + 5.460341e-01, + 2.252114e-01, + -1.647100e-01, + -4.652302e-01, + -1.071603e-01, + 8.104940e-02, + -8.080390e-02, + -1.180510e-01, + ], + [ + -2.326701e-01, + 2.252114e-01, + 3.738573e-01, + -2.713570e-02, + -1.472865e-01, + -7.031900e-02, + 2.598057e-01, + -7.792490e-02, + -3.035382e-01, + ], + [ + -8.253820e-02, + -1.647100e-01, + -2.713570e-02, + 7.455040e-02, + 1.315365e-01, + 1.474740e-02, + 7.987800e-03, + 3.317350e-02, + 1.238830e-02, + ], + [ + -1.226155e-01, + -4.652302e-01, + -1.472865e-01, + 1.315365e-01, + 4.787640e-01, + 1.470895e-01, + -8.921000e-03, + -1.353380e-02, + 1.970000e-04, + ], + [ + -2.676000e-03, + -1.071603e-01, + -7.031900e-02, + 1.474740e-02, + 1.470895e-01, + 8.125140e-02, + -1.207140e-02, + -3.992910e-02, + -1.093230e-02, + ], + [ + -2.337479e-01, + 8.104940e-02, + 2.598057e-01, + 7.987800e-03, + -8.921000e-03, + -1.207140e-02, + 2.257601e-01, + -7.212840e-02, + -2.477343e-01, + ], + [ + 3.895480e-02, + -8.080390e-02, + -7.792490e-02, + 3.317350e-02, + -1.353380e-02, + -3.992910e-02, + -7.212840e-02, + 9.433770e-02, + 1.178541e-01, + ], + [ + 2.353461e-01, + -1.180510e-01, + -3.035382e-01, + 1.238830e-02, + 1.970000e-04, + -1.093230e-02, + -2.477343e-01, + 1.178541e-01, + 3.144706e-01, + ], + ] + ) + assert_allclose(data["athessian"], hessian) def test_load_one_qchemlog_freq(): source = files("iodata.test.data").joinpath("water_hf_ccpvtz_freq_qchem.out") with as_file(source) as fn_qchemlog: - mol = load_one(str(fn_qchemlog), fmt='qchemlog') + mol = load_one(str(fn_qchemlog), fmt="qchemlog") assert_allclose(mol.energy, -76.0571936393) assert mol.g_rot == 1 assert mol.nelec == 10 - assert mol.run_type == 'freq' - assert mol.lot == 'hf' - assert mol.obasis_name == 'cc-pvtz' - assert_allclose(mol.atcharges['mulliken'], np.array([-0.482641, 0.241321, 0.241321])) - assert_equal(mol.moments[(1, 'c')], np.array([1.4989, 1.1097, -0.784])) - assert_equal(mol.moments[(2, 'c')], - np.array([-6.1922, 0.2058, -0.9308, -5.0469, 1.1096, -5.762])) - hessian = np.array([[3.162861e-01, 8.366060e-02, -2.326701e-01, -8.253820e-02, - -1.226155e-01, -2.676000e-03, -2.337479e-01, 3.895480e-02, 2.353461e-01], - [8.366060e-02, 5.460341e-01, 2.252114e-01, -1.647100e-01, - -4.652302e-01, -1.071603e-01, 8.104940e-02, -8.080390e-02, -1.180510e-01], - [-2.326701e-01, 2.252114e-01, 3.738573e-01, -2.713570e-02, - -1.472865e-01, -7.031900e-02, 2.598057e-01, -7.792490e-02, -3.035382e-01], - [-8.253820e-02, -1.647100e-01, -2.713570e-02, 7.455040e-02, - 1.315365e-01, 1.474740e-02, 7.987800e-03, 3.317350e-02, 1.238830e-02], - [-1.226155e-01, -4.652302e-01, -1.472865e-01, 1.315365e-01, - 4.787640e-01, 1.470895e-01, -8.921000e-03, -1.353380e-02, 1.970000e-04], - [-2.676000e-03, -1.071603e-01, -7.031900e-02, 1.474740e-02, - 1.470895e-01, 8.125140e-02, -1.207140e-02, -3.992910e-02, -1.093230e-02], - [-2.337479e-01, 8.104940e-02, 2.598057e-01, 7.987800e-03, - -8.921000e-03, -1.207140e-02, 2.257601e-01, -7.212840e-02, -2.477343e-01], - [3.895480e-02, -8.080390e-02, -7.792490e-02, 3.317350e-02, - -1.353380e-02, -3.992910e-02, -7.212840e-02, 9.433770e-02, 1.178541e-01], - [2.353461e-01, -1.180510e-01, -3.035382e-01, 1.238830e-02, - 1.970000e-04, -1.093230e-02, -2.477343e-01, 1.178541e-01, 3.144706e-01]]) + assert mol.run_type == "freq" + assert mol.lot == "hf" + assert mol.obasis_name == "cc-pvtz" + assert_allclose(mol.atcharges["mulliken"], np.array([-0.482641, 0.241321, 0.241321])) + assert_equal(mol.moments[(1, "c")], np.array([1.4989, 1.1097, -0.784])) + assert_equal( + mol.moments[(2, "c")], np.array([-6.1922, 0.2058, -0.9308, -5.0469, 1.1096, -5.762]) + ) + hessian = np.array( + [ + [ + 3.162861e-01, + 8.366060e-02, + -2.326701e-01, + -8.253820e-02, + -1.226155e-01, + -2.676000e-03, + -2.337479e-01, + 3.895480e-02, + 2.353461e-01, + ], + [ + 8.366060e-02, + 5.460341e-01, + 2.252114e-01, + -1.647100e-01, + -4.652302e-01, + -1.071603e-01, + 8.104940e-02, + -8.080390e-02, + -1.180510e-01, + ], + [ + -2.326701e-01, + 2.252114e-01, + 3.738573e-01, + -2.713570e-02, + -1.472865e-01, + -7.031900e-02, + 2.598057e-01, + -7.792490e-02, + -3.035382e-01, + ], + [ + -8.253820e-02, + -1.647100e-01, + -2.713570e-02, + 7.455040e-02, + 1.315365e-01, + 1.474740e-02, + 7.987800e-03, + 3.317350e-02, + 1.238830e-02, + ], + [ + -1.226155e-01, + -4.652302e-01, + -1.472865e-01, + 1.315365e-01, + 4.787640e-01, + 1.470895e-01, + -8.921000e-03, + -1.353380e-02, + 1.970000e-04, + ], + [ + -2.676000e-03, + -1.071603e-01, + -7.031900e-02, + 1.474740e-02, + 1.470895e-01, + 8.125140e-02, + -1.207140e-02, + -3.992910e-02, + -1.093230e-02, + ], + [ + -2.337479e-01, + 8.104940e-02, + 2.598057e-01, + 7.987800e-03, + -8.921000e-03, + -1.207140e-02, + 2.257601e-01, + -7.212840e-02, + -2.477343e-01, + ], + [ + 3.895480e-02, + -8.080390e-02, + -7.792490e-02, + 3.317350e-02, + -1.353380e-02, + -3.992910e-02, + -7.212840e-02, + 9.433770e-02, + 1.178541e-01, + ], + [ + 2.353461e-01, + -1.180510e-01, + -3.035382e-01, + 1.238830e-02, + 1.970000e-04, + -1.093230e-02, + -2.477343e-01, + 1.178541e-01, + 3.144706e-01, + ], + ] + ) assert_equal(mol.athessian, hessian) - assert mol.extra['nuclear_repulsion_energy'] == 9.19775748 - assert mol.extra['imaginary_freq'] == 0 + assert mol.extra["nuclear_repulsion_energy"] == 9.19775748 + assert mol.extra["imaginary_freq"] == 0 # unit conversion for entropy terms, used atomic units + Kalvin - assert_allclose(mol.extra['entropy_dict']['trans_entropy'], 34.608 * 1.593601437640628e-06) - assert_allclose(mol.extra['entropy_dict']['rot_entropy'], 11.82 * 1.593601437640628e-06) - assert_allclose(mol.extra['entropy_dict']['vib_entropy'], 0.003 * 1.593601437640628e-06) - assert_allclose(mol.extra['entropy_dict']['entropy_total'], 46.432 * 1.593601437640628e-06) - assert_allclose(mol.extra['vib_energy'], 0.022122375167392933) - assert_allclose(mol.extra['enthalpy_dict']['trans_enthalpy'], 0.0014167116787071256) - assert_allclose(mol.extra['enthalpy_dict']['rot_enthalpy'], 0.0014167116787071256) - assert_allclose(mol.extra['enthalpy_dict']['vib_enthalpy'], 0.022123968768831298) - assert_allclose(mol.extra['enthalpy_dict']['enthalpy_total'], 0.025900804177758054) - polarizability_tensor = np.array([[-6.1256608, -0.1911917, 0.8593603], - [-0.1911917, -7.180854, -1.0224452], - [0.8593603, -1.0224452, -6.52088]]) - assert_equal(mol.extra['polarizability_tensor'], polarizability_tensor) - atcoords = np.array([[0.00575, 0.00426, -0.00301], - [0.27588, 0.88612, 0.25191], - [0.60257, -0.23578, -0.7114]]) * angstrom + assert_allclose(mol.extra["entropy_dict"]["trans_entropy"], 34.608 * 1.593601437640628e-06) + assert_allclose(mol.extra["entropy_dict"]["rot_entropy"], 11.82 * 1.593601437640628e-06) + assert_allclose(mol.extra["entropy_dict"]["vib_entropy"], 0.003 * 1.593601437640628e-06) + assert_allclose(mol.extra["entropy_dict"]["entropy_total"], 46.432 * 1.593601437640628e-06) + assert_allclose(mol.extra["vib_energy"], 0.022122375167392933) + assert_allclose(mol.extra["enthalpy_dict"]["trans_enthalpy"], 0.0014167116787071256) + assert_allclose(mol.extra["enthalpy_dict"]["rot_enthalpy"], 0.0014167116787071256) + assert_allclose(mol.extra["enthalpy_dict"]["vib_enthalpy"], 0.022123968768831298) + assert_allclose(mol.extra["enthalpy_dict"]["enthalpy_total"], 0.025900804177758054) + polarizability_tensor = np.array( + [ + [-6.1256608, -0.1911917, 0.8593603], + [-0.1911917, -7.180854, -1.0224452], + [0.8593603, -1.0224452, -6.52088], + ] + ) + assert_equal(mol.extra["polarizability_tensor"], polarizability_tensor) + atcoords = ( + np.array( + [ + [0.00575, 0.00426, -0.00301], + [0.27588, 0.88612, 0.25191], + [0.60257, -0.23578, -0.7114], + ] + ) + * angstrom + ) assert_equal(mol.atcoords, atcoords) assert_equal(mol.atmasses, np.array([15.99491, 1.00783, 1.00783])) assert_equal(mol.atnums, np.array([8, 1, 1])) # molecule orbital related # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 58 assert mol.mo.norbb == 58 assert mol.mo.norb == 116 @@ -182,13 +474,63 @@ def test_load_one_qchemlog_freq(): # beta occupied orbital energies assert_allclose(mol.mo.energies[58:63], occupied) # alpha virtual orbital energies - virtual = np.array([0.1423, 0.2041, 0.5445, 0.6021, 0.6682, 0.7874, 0.8014, 0.8052, - 0.8610, 0.9557, 1.1314, 1.1970, 1.5276, 1.5667, 2.0366, 2.0520, - 2.0664, 2.1712, 2.2342, 2.5910, 2.9639, 3.3568, 3.4919, 3.5814, - 3.6562, 3.8012, 3.8795, 3.8849, 3.9617, 4.0196, 4.0768, 4.1932, - 4.3149, 4.3900, 4.5839, 4.6857, 4.8666, 5.1595, 5.2529, 5.5288, - 6.0522, 6.5707, 6.9264, 6.9442, 7.0027, 7.0224, 7.0680, 7.1668, - 7.2377, 7.4574, 7.7953, 8.2906, 12.8843]) + virtual = np.array( + [ + 0.1423, + 0.2041, + 0.5445, + 0.6021, + 0.6682, + 0.7874, + 0.8014, + 0.8052, + 0.8610, + 0.9557, + 1.1314, + 1.1970, + 1.5276, + 1.5667, + 2.0366, + 2.0520, + 2.0664, + 2.1712, + 2.2342, + 2.5910, + 2.9639, + 3.3568, + 3.4919, + 3.5814, + 3.6562, + 3.8012, + 3.8795, + 3.8849, + 3.9617, + 4.0196, + 4.0768, + 4.1932, + 4.3149, + 4.3900, + 4.5839, + 4.6857, + 4.8666, + 5.1595, + 5.2529, + 5.5288, + 6.0522, + 6.5707, + 6.9264, + 6.9442, + 7.0027, + 7.0224, + 7.0680, + 7.1668, + 7.2377, + 7.4574, + 7.7953, + 8.2906, + 12.8843, + ] + ) assert_allclose(mol.mo.energies[5:58], virtual) # beta virtual orbital energies assert_allclose(mol.mo.energies[63:], virtual) @@ -201,172 +543,413 @@ def test_load_qchemlog_low_qchemlog_h2o_dimer_eda2(): data = load_qchemlog_low(LineIterator(str(fq))) # check loaded data - assert data['run_type'] == 'eda' - assert data['lot'] == 'wb97x-v' - assert data['obasis_name'] == 'def2-tzvpd' - assert not data['unrestricted'] - assert not data['symm'] - assert data['alpha_elec'] == 10 - assert data['beta_elec'] == 10 - assert_allclose(data['nuclear_repulsion_energy'], 36.66284801) - assert_allclose(data['energy'], -152.8772543727) - assert data['norba'] == 116 - assert_allclose(data['dipole_tol'], 2.5701) - assert_equal(data['atnums'], np.array([8, 1, 1, 8, 1, 1])) - atcoords = np.array([[-1.5510070000, -0.1145200000, 0.0000000000], - [-1.9342590000, 0.7625030000, 0.0000000000], - [-0.5996770000, 0.0407120000, 0.0000000000], - [1.3506250000, 0.1114690000, 0.0000000000], - [1.6803980000, -0.3737410000, -0.7585610000], - [1.6803980000, -0.3737410000, 0.7585610000]]) * angstrom - assert_allclose(data['atcoords'], atcoords) - assert_allclose(data['mo_a_occ'], np.array([-19.2455, -19.1897, -1.1734, -1.1173, -0.6729, - -0.6242, -0.5373, -0.4825, -0.4530, -0.4045])) + assert data["run_type"] == "eda" + assert data["lot"] == "wb97x-v" + assert data["obasis_name"] == "def2-tzvpd" + assert not data["unrestricted"] + assert not data["symm"] + assert data["alpha_elec"] == 10 + assert data["beta_elec"] == 10 + assert_allclose(data["nuclear_repulsion_energy"], 36.66284801) + assert_allclose(data["energy"], -152.8772543727) + assert data["norba"] == 116 + assert_allclose(data["dipole_tol"], 2.5701) + assert_equal(data["atnums"], np.array([8, 1, 1, 8, 1, 1])) + atcoords = ( + np.array( + [ + [-1.5510070000, -0.1145200000, 0.0000000000], + [-1.9342590000, 0.7625030000, 0.0000000000], + [-0.5996770000, 0.0407120000, 0.0000000000], + [1.3506250000, 0.1114690000, 0.0000000000], + [1.6803980000, -0.3737410000, -0.7585610000], + [1.6803980000, -0.3737410000, 0.7585610000], + ] + ) + * angstrom + ) + assert_allclose(data["atcoords"], atcoords) + assert_allclose( + data["mo_a_occ"], + np.array( + [ + -19.2455, + -19.1897, + -1.1734, + -1.1173, + -0.6729, + -0.6242, + -0.5373, + -0.4825, + -0.4530, + -0.4045, + ] + ), + ) - alpha_mo_unoccupied = np.array([0.0485, 0.0863, 0.0927, 0.1035, 0.1344, 0.1474, 0.1539, 0.1880, - 0.1982, 0.2280, 0.2507, 0.2532, 0.2732, 0.2865, 0.2992, 0.3216, - 0.3260, 0.3454, 0.3542, 0.3850, 0.3991, 0.4016, 0.4155, 0.4831, - 0.5016, 0.5133, 0.5502, 0.5505, 0.5745, 0.5992, 0.6275, 0.6454, - 0.6664, 0.6869, 0.7423, 0.7874, 0.8039, 0.8204, 0.8457, 0.9021, - 0.9149, 0.9749, 1.0168, 1.0490, 1.1274, 1.2009, 1.6233, 1.6642, - 1.6723, 1.6877, 1.7314, 1.7347, 1.8246, 1.8635, 1.8877, 1.9254, - 2.0091, 2.1339, 2.2139, 2.2489, 2.2799, 2.3420, 2.3777, 2.5255, - 2.6135, 2.6373, 2.6727, 2.7228, 2.8765, 2.8841, 2.9076, 2.9624, - 3.0377, 3.0978, 3.2509, 3.3613, 3.8767, 3.9603, 4.0824, 4.1424, - 5.1826, 5.2283, 5.3319, 5.3817, 5.4919, 5.5386, 5.5584, 5.5648, - 5.6049, 5.6226, 6.1591, 6.2079, 6.3862, 6.4446, 6.5805, 6.5926, - 6.6092, 6.6315, 6.6557, 6.6703, 7.0400, 7.1334, 7.1456, 7.2547, - 43.7457, 43.9166]) - assert_allclose(data['mo_a_vir'], alpha_mo_unoccupied) - assert_allclose(data['mulliken_charges'], np.array([-0.610250, 0.304021, 0.337060, - -0.663525, 0.316347, 0.316347])) - assert_allclose(data['dipole'], np.array([2.5689, 0.0770, 0.0000])) - assert_allclose(data['quadrupole'], [-12.0581, -6.2544, -12.8954, -0.0000, -0.0000, -12.2310]) + alpha_mo_unoccupied = np.array( + [ + 0.0485, + 0.0863, + 0.0927, + 0.1035, + 0.1344, + 0.1474, + 0.1539, + 0.1880, + 0.1982, + 0.2280, + 0.2507, + 0.2532, + 0.2732, + 0.2865, + 0.2992, + 0.3216, + 0.3260, + 0.3454, + 0.3542, + 0.3850, + 0.3991, + 0.4016, + 0.4155, + 0.4831, + 0.5016, + 0.5133, + 0.5502, + 0.5505, + 0.5745, + 0.5992, + 0.6275, + 0.6454, + 0.6664, + 0.6869, + 0.7423, + 0.7874, + 0.8039, + 0.8204, + 0.8457, + 0.9021, + 0.9149, + 0.9749, + 1.0168, + 1.0490, + 1.1274, + 1.2009, + 1.6233, + 1.6642, + 1.6723, + 1.6877, + 1.7314, + 1.7347, + 1.8246, + 1.8635, + 1.8877, + 1.9254, + 2.0091, + 2.1339, + 2.2139, + 2.2489, + 2.2799, + 2.3420, + 2.3777, + 2.5255, + 2.6135, + 2.6373, + 2.6727, + 2.7228, + 2.8765, + 2.8841, + 2.9076, + 2.9624, + 3.0377, + 3.0978, + 3.2509, + 3.3613, + 3.8767, + 3.9603, + 4.0824, + 4.1424, + 5.1826, + 5.2283, + 5.3319, + 5.3817, + 5.4919, + 5.5386, + 5.5584, + 5.5648, + 5.6049, + 5.6226, + 6.1591, + 6.2079, + 6.3862, + 6.4446, + 6.5805, + 6.5926, + 6.6092, + 6.6315, + 6.6557, + 6.6703, + 7.0400, + 7.1334, + 7.1456, + 7.2547, + 43.7457, + 43.9166, + ] + ) + assert_allclose(data["mo_a_vir"], alpha_mo_unoccupied) + assert_allclose( + data["mulliken_charges"], + np.array([-0.610250, 0.304021, 0.337060, -0.663525, 0.316347, 0.316347]), + ) + assert_allclose(data["dipole"], np.array([2.5689, 0.0770, 0.0000])) + assert_allclose(data["quadrupole"], [-12.0581, -6.2544, -12.8954, -0.0000, -0.0000, -12.2310]) # check eda2 info - assert_allclose(data['eda2']['e_elec'], -65.9887) - assert_allclose(data['eda2']['e_kep_pauli'], 78.5700) - assert_allclose(data['eda2']['e_disp_free_pauli'], -14.2495) - assert_allclose(data['eda2']['e_disp'], -7.7384) - assert_allclose(data['eda2']['e_cls_elec'], -35.1257) - assert_allclose(data['eda2']['e_cls_pauli'], 25.7192) - assert_allclose(data['eda2']['e_mod_pauli'], 33.4576) - assert_allclose(data['eda2']['preparation'], 0.0000) - assert_allclose(data['eda2']['frozen'], -1.6681) - assert_allclose(data['eda2']['pauli'], 64.3205) - assert_allclose(data['eda2']['dispersion'], -7.7384) - assert_allclose(data['eda2']['polarization'], -4.6371) - assert_allclose(data['eda2']['charge transfer'], -7.0689) - assert_allclose(data['eda2']['total'], -21.1126) + assert_allclose(data["eda2"]["e_elec"], -65.9887) + assert_allclose(data["eda2"]["e_kep_pauli"], 78.5700) + assert_allclose(data["eda2"]["e_disp_free_pauli"], -14.2495) + assert_allclose(data["eda2"]["e_disp"], -7.7384) + assert_allclose(data["eda2"]["e_cls_elec"], -35.1257) + assert_allclose(data["eda2"]["e_cls_pauli"], 25.7192) + assert_allclose(data["eda2"]["e_mod_pauli"], 33.4576) + assert_allclose(data["eda2"]["preparation"], 0.0000) + assert_allclose(data["eda2"]["frozen"], -1.6681) + assert_allclose(data["eda2"]["pauli"], 64.3205) + assert_allclose(data["eda2"]["dispersion"], -7.7384) + assert_allclose(data["eda2"]["polarization"], -4.6371) + assert_allclose(data["eda2"]["charge transfer"], -7.0689) + assert_allclose(data["eda2"]["total"], -21.1126) # check fragments coords1 = [[-1.551007, -0.11452, 0.0], [-1.934259, 0.762503, 0.0], [-0.599677, 0.040712, 0.0]] - coords2 = [[1.350625, 0.111469, 0.0], [1.680398, -0.373741, -0.758561], - [1.680398, -0.373741, 0.758561]] - assert_equal(len(data['frags']), 2) + coords2 = [ + [1.350625, 0.111469, 0.0], + [1.680398, -0.373741, -0.758561], + [1.680398, -0.373741, 0.758561], + ] + assert_equal(len(data["frags"]), 2) - assert_equal(data['frags'][0]['atnums'], [8, 1, 1]) - assert_equal(data['frags'][0]['alpha_elec'], 5) - assert_equal(data['frags'][0]['beta_elec'], 5) - assert_equal(data['frags'][0]['nbasis'], 58) - assert_allclose(data['frags'][0]['atcoords'], np.array(coords1) * angstrom) - assert_allclose(data['frags'][0]['nuclear_repulsion_energy'], 9.16383018) - assert_allclose(data['frags'][0]['energy'], -76.4345994141) + assert_equal(data["frags"][0]["atnums"], [8, 1, 1]) + assert_equal(data["frags"][0]["alpha_elec"], 5) + assert_equal(data["frags"][0]["beta_elec"], 5) + assert_equal(data["frags"][0]["nbasis"], 58) + assert_allclose(data["frags"][0]["atcoords"], np.array(coords1) * angstrom) + assert_allclose(data["frags"][0]["nuclear_repulsion_energy"], 9.16383018) + assert_allclose(data["frags"][0]["energy"], -76.4345994141) - assert_equal(data['frags'][1]['atnums'], [8, 1, 1]) - assert_equal(data['frags'][1]['alpha_elec'], 5) - assert_equal(data['frags'][1]['beta_elec'], 5) - assert_equal(data['frags'][1]['nbasis'], 58) - assert_allclose(data['frags'][1]['atcoords'], np.array(coords2) * angstrom) - assert_allclose(data['frags'][1]['nuclear_repulsion_energy'], 9.17803894) - assert_allclose(data['frags'][1]['energy'], -76.4346136883) + assert_equal(data["frags"][1]["atnums"], [8, 1, 1]) + assert_equal(data["frags"][1]["alpha_elec"], 5) + assert_equal(data["frags"][1]["beta_elec"], 5) + assert_equal(data["frags"][1]["nbasis"], 58) + assert_allclose(data["frags"][1]["atcoords"], np.array(coords2) * angstrom) + assert_allclose(data["frags"][1]["nuclear_repulsion_energy"], 9.17803894) + assert_allclose(data["frags"][1]["energy"], -76.4346136883) def test_load_one_h2o_dimer_eda2(): """Test load_one with h2o_dimer_eda_qchem5.3.out.""" # pylint: disable=too-many-statements with as_file(files("iodata.test.data").joinpath("h2o_dimer_eda_qchem5.3.out")) as fn_qchemlog: - mol = load_one(str(fn_qchemlog), fmt='qchemlog') + mol = load_one(str(fn_qchemlog), fmt="qchemlog") # check loaded data - assert mol.run_type == 'eda' - assert mol.lot == 'wb97x-v' - assert mol.obasis_name == 'def2-tzvpd' - assert mol.mo.kind == 'restricted' + assert mol.run_type == "eda" + assert mol.lot == "wb97x-v" + assert mol.obasis_name == "def2-tzvpd" + assert mol.mo.kind == "restricted" # assert not data['symm'] # # assert data['g_rot'] == 1 # assert data['alpha_elec'] == 10 # assert data['beta_elec'] == 10 - assert_allclose(mol.extra['nuclear_repulsion_energy'], 36.66284801) + assert_allclose(mol.extra["nuclear_repulsion_energy"], 36.66284801) assert_allclose(mol.energy, -152.8772543727) assert mol.mo.norba == 116 assert mol.mo.norbb == 116 assert_equal(mol.atnums, np.array([8, 1, 1, 8, 1, 1])) # assert_equal(data['atmasses'], [15.99491, 1.00783, 1.00783]) - atcoords = np.array([[-1.5510070000, -0.1145200000, 0.0000000000], - [-1.9342590000, 0.7625030000, 0.0000000000], - [-0.5996770000, 0.0407120000, 0.0000000000], - [1.3506250000, 0.1114690000, 0.0000000000], - [1.6803980000, -0.3737410000, -0.7585610000], - [1.6803980000, -0.3737410000, 0.7585610000]]) * angstrom + atcoords = ( + np.array( + [ + [-1.5510070000, -0.1145200000, 0.0000000000], + [-1.9342590000, 0.7625030000, 0.0000000000], + [-0.5996770000, 0.0407120000, 0.0000000000], + [1.3506250000, 0.1114690000, 0.0000000000], + [1.6803980000, -0.3737410000, -0.7585610000], + [1.6803980000, -0.3737410000, 0.7585610000], + ] + ) + * angstrom + ) assert_equal(mol.atcoords, atcoords) # check MO energies - mo_energies_a = [-19.2455, -19.1897, -1.1734, -1.1173, -0.6729, -0.6242, -0.5373, -0.4825, - -0.4530, -0.4045, 0.0485, 0.0863, 0.0927, 0.1035, 0.1344, 0.1474, 0.1539, - 0.1880, 0.1982, 0.2280, 0.2507, 0.2532, 0.2732, 0.2865, 0.2992, 0.3216, - 0.3260, 0.3454, 0.3542, 0.3850, 0.3991, 0.4016, 0.4155, 0.4831, 0.5016, - 0.5133, 0.5502, 0.5505, 0.5745, 0.5992, 0.6275, 0.6454, 0.6664, 0.6869, - 0.7423, 0.7874, 0.8039, 0.8204, 0.8457, 0.9021, 0.9149, 0.9749, 1.0168, - 1.0490, 1.1274, 1.2009, 1.6233, 1.6642, 1.6723, 1.6877, 1.7314, 1.7347, - 1.8246, 1.8635, 1.8877, 1.9254, 2.0091, 2.1339, 2.2139, 2.2489, 2.2799, - 2.3420, 2.3777, 2.5255, 2.6135, 2.6373, 2.6727, 2.7228, 2.8765, 2.8841, - 2.9076, 2.9624, 3.0377, 3.0978, 3.2509, 3.3613, 3.8767, 3.9603, 4.0824, - 4.1424, 5.1826, 5.2283, 5.3319, 5.3817, 5.4919, 5.5386, 5.5584, 5.5648, - 5.6049, 5.6226, 6.1591, 6.2079, 6.3862, 6.4446, 6.5805, 6.5926, 6.6092, - 6.6315, 6.6557, 6.6703, 7.0400, 7.1334, 7.1456, 7.2547, 43.7457, 43.9166] + mo_energies_a = [ + -19.2455, + -19.1897, + -1.1734, + -1.1173, + -0.6729, + -0.6242, + -0.5373, + -0.4825, + -0.4530, + -0.4045, + 0.0485, + 0.0863, + 0.0927, + 0.1035, + 0.1344, + 0.1474, + 0.1539, + 0.1880, + 0.1982, + 0.2280, + 0.2507, + 0.2532, + 0.2732, + 0.2865, + 0.2992, + 0.3216, + 0.3260, + 0.3454, + 0.3542, + 0.3850, + 0.3991, + 0.4016, + 0.4155, + 0.4831, + 0.5016, + 0.5133, + 0.5502, + 0.5505, + 0.5745, + 0.5992, + 0.6275, + 0.6454, + 0.6664, + 0.6869, + 0.7423, + 0.7874, + 0.8039, + 0.8204, + 0.8457, + 0.9021, + 0.9149, + 0.9749, + 1.0168, + 1.0490, + 1.1274, + 1.2009, + 1.6233, + 1.6642, + 1.6723, + 1.6877, + 1.7314, + 1.7347, + 1.8246, + 1.8635, + 1.8877, + 1.9254, + 2.0091, + 2.1339, + 2.2139, + 2.2489, + 2.2799, + 2.3420, + 2.3777, + 2.5255, + 2.6135, + 2.6373, + 2.6727, + 2.7228, + 2.8765, + 2.8841, + 2.9076, + 2.9624, + 3.0377, + 3.0978, + 3.2509, + 3.3613, + 3.8767, + 3.9603, + 4.0824, + 4.1424, + 5.1826, + 5.2283, + 5.3319, + 5.3817, + 5.4919, + 5.5386, + 5.5584, + 5.5648, + 5.6049, + 5.6226, + 6.1591, + 6.2079, + 6.3862, + 6.4446, + 6.5805, + 6.5926, + 6.6092, + 6.6315, + 6.6557, + 6.6703, + 7.0400, + 7.1334, + 7.1456, + 7.2547, + 43.7457, + 43.9166, + ] assert_allclose(mol.mo.energiesa, mo_energies_a) assert_allclose(mol.mo.energiesb, mo_energies_a) - assert_equal(mol.atcharges['mulliken'], np.array([-0.610250, 0.304021, 0.337060, - -0.663525, 0.316347, 0.316347])) - assert_allclose(mol.moments[(1, 'c')], np.array([2.5689, 0.0770, 0.0000])) - assert_allclose(mol.moments[(2, 'c')], - [-12.0581, -6.2544, -0.0000, -12.8954, -0.0000, -12.2310]) + assert_equal( + mol.atcharges["mulliken"], + np.array([-0.610250, 0.304021, 0.337060, -0.663525, 0.316347, 0.316347]), + ) + assert_allclose(mol.moments[(1, "c")], np.array([2.5689, 0.0770, 0.0000])) + assert_allclose( + mol.moments[(2, "c")], [-12.0581, -6.2544, -0.0000, -12.8954, -0.0000, -12.2310] + ) # check eda2 info - assert_equal(mol.extra['eda2']['e_elec'], -65.9887 * kjmol) - assert_equal(mol.extra['eda2']['e_kep_pauli'], 78.5700 * kjmol) - assert_equal(mol.extra['eda2']['e_disp_free_pauli'], -14.2495 * kjmol) - assert_equal(mol.extra['eda2']['e_disp'], -7.7384 * kjmol) - assert_equal(mol.extra['eda2']['e_cls_elec'], -35.1257 * kjmol) - assert_equal(mol.extra['eda2']['e_cls_pauli'], 25.7192 * kjmol) - assert_equal(mol.extra['eda2']['e_mod_pauli'], 33.4576 * kjmol) - assert_equal(mol.extra['eda2']['preparation'], 0.0000) - assert_equal(mol.extra['eda2']['frozen'], -1.6681 * kjmol) - assert_equal(mol.extra['eda2']['pauli'], 64.3205 * kjmol) - assert_equal(mol.extra['eda2']['dispersion'], -7.7384 * kjmol) - assert_equal(mol.extra['eda2']['polarization'], -4.6371 * kjmol) - assert_equal(mol.extra['eda2']['charge transfer'], -7.0689 * kjmol) - assert_equal(mol.extra['eda2']['total'], -21.1126 * kjmol) + assert_equal(mol.extra["eda2"]["e_elec"], -65.9887 * kjmol) + assert_equal(mol.extra["eda2"]["e_kep_pauli"], 78.5700 * kjmol) + assert_equal(mol.extra["eda2"]["e_disp_free_pauli"], -14.2495 * kjmol) + assert_equal(mol.extra["eda2"]["e_disp"], -7.7384 * kjmol) + assert_equal(mol.extra["eda2"]["e_cls_elec"], -35.1257 * kjmol) + assert_equal(mol.extra["eda2"]["e_cls_pauli"], 25.7192 * kjmol) + assert_equal(mol.extra["eda2"]["e_mod_pauli"], 33.4576 * kjmol) + assert_equal(mol.extra["eda2"]["preparation"], 0.0000) + assert_equal(mol.extra["eda2"]["frozen"], -1.6681 * kjmol) + assert_equal(mol.extra["eda2"]["pauli"], 64.3205 * kjmol) + assert_equal(mol.extra["eda2"]["dispersion"], -7.7384 * kjmol) + assert_equal(mol.extra["eda2"]["polarization"], -4.6371 * kjmol) + assert_equal(mol.extra["eda2"]["charge transfer"], -7.0689 * kjmol) + assert_equal(mol.extra["eda2"]["total"], -21.1126 * kjmol) # check fragments coords1 = [[-1.551007, -0.11452, 0.0], [-1.934259, 0.762503, 0.0], [-0.599677, 0.040712, 0.0]] - coords2 = [[1.350625, 0.111469, 0.0], [1.680398, -0.373741, -0.758561], - [1.680398, -0.373741, 0.758561]] - assert_equal(len(mol.extra['frags']), 2) + coords2 = [ + [1.350625, 0.111469, 0.0], + [1.680398, -0.373741, -0.758561], + [1.680398, -0.373741, 0.758561], + ] + assert_equal(len(mol.extra["frags"]), 2) - assert_equal(mol.extra['frags'][0]['atnums'], [8, 1, 1]) - assert_equal(mol.extra['frags'][0]['alpha_elec'], 5) - assert_equal(mol.extra['frags'][0]['beta_elec'], 5) - assert_equal(mol.extra['frags'][0]['nbasis'], 58) - assert_allclose(mol.extra['frags'][0]['atcoords'], np.array(coords1) * angstrom) - assert_allclose(mol.extra['frags'][0]['nuclear_repulsion_energy'], 9.16383018) - assert_allclose(mol.extra['frags'][0]['energy'], -76.4345994141) + assert_equal(mol.extra["frags"][0]["atnums"], [8, 1, 1]) + assert_equal(mol.extra["frags"][0]["alpha_elec"], 5) + assert_equal(mol.extra["frags"][0]["beta_elec"], 5) + assert_equal(mol.extra["frags"][0]["nbasis"], 58) + assert_allclose(mol.extra["frags"][0]["atcoords"], np.array(coords1) * angstrom) + assert_allclose(mol.extra["frags"][0]["nuclear_repulsion_energy"], 9.16383018) + assert_allclose(mol.extra["frags"][0]["energy"], -76.4345994141) - assert_equal(mol.extra['frags'][1]['atnums'], [8, 1, 1]) - assert_equal(mol.extra['frags'][1]['alpha_elec'], 5) - assert_equal(mol.extra['frags'][1]['beta_elec'], 5) - assert_equal(mol.extra['frags'][1]['nbasis'], 58) - assert_allclose(mol.extra['frags'][1]['atcoords'], np.array(coords2) * angstrom) - assert_allclose(mol.extra['frags'][1]['nuclear_repulsion_energy'], 9.17803894) - assert_allclose(mol.extra['frags'][1]['energy'], -76.4346136883) + assert_equal(mol.extra["frags"][1]["atnums"], [8, 1, 1]) + assert_equal(mol.extra["frags"][1]["alpha_elec"], 5) + assert_equal(mol.extra["frags"][1]["beta_elec"], 5) + assert_equal(mol.extra["frags"][1]["nbasis"], 58) + assert_allclose(mol.extra["frags"][1]["atcoords"], np.array(coords2) * angstrom) + assert_allclose(mol.extra["frags"][1]["nuclear_repulsion_energy"], 9.17803894) + assert_allclose(mol.extra["frags"][1]["energy"], -76.4346136883) diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index a5571ad7..07429f9c 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -60,7 +60,7 @@ def test_sdf_formaterror(tmpdir): def check_example(mol): """Test some things on example file.""" - assert mol.title == '24978498' + assert mol.title == "24978498" assert mol.natom == 16 assert len(mol.bonds) == 15 assert_equal(mol.atnums, [16, 8, 8, 8, 8, 7, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1]) @@ -79,13 +79,13 @@ def check_load_dump_consistency(tmpdir, fn): """Check if dumping and loading an SDF file results in the same data.""" mol0 = load_one(str(fn)) # write sdf file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test.sdf') + fn_tmp = os.path.join(tmpdir, "test.sdf") dump_one(mol0, fn_tmp) mol1 = load_one(fn_tmp) # check two sdf files assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) assert_equal(mol0.bonds, mol1.bonds) @@ -104,7 +104,7 @@ def test_load_many(): mols = list(load_many(str(fn_sdf))) assert len(mols) == 2 check_example(mols[0]) - assert mols[1].title == '24978481' + assert mols[1].title == "24978481" assert_equal(mols[1].natom, 21) assert_allclose(mols[0].atcoords[0] / angstrom, [2.8660, -0.4400, 0.0000]) assert_allclose(mols[1].atcoords[1] / angstrom, [1.4030, 1.4030, 0.0000]) @@ -114,14 +114,14 @@ def test_load_dump_many_consistency(tmpdir): with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_sdf: mols0 = list(load_many(str(fn_sdf))) # write sdf file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test') - dump_many(mols0, fn_tmp, fmt='sdf') - mols1 = list(load_many(fn_tmp, fmt='sdf')) + fn_tmp = os.path.join(tmpdir, "test") + dump_many(mols0, fn_tmp, fmt="sdf") + mols1 = list(load_many(fn_tmp, fmt="sdf")) assert len(mols0) == len(mols1) for mol0, mol1 in zip(mols0, mols1): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) assert_equal(mol0.bonds, mol1.bonds) diff --git a/iodata/test/test_wfn.py b/iodata/test/test_wfn.py index c1f8464f..306c94c8 100644 --- a/iodata/test/test_wfn.py +++ b/iodata/test/test_wfn.py @@ -36,6 +36,7 @@ # TODO: removed density, kin, nucnuc checks + def helper_load_wfn_low(fn_wfn): """Load a testing Gaussian log file with iodata.formats.wfn.load_wfn_low.""" with as_file(files("iodata.test.data").joinpath(fn_wfn)) as fn: @@ -44,11 +45,11 @@ def helper_load_wfn_low(fn_wfn): def test_load_wfn_low_he_s(): - data = helper_load_wfn_low('he_s_orbital.wfn') + data = helper_load_wfn_low("he_s_orbital.wfn") # unpack data title, atnums, atcoords, centers, type_assignments = data[:5] exponents, mo_count, occ_num, mo_energy, coefficients, energy, virial, _ = data[5:] - assert title == 'He atom - decontracted 6-31G basis set' + assert title == "He atom - decontracted 6-31G basis set" assert_equal(atnums.shape, (1,)) assert_equal(atnums, [2]) assert_equal(atcoords.shape, (1, 3)) @@ -64,22 +65,20 @@ def test_load_wfn_low_he_s(): assert_equal(centers, [0, 0, 0, 0]) assert_equal(occ_num, [2.0]) assert_allclose(atcoords, np.array([[0.00, 0.00, 0.00]])) - assert_allclose(exponents, [0.3842163E+02, 0.5778030E+01, - 0.1241774E+01, 0.2979640E+00]) + assert_allclose(exponents, [0.3842163e02, 0.5778030e01, 0.1241774e01, 0.2979640e00]) assert_allclose(mo_energy, [-0.914127]) - expected = np.array([0.26139500E+00, 0.41084277E+00, - 0.39372947E+00, 0.14762025E+00]) + expected = np.array([0.26139500e00, 0.41084277e00, 0.39372947e00, 0.14762025e00]) assert_allclose(coefficients, expected.reshape(4, 1)) - assert_allclose(energy, -2.855160426155, atol=1.e-5) - assert_allclose(virial, 1.99994256, atol=1.e-6) + assert_allclose(energy, -2.855160426155, atol=1.0e-5) + assert_allclose(virial, 1.99994256, atol=1.0e-6) def test_load_wfn_low_h2o(): - data = helper_load_wfn_low('h2o_sto3g.wfn') + data = helper_load_wfn_low("h2o_sto3g.wfn") # unpack data title, atnums, atcoords, centers, type_assignments = data[:5] exponents, mo_count, occ_num, mo_energy, coefficients, energy, virial, _ = data[5:] - assert title == 'H2O Optimization' + assert title == "H2O Optimization" assert_equal(atnums.shape, (3,)) assert_equal(atcoords.shape, (3, 3)) assert_equal(centers.shape, (21,)) @@ -93,36 +92,37 @@ def test_load_wfn_low_h2o(): assert_equal(centers[:15], np.zeros(15, int)) assert_equal(centers[15:], np.array([1, 1, 1, 2, 2, 2])) assert_equal(type_assignments[:6], np.zeros(6)) - assert_equal(type_assignments[6:15], np.array( - [1, 1, 1, 2, 2, 2, 3, 3, 3])) + assert_equal(type_assignments[6:15], np.array([1, 1, 1, 2, 2, 2, 3, 3, 3])) assert_equal(type_assignments[15:], np.zeros(6)) assert_equal(mo_count, [1, 2, 3, 4, 5]) assert_equal(np.sum(occ_num), 10.0) assert_equal(occ_num, [2.0, 2.0, 2.0, 2.0, 2.0]) - assert_allclose(atcoords, np.array([ - [-4.44734101, 3.39697999, 0.00000000], - [-2.58401495, 3.55136194, 0.00000000], - [-4.92380519, 5.20496220, 0.00000000]])) - assert_allclose(exponents[:3], - [0.1307093E+03, 0.2380887E+02, 0.6443608E+01]) - assert_allclose(exponents[5:8], - [0.3803890E+00, 0.5033151E+01, 0.1169596E+01]) - assert_allclose(exponents[13:16], - [0.1169596E+01, 0.3803890E+00, 0.3425251E+01]) - assert_allclose(exponents[-1], 0.1688554E+00) + assert_allclose( + atcoords, + np.array( + [ + [-4.44734101, 3.39697999, 0.00000000], + [-2.58401495, 3.55136194, 0.00000000], + [-4.92380519, 5.20496220, 0.00000000], + ] + ), + ) + assert_allclose(exponents[:3], [0.1307093e03, 0.2380887e02, 0.6443608e01]) + assert_allclose(exponents[5:8], [0.3803890e00, 0.5033151e01, 0.1169596e01]) + assert_allclose(exponents[13:16], [0.1169596e01, 0.3803890e00, 0.3425251e01]) + assert_allclose(exponents[-1], 0.1688554e00) assert_allclose(mo_energy, np.sort(mo_energy)) assert_allclose(mo_energy[:3], [-20.251576, -1.257549, -0.593857]) assert_allclose(mo_energy[3:], [-0.459729, -0.392617]) - expected = [0.42273517E+01, -0.99395832E+00, - 0.19183487E-11, 0.44235381E+00, -0.57941668E-14] + expected = [0.42273517e01, -0.99395832e00, 0.19183487e-11, 0.44235381e00, -0.57941668e-14] assert_allclose(coefficients[0], expected) - assert_allclose(coefficients[6, 2], 0.83831599E+00) - assert_allclose(coefficients[10, 3], 0.65034846E+00) - assert_allclose(coefficients[17, 1], 0.12988055E-01) - assert_allclose(coefficients[-1, 0], -0.46610858E-03) - assert_allclose(coefficients[-1, -1], -0.33277355E-15) - assert_allclose(energy, -74.965901217080, atol=1.e-6) - assert_allclose(virial, 2.00600239, atol=1.e-6) + assert_allclose(coefficients[6, 2], 0.83831599e00) + assert_allclose(coefficients[10, 3], 0.65034846e00) + assert_allclose(coefficients[17, 1], 0.12988055e-01) + assert_allclose(coefficients[-1, 0], -0.46610858e-03) + assert_allclose(coefficients[-1, -1], -0.33277355e-15) + assert_allclose(energy, -74.965901217080, atol=1.0e-6) + assert_allclose(virial, 2.00600239, atol=1.0e-6) def check_wfn(fn_wfn, nbasis, energy, charges_mulliken): @@ -134,109 +134,103 @@ def check_wfn(fn_wfn, nbasis, energy, charges_mulliken): assert mol.obasis.nbasis == nbasis # check orthonormal mo olp = compute_overlap(mol.obasis, mol.atcoords) - check_orthonormal(mol.mo.coeffsa, olp, 1.e-5) - if mol.mo.kind == 'unrestricted': - check_orthonormal(mol.mo.coeffsb, olp, 1.e-5) + check_orthonormal(mol.mo.coeffsa, olp, 1.0e-5) + if mol.mo.kind == "unrestricted": + check_orthonormal(mol.mo.coeffsb, olp, 1.0e-5) # check energy & atomic charges if energy is not None: - assert_allclose(mol.energy, energy, rtol=0., atol=1.e-5) + assert_allclose(mol.energy, energy, rtol=0.0, atol=1.0e-5) if charges_mulliken is not None: charges = compute_mulliken_charges(mol) - assert_allclose(charges_mulliken, charges, rtol=0., atol=1.e-5) + assert_allclose(charges_mulliken, charges, rtol=0.0, atol=1.0e-5) return mol def test_load_wfn_h2o_sto3g_decontracted(): charges = np.array([-0.546656, 0.273328, 0.273328]) - check_wfn('h2o_sto3g_decontracted.wfn', 21, -75.162231674351, charges) + check_wfn("h2o_sto3g_decontracted.wfn", 21, -75.162231674351, charges) def test_load_wfn_h2_ccpvqz_virtual(): - mol = check_wfn('h2_ccpvqz.wfn', 74, -1.133504568400, np.array([0.0, 0.0])) + mol = check_wfn("h2_ccpvqz.wfn", 74, -1.133504568400, np.array([0.0, 0.0])) expect = [82.64000, 12.41000, 2.824000, 0.7977000, 0.2581000] - assert_allclose([shell.exponents[0] for shell in mol.obasis.shells[:5]], - expect, rtol=0., atol=1.e-6) + assert_allclose( + [shell.exponents[0] for shell in mol.obasis.shells[:5]], expect, rtol=0.0, atol=1.0e-6 + ) expect = [-0.596838, 0.144565, 0.209605, 0.460401, 0.460401] - assert_allclose(mol.mo.energies[:5], expect, rtol=0., atol=1.e-6) + assert_allclose(mol.mo.energies[:5], expect, rtol=0.0, atol=1.0e-6) expect = [12.859067, 13.017471, 16.405834, 25.824716, 26.100443] - assert_allclose(mol.mo.energies[-5:], expect, rtol=0., atol=1.e-6) + assert_allclose(mol.mo.energies[-5:], expect, rtol=0.0, atol=1.0e-6) assert_equal(mol.mo.occs[:5], [2, 0, 0, 0, 0]) assert_equal(mol.mo.occs.sum(), 2) def test_load_wfn_h2o_sto3g(): - check_wfn('h2o_sto3g.wfn', 21, -74.96590121708, - np.array([-0.330532, 0.165266, 0.165266])) + check_wfn("h2o_sto3g.wfn", 21, -74.96590121708, np.array([-0.330532, 0.165266, 0.165266])) def test_load_wfn_li_sp_virtual(): - mol = check_wfn('li_sp_virtual.wfn', 8, -3.712905542719, np.array([0.0])) + mol = check_wfn("li_sp_virtual.wfn", 8, -3.712905542719, np.array([0.0])) assert_equal(mol.mo.occs[:8], [1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) assert_equal(mol.mo.occs[8:], [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]) - expect = [-0.087492, -0.080310, 0.158784, 0.158784, - 1.078773, 1.090891, 1.090891, 49.643670] - assert_allclose(mol.mo.energies[:8], expect, rtol=0., atol=1.e-6) - expect = [-0.079905, 0.176681, 0.176681, 0.212494, - 1.096631, 1.096631, 1.122821, 49.643827] - assert_allclose(mol.mo.energies[8:], expect, rtol=0., atol=1.e-6) + expect = [-0.087492, -0.080310, 0.158784, 0.158784, 1.078773, 1.090891, 1.090891, 49.643670] + assert_allclose(mol.mo.energies[:8], expect, rtol=0.0, atol=1.0e-6) + expect = [-0.079905, 0.176681, 0.176681, 0.212494, 1.096631, 1.096631, 1.122821, 49.643827] + assert_allclose(mol.mo.energies[8:], expect, rtol=0.0, atol=1.0e-6) assert_equal(mol.mo.coeffs.shape, (8, 16)) def test_load_wfn_li_sp(): - mol = check_wfn('li_sp_orbital.wfn', 8, -3.712905542719, None) - assert mol.title == 'Li atom - using s & p orbitals' + mol = check_wfn("li_sp_orbital.wfn", 8, -3.712905542719, None) + assert mol.title == "Li atom - using s & p orbitals" assert_equal([mol.mo.norba, mol.mo.norbb], [2, 1]) - assert_allclose(mol.mo.energies, - [-0.087492, -0.080310, -0.079905], rtol=0., atol=1.e-6) + assert_allclose(mol.mo.energies, [-0.087492, -0.080310, -0.079905], rtol=0.0, atol=1.0e-6) def test_load_wfn_o2(): - mol = check_wfn('o2_uhf.wfn', 72, -149.664140769678, np.array([0.0, 0.0])) + mol = check_wfn("o2_uhf.wfn", 72, -149.664140769678, np.array([0.0, 0.0])) assert_equal([mol.mo.norba, mol.mo.norbb], [9, 7]) def test_load_wfn_o2_virtual(): - mol = check_wfn('o2_uhf_virtual.wfn', 72, - -149.664140769678, np.array([0.0, 0.0])) + mol = check_wfn("o2_uhf_virtual.wfn", 72, -149.664140769678, np.array([0.0, 0.0])) # check MO occupation assert_equal(mol.mo.occs.shape, (88,)) - assert_allclose(mol.mo.occsa, [1.] * 9 + [0.] * 35) - assert_allclose(mol.mo.occsb, [1.] * 7 + [0.] * 37) + assert_allclose(mol.mo.occsa, [1.0] * 9 + [0.0] * 35) + assert_allclose(mol.mo.occsb, [1.0] * 7 + [0.0] * 37) # check MO energies assert_equal(mol.mo.energies.shape, (88,)) mo_energies_a = mol.mo.energiesa - assert_allclose(mo_energies_a[0], -20.752000, rtol=0, atol=1.e-6) - assert_allclose(mo_energies_a[10], 0.179578, rtol=0, atol=1.e-6) - assert_allclose(mo_energies_a[-1], 51.503193, rtol=0, atol=1.e-6) + assert_allclose(mo_energies_a[0], -20.752000, rtol=0, atol=1.0e-6) + assert_allclose(mo_energies_a[10], 0.179578, rtol=0, atol=1.0e-6) + assert_allclose(mo_energies_a[-1], 51.503193, rtol=0, atol=1.0e-6) mo_energies_b = mol.mo.energiesb - assert_allclose(mo_energies_b[0], -20.697027, rtol=0, atol=1.e-6) - assert_allclose(mo_energies_b[15], 0.322590, rtol=0, atol=1.e-6) - assert_allclose(mo_energies_b[-1], 51.535258, rtol=0, atol=1.e-6) + assert_allclose(mo_energies_b[0], -20.697027, rtol=0, atol=1.0e-6) + assert_allclose(mo_energies_b[15], 0.322590, rtol=0, atol=1.0e-6) + assert_allclose(mo_energies_b[-1], 51.535258, rtol=0, atol=1.0e-6) # check MO coefficients assert_equal(mol.mo.coeffs.shape, (72, 88)) def test_load_wfn_lif_fci(): - mol = check_wfn('lif_fci.wfn', 44, -107.0575700853, - np.array([-0.645282, 0.645282])) + mol = check_wfn("lif_fci.wfn", 44, -107.0575700853, np.array([-0.645282, 0.645282])) assert_equal(mol.mo.occs.shape, (18,)) - assert_allclose(mol.mo.occs.sum(), 12.0, rtol=0., atol=1.e-6) - assert_allclose(mol.mo.occs[0], 2.0, rtol=0., atol=1.e-6) - assert_allclose(mol.mo.occs[10], 0.00128021, rtol=0., atol=1.e-6) - assert_allclose(mol.mo.occs[-1], 0.00000054, rtol=0., atol=1.e-6) + assert_allclose(mol.mo.occs.sum(), 12.0, rtol=0.0, atol=1.0e-6) + assert_allclose(mol.mo.occs[0], 2.0, rtol=0.0, atol=1.0e-6) + assert_allclose(mol.mo.occs[10], 0.00128021, rtol=0.0, atol=1.0e-6) + assert_allclose(mol.mo.occs[-1], 0.00000054, rtol=0.0, atol=1.0e-6) assert_equal(mol.mo.energies.shape, (18,)) - assert_allclose(mol.mo.energies[0], -26.09321253, rtol=0., atol=1.e-7) - assert_allclose(mol.mo.energies[15], 1.70096290, rtol=0., atol=1.e-7) - assert_allclose(mol.mo.energies[-1], 2.17434072, rtol=0., atol=1.e-7) + assert_allclose(mol.mo.energies[0], -26.09321253, rtol=0.0, atol=1.0e-7) + assert_allclose(mol.mo.energies[15], 1.70096290, rtol=0.0, atol=1.0e-7) + assert_allclose(mol.mo.energies[-1], 2.17434072, rtol=0.0, atol=1.0e-7) assert_equal(mol.mo.coeffs.shape, (44, 18)) def test_load_wfn_lih_cation_fci(): - mol = check_wfn('lih_cation_fci.wfn', 26, -7.7214366383, - np.array([0.913206, 0.086794])) + mol = check_wfn("lih_cation_fci.wfn", 26, -7.7214366383, np.array([0.913206, 0.086794])) assert_equal(mol.atnums, [3, 1]) assert_equal(mol.mo.occs.shape, (11,)) - assert_allclose(mol.mo.occs.sum(), 3., rtol=0., atol=1.e-6) + assert_allclose(mol.mo.occs.sum(), 3.0, rtol=0.0, atol=1.0e-6) # assert abs(mol.mo.occsa.sum() - 1.5) < 1.e-6 @@ -244,7 +238,7 @@ def test_load_one_lih_cation_cisd(): with as_file(files("iodata.test.data").joinpath("lih_cation_cisd.wfn")) as file_wfn: mol = load_one(str(file_wfn)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 11 assert mol.mo.norbb == 11 assert mol.mo.norb == 22 @@ -260,7 +254,7 @@ def test_load_one_lih_cation_uhf(): with as_file(files("iodata.test.data").joinpath("lih_cation_uhf.wfn")) as file_wfn: mol = load_one(str(file_wfn)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 2 assert mol.mo.norbb == 1 assert mol.mo.norb == 3 @@ -276,7 +270,7 @@ def test_load_one_lih_cation_rohf(): with as_file(files("iodata.test.data").joinpath("lih_cation_rohf.wfn")) as file_wfn: mol = load_one(str(file_wfn)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert mol.mo.norba == 2 assert mol.mo.norbb == 2 assert mol.mo.norb == 2 @@ -293,7 +287,7 @@ def test_load_one_cah110_hf_sto3g_g09(): with as_file(files("iodata.test.data").joinpath("cah110_hf_sto3g_g09.wfn")) as file_wfn: mol = load_one(str(file_wfn)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 123 assert mol.mo.norbb == 123 assert mol.mo.norb == 246 @@ -309,7 +303,7 @@ def test_load_one_cah110_hf_sto3g_g09(): check_orthonormal(mol.mo.coeffsb, olp, 1e-5) -def check_load_dump_consistency(fn, tmpdir, fmt_from='wfn', fmt_to='wfn', atol=1.0e-6): +def check_load_dump_consistency(fn, tmpdir, fmt_from="wfn", fmt_to="wfn", atol=1.0e-6): """Check if data is preserved after dumping and loading a WFN file. Parameters @@ -326,7 +320,7 @@ def check_load_dump_consistency(fn, tmpdir, fmt_from='wfn', fmt_to='wfn', atol=1 """ with as_file(files("iodata.test.data").joinpath(fn)) as file_name: mol1 = load_one(str(file_name), fmt=fmt_from) - fn_tmp = os.path.join(tmpdir, 'foo.bar') + fn_tmp = os.path.join(tmpdir, "foo.bar") dump_one(mol1, fn_tmp, fmt=fmt_to) mol2 = load_one(fn_tmp, fmt=fmt_to) # compare Mulliken charges @@ -338,59 +332,59 @@ def check_load_dump_consistency(fn, tmpdir, fmt_from='wfn', fmt_to='wfn', atol=1 def test_load_dump_consistency_lih_cation_cisd(tmpdir): - check_load_dump_consistency('lih_cation_cisd.wfn', tmpdir) + check_load_dump_consistency("lih_cation_cisd.wfn", tmpdir) def test_load_dump_consistency_lih_cation_uhf(tmpdir): - check_load_dump_consistency('lih_cation_uhf.wfn', tmpdir) + check_load_dump_consistency("lih_cation_uhf.wfn", tmpdir) def test_load_dump_consistency_lih_cation_rohf(tmpdir): - check_load_dump_consistency('lih_cation_rohf.wfn', tmpdir) + check_load_dump_consistency("lih_cation_rohf.wfn", tmpdir) def test_load_dump_consistency_h2o(tmpdir): - check_load_dump_consistency('h2o_sto3g.wfn', tmpdir) - check_load_dump_consistency('h2o_sto3g_decontracted.wfn', tmpdir) + check_load_dump_consistency("h2o_sto3g.wfn", tmpdir) + check_load_dump_consistency("h2o_sto3g_decontracted.wfn", tmpdir) def test_load_dump_consistency_lif(tmpdir): - check_load_dump_consistency('lif_fci.wfn', tmpdir, atol=1.0e-6) + check_load_dump_consistency("lif_fci.wfn", tmpdir, atol=1.0e-6) def test_load_dump_consistency_cah110(tmpdir): - check_load_dump_consistency('cah110_hf_sto3g_g09.wfn', tmpdir) + check_load_dump_consistency("cah110_hf_sto3g_g09.wfn", tmpdir) def test_load_dump_consistency_li(tmpdir): - check_load_dump_consistency('li_sp_orbital.wfn', tmpdir) - check_load_dump_consistency('li_sp_virtual.wfn', tmpdir) + check_load_dump_consistency("li_sp_orbital.wfn", tmpdir) + check_load_dump_consistency("li_sp_virtual.wfn", tmpdir) def test_load_dump_consistency_he(tmpdir): - check_load_dump_consistency('he_s_orbital.wfn', tmpdir) - check_load_dump_consistency('he_s_virtual.wfn', tmpdir) - check_load_dump_consistency('he_p_orbital.wfn', tmpdir) - check_load_dump_consistency('he_d_orbital.wfn', tmpdir) - check_load_dump_consistency('he_sp_orbital.wfn', tmpdir) - check_load_dump_consistency('he_spd_orbital.wfn', tmpdir) - check_load_dump_consistency('he_spdf_orbital.wfn', tmpdir) - check_load_dump_consistency('he_spdfgh_orbital.wfn', tmpdir) - check_load_dump_consistency('he_spdfgh_virtual.wfn', tmpdir) + check_load_dump_consistency("he_s_orbital.wfn", tmpdir) + check_load_dump_consistency("he_s_virtual.wfn", tmpdir) + check_load_dump_consistency("he_p_orbital.wfn", tmpdir) + check_load_dump_consistency("he_d_orbital.wfn", tmpdir) + check_load_dump_consistency("he_sp_orbital.wfn", tmpdir) + check_load_dump_consistency("he_spd_orbital.wfn", tmpdir) + check_load_dump_consistency("he_spdf_orbital.wfn", tmpdir) + check_load_dump_consistency("he_spdfgh_orbital.wfn", tmpdir) + check_load_dump_consistency("he_spdfgh_virtual.wfn", tmpdir) def test_load_dump_consistency_h2(tmpdir): - check_load_dump_consistency('h2_ccpvqz.wfn', tmpdir) + check_load_dump_consistency("h2_ccpvqz.wfn", tmpdir) def test_load_dump_consistency_o2(tmpdir): - check_load_dump_consistency('o2_uhf.wfn', tmpdir) - check_load_dump_consistency('o2_uhf_virtual.wfn', tmpdir) + check_load_dump_consistency("o2_uhf.wfn", tmpdir) + check_load_dump_consistency("o2_uhf_virtual.wfn", tmpdir) def test_load_dump_consistency_from_fchk_h2o(tmpdir): - check_load_dump_consistency('h2o_sto3g.fchk', tmpdir, fmt_from='fchk', fmt_to='wfn') + check_load_dump_consistency("h2o_sto3g.fchk", tmpdir, fmt_from="fchk", fmt_to="wfn") def test_load_dump_consistency_from_molden_nh3(tmpdir): - check_load_dump_consistency('nh3_molden_cart.molden', tmpdir, fmt_from='molden', fmt_to='wfn') + check_load_dump_consistency("nh3_molden_cart.molden", tmpdir, fmt_from="molden", fmt_to="wfn") diff --git a/iodata/test/test_wfx.py b/iodata/test/test_wfx.py index 8ce05690..53b19f4e 100644 --- a/iodata/test/test_wfx.py +++ b/iodata/test/test_wfx.py @@ -28,8 +28,13 @@ from ..overlap import compute_overlap from ..utils import LineIterator -from .common import (check_orthonormal, truncated_file, compare_mols, - compute_mulliken_charges, load_one_warning) +from .common import ( + check_orthonormal, + truncated_file, + compare_mols, + compute_mulliken_charges, + load_one_warning, +) try: from importlib_resources import as_file, files @@ -55,10 +60,10 @@ def check_load_dump_consistency(fn, tmpdir): The temporary directory to dump and load the file. """ with as_file(files("iodata.test.data").joinpath(fn)) as file_name: - mol1 = load_one(str(file_name), fmt='wfx') - fn_tmp = os.path.join(tmpdir, 'foo.bar') - dump_one(mol1, fn_tmp, fmt='wfx') - mol2 = load_one(fn_tmp, fmt='wfx') + mol1 = load_one(str(file_name), fmt="wfx") + fn_tmp = os.path.join(tmpdir, "foo.bar") + dump_one(mol1, fn_tmp, fmt="wfx") + mol2 = load_one(fn_tmp, fmt="wfx") compare_mols(mol1, mol2) # compare Mulliken charges charges1 = compute_mulliken_charges(mol1) @@ -67,28 +72,28 @@ def check_load_dump_consistency(fn, tmpdir): def test_load_dump_consistency_water(tmpdir): - check_load_dump_consistency('water_sto3g_hf.wfx', tmpdir) + check_load_dump_consistency("water_sto3g_hf.wfx", tmpdir) def test_load_dump_consistency_h2(tmpdir): - check_load_dump_consistency('h2_ub3lyp_ccpvtz.wfx', tmpdir) + check_load_dump_consistency("h2_ub3lyp_ccpvtz.wfx", tmpdir) def test_load_dump_consistency_lih_cation_cisd(tmpdir): - check_load_dump_consistency('lih_cation_cisd.wfx', tmpdir) + check_load_dump_consistency("lih_cation_cisd.wfx", tmpdir) def test_load_dump_consistency_lih_cation_uhf(tmpdir): - check_load_dump_consistency('lih_cation_uhf.wfx', tmpdir) + check_load_dump_consistency("lih_cation_uhf.wfx", tmpdir) def test_load_dump_consistency_lih_cation_rohf(tmpdir): - check_load_dump_consistency('lih_cation_rohf.wfx', tmpdir) + check_load_dump_consistency("lih_cation_rohf.wfx", tmpdir) def compare_mulliken_charges( - fname: str, tmpdir: str, rtol: float = 1.0e-7, atol: float = 0.0, - match: str = None): + fname: str, tmpdir: str, rtol: float = 1.0e-7, atol: float = 0.0, match: str = None +): """Check if charges are computed correctly after dumping and loading WFX file format. Parameters @@ -119,81 +124,82 @@ def compare_mulliken_charges( def test_dump_one_from_fchk_h2o(tmpdir): - compare_mulliken_charges('h2o_sto3g.fchk', tmpdir) - compare_mulliken_charges('water_hfs_321g.fchk', tmpdir) - compare_mulliken_charges('water_sto3g_hf_g03.fchk', tmpdir) + compare_mulliken_charges("h2o_sto3g.fchk", tmpdir) + compare_mulliken_charges("water_hfs_321g.fchk", tmpdir) + compare_mulliken_charges("water_sto3g_hf_g03.fchk", tmpdir) def test_dump_one_from_fchk_ch3_restricted(tmpdir): - compare_mulliken_charges('ch3_hf_sto3g.fchk', tmpdir) + compare_mulliken_charges("ch3_hf_sto3g.fchk", tmpdir) def test_dump_one_from_fchk_ch3_unrestricted(tmpdir): - compare_mulliken_charges('ch3_rohf_sto3g_g03.fchk', tmpdir) + compare_mulliken_charges("ch3_rohf_sto3g_g03.fchk", tmpdir) def test_dump_one_from_wfn_h2o(tmpdir): - compare_mulliken_charges('h2o_sto3g.wfn', tmpdir) - compare_mulliken_charges('h2o_sto3g_decontracted.wfn', tmpdir) + compare_mulliken_charges("h2o_sto3g.wfn", tmpdir) + compare_mulliken_charges("h2o_sto3g_decontracted.wfn", tmpdir) def test_dump_one_from_wfn_o2(tmpdir): - compare_mulliken_charges('o2_uhf.wfn', tmpdir) - compare_mulliken_charges('o2_uhf_virtual.wfn', tmpdir) + compare_mulliken_charges("o2_uhf.wfn", tmpdir) + compare_mulliken_charges("o2_uhf_virtual.wfn", tmpdir) def test_dump_one_from_wfn_atom(tmpdir): # Li atom - compare_mulliken_charges('li_sp_orbital.wfn', tmpdir) - compare_mulliken_charges('li_sp_virtual.wfn', tmpdir) + compare_mulliken_charges("li_sp_orbital.wfn", tmpdir) + compare_mulliken_charges("li_sp_virtual.wfn", tmpdir) # He atom - compare_mulliken_charges('he_s_orbital.wfn', tmpdir) - compare_mulliken_charges('he_s_virtual.wfn', tmpdir) - compare_mulliken_charges('he_p_orbital.wfn', tmpdir) - compare_mulliken_charges('he_d_orbital.wfn', tmpdir) - compare_mulliken_charges('he_sp_orbital.wfn', tmpdir) - compare_mulliken_charges('he_spd_orbital.wfn', tmpdir) - compare_mulliken_charges('he_spdf_orbital.wfn', tmpdir) - compare_mulliken_charges('he_spdfgh_orbital.wfn', tmpdir) - compare_mulliken_charges('he_spdfgh_virtual.wfn', tmpdir) + compare_mulliken_charges("he_s_orbital.wfn", tmpdir) + compare_mulliken_charges("he_s_virtual.wfn", tmpdir) + compare_mulliken_charges("he_p_orbital.wfn", tmpdir) + compare_mulliken_charges("he_d_orbital.wfn", tmpdir) + compare_mulliken_charges("he_sp_orbital.wfn", tmpdir) + compare_mulliken_charges("he_spd_orbital.wfn", tmpdir) + compare_mulliken_charges("he_spdf_orbital.wfn", tmpdir) + compare_mulliken_charges("he_spdfgh_orbital.wfn", tmpdir) + compare_mulliken_charges("he_spdfgh_virtual.wfn", tmpdir) def test_dump_one_from_wfn_lih(tmpdir): - compare_mulliken_charges('lih_cation_uhf.wfn', tmpdir) - compare_mulliken_charges('lih_cation_rohf.wfn', tmpdir) - compare_mulliken_charges('lih_cation_cisd.wfn', tmpdir) - compare_mulliken_charges('lih_cation_fci.wfn', tmpdir) + compare_mulliken_charges("lih_cation_uhf.wfn", tmpdir) + compare_mulliken_charges("lih_cation_rohf.wfn", tmpdir) + compare_mulliken_charges("lih_cation_cisd.wfn", tmpdir) + compare_mulliken_charges("lih_cation_fci.wfn", tmpdir) def test_dump_one_from_wfn_lif(tmpdir): - compare_mulliken_charges('lif_fci.wfn', tmpdir) + compare_mulliken_charges("lif_fci.wfn", tmpdir) def test_dump_one_from_molden_h2o(tmpdir): - compare_mulliken_charges('h2o.molden.input', tmpdir, match="ORCA") + compare_mulliken_charges("h2o.molden.input", tmpdir, match="ORCA") def test_dump_one_from_molden_he2(tmpdir): - compare_mulliken_charges('he2_ghost_psi4_1.0.molden', tmpdir) + compare_mulliken_charges("he2_ghost_psi4_1.0.molden", tmpdir) def test_dump_one_from_molden_neon(tmpdir): compare_mulliken_charges( - 'neon_turbomole_def2-qzvp.molden', tmpdir, atol=1.0e-10, match="Turbomole") + "neon_turbomole_def2-qzvp.molden", tmpdir, atol=1.0e-10, match="Turbomole" + ) def test_dump_one_from_molden_nh3(tmpdir): - compare_mulliken_charges('nh3_molden_cart.molden', tmpdir) - compare_mulliken_charges('nh3_molpro2012.molden', tmpdir) - compare_mulliken_charges('nh3_turbomole.molden', tmpdir, match="Turbomole") + compare_mulliken_charges("nh3_molden_cart.molden", tmpdir) + compare_mulliken_charges("nh3_molpro2012.molden", tmpdir) + compare_mulliken_charges("nh3_turbomole.molden", tmpdir, match="Turbomole") def test_dump_one_from_mkl_methanol(tmpdir): - compare_mulliken_charges('ethanol.mkl', tmpdir, match="ORCA") + compare_mulliken_charges("ethanol.mkl", tmpdir, match="ORCA") def test_dump_one_from_mkl_h2(tmpdir): - compare_mulliken_charges('h2_sto3g.mkl', tmpdir, match="ORCA") + compare_mulliken_charges("h2_sto3g.mkl", tmpdir, match="ORCA") # add this test when pure to Cartesian basis set conversion is supported @@ -203,151 +209,394 @@ def test_dump_one_from_mkl_h2(tmpdir): def test_load_data_wfx_h2(): """Test load_data_wfx with h2_ub3lyp_ccpvtz.wfx.""" - data = helper_load_data_wfx('h2_ub3lyp_ccpvtz.wfx') + data = helper_load_data_wfx("h2_ub3lyp_ccpvtz.wfx") # check loaded data - assert data['title'] == 'h2 ub3lyp/cc-pvtz opt-stable-freq' - assert data['keywords'] == 'GTO' + assert data["title"] == "h2 ub3lyp/cc-pvtz opt-stable-freq" + assert data["keywords"] == "GTO" # assert model_name is None - assert data['num_atoms'] == 2 - assert data['num_primitives'] == 34 - assert data['num_occ_mo'] == 56 - assert data['num_perturbations'] == 0 - assert data['num_electrons'] == 2 - assert data['num_alpha_electron'] == 1 - assert data['num_beta_electron'] == 1 - assert data['charge'] == 0.0 - assert data['spin_multi'] == 1 - assert_allclose(data['energy'], -1.179998789924e+00) - assert_allclose(data['virial_ratio'], 2.036441983763e+00) - assert_allclose(data['nuc_viral'], 1.008787649881e-08) - assert_allclose(data['full_virial_ratio'], 2.036441992623e+00) - assert_equal(data['nuclear_names'], ['H1', 'H2']) - assert_equal(data['atnums'], np.array([1, 1])) - assert_equal(data['mo_spins'], np.array(['Alpha'] * 28 + ['Beta'] * 28).T) - coords = np.array([[0., 0., 0.7019452462164], [0., 0., -0.7019452462164]]) - assert_allclose(data['atcoords'], coords) - assert_allclose(data['centers'], np.array([ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2])) - assert_allclose(data['types'], np.array([ - 1, 1, 1, 1, 1, 2, 3, 4, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 1, 1, 1, 1, 1, 2, 3, 4, 2, 3, 4, 5, 6, 7, 8, 9, 10])) - assert_allclose(data['exponents'], np.array([ - 3.387000000000e+01, 5.095000000000e+00, 1.159000000000e+00, - 3.258000000000e-01, 1.027000000000e-01, 1.407000000000e+00, - 1.407000000000e+00, 1.407000000000e+00, 3.880000000000e-01, - 3.880000000000e-01, 3.880000000000e-01, 1.057000000000e+00, - 1.057000000000e+00, 1.057000000000e+00, 1.057000000000e+00, - 1.057000000000e+00, 1.057000000000e+00, 3.387000000000e+01, - 5.095000000000e+00, 1.159000000000e+00, 3.258000000000e-01, - 1.027000000000e-01, 1.407000000000e+00, 1.407000000000e+00, - 1.407000000000e+00, 3.880000000000e-01, 3.880000000000e-01, - 3.880000000000e-01, 1.057000000000e+00, 1.057000000000e+00, - 1.057000000000e+00, 1.057000000000e+00, 1.057000000000e+00, - 1.057000000000e+00])) - assert_allclose(data['mo_occs'], ([1] + [0] * 27) * 2) - assert_allclose(data['mo_energies'], np.array([ - -4.340830854172e-01, 5.810590098068e-02, 1.957476339319e-01, - 4.705943952631e-01, 5.116003517961e-01, 5.116003517961e-01, - 9.109680450208e-01, 9.372078887497e-01, 9.372078887497e-01, - 1.367198523024e+00, 2.035656924620e+00, 2.093459617091e+00, - 2.882582109554e+00, 2.882582109559e+00, 3.079758295551e+00, - 3.079758295551e+00, 3.356387932344e+00, 3.600856684661e+00, - 3.600856684661e+00, 3.793185027287e+00, 3.793185027400e+00, - 3.807665977092e+00, 3.807665977092e+00, 4.345665616275e+00, - 5.386560784523e+00, 5.386560784523e+00, 5.448122593462e+00, - 6.522366660004e+00, -4.340830854172e-01, 5.810590098068e-02, - 1.957476339319e-01, 4.705943952631e-01, 5.116003517961e-01, - 5.116003517961e-01, 9.109680450208e-01, 9.372078887497e-01, - 9.372078887497e-01, 1.367198523024e+00, 2.035656924620e+00, - 2.093459617091e+00, 2.882582109554e+00, 2.882582109559e+00, - 3.079758295551e+00, 3.079758295551e+00, 3.356387932344e+00, - 3.600856684661e+00, 3.600856684661e+00, 3.793185027287e+00, - 3.793185027400e+00, 3.807665977092e+00, 3.807665977092e+00, - 4.345665616275e+00, 5.386560784523e+00, 5.386560784523e+00, - 5.448122593462e+00, 6.522366660004e+00])) - assert_allclose(data['atgradient'], [ - [9.744384163503e-17, -2.088844408785e-16, -7.185657679987e-09], - [-9.744384163503e-17, 2.088844408785e-16, 7.185657679987e-09]]) - assert data['mo_coeffs'].shape == (34, 56) - assert_allclose(data['mo_coeffs'][:, 0], [ - 5.054717669172e-02, 9.116391481072e-02, 1.344211391235e-01, - 8.321037376208e-02, 1.854203733451e-02, -5.552096650015e-17, - 1.685043781907e-17, -2.493514848195e-02, 5.367769875676e-18, - -8.640401342563e-21, -4.805966923740e-03, -3.124765025063e-04, - -3.124765025063e-04, 6.249530050126e-04, 6.560467295881e-16, - -8.389003686496e-17, 1.457172009403e-16, 5.054717669172e-02, - 9.116391481072e-02, 1.344211391235e-01, 8.321037376215e-02, - 1.854203733451e-02, 1.377812848830e-16, -5.365229184139e-18, - 2.493514848197e-02, -2.522774106094e-17, 2.213188439119e-17, - 4.805966923784e-03, -3.124765025186e-04, -3.124765025186e-04, - 6.249530050373e-04, -6.548275062740e-16, 4.865003740982e-17, - -1.099855647247e-16]) - assert_allclose(data['mo_coeffs'][2, 9], 1.779549601504e-02) - assert_allclose(data['mo_coeffs'][19, 14], -1.027984391469e-15) - assert_allclose(data['mo_coeffs'][26, 36], -5.700424557682e-01) + assert data["num_atoms"] == 2 + assert data["num_primitives"] == 34 + assert data["num_occ_mo"] == 56 + assert data["num_perturbations"] == 0 + assert data["num_electrons"] == 2 + assert data["num_alpha_electron"] == 1 + assert data["num_beta_electron"] == 1 + assert data["charge"] == 0.0 + assert data["spin_multi"] == 1 + assert_allclose(data["energy"], -1.179998789924e00) + assert_allclose(data["virial_ratio"], 2.036441983763e00) + assert_allclose(data["nuc_viral"], 1.008787649881e-08) + assert_allclose(data["full_virial_ratio"], 2.036441992623e00) + assert_equal(data["nuclear_names"], ["H1", "H2"]) + assert_equal(data["atnums"], np.array([1, 1])) + assert_equal(data["mo_spins"], np.array(["Alpha"] * 28 + ["Beta"] * 28).T) + coords = np.array([[0.0, 0.0, 0.7019452462164], [0.0, 0.0, -0.7019452462164]]) + assert_allclose(data["atcoords"], coords) + assert_allclose( + data["centers"], + np.array( + [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + ] + ), + ) + assert_allclose( + data["types"], + np.array( + [ + 1, + 1, + 1, + 1, + 1, + 2, + 3, + 4, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 1, + 1, + 1, + 1, + 1, + 2, + 3, + 4, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + ] + ), + ) + assert_allclose( + data["exponents"], + np.array( + [ + 3.387000000000e01, + 5.095000000000e00, + 1.159000000000e00, + 3.258000000000e-01, + 1.027000000000e-01, + 1.407000000000e00, + 1.407000000000e00, + 1.407000000000e00, + 3.880000000000e-01, + 3.880000000000e-01, + 3.880000000000e-01, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 3.387000000000e01, + 5.095000000000e00, + 1.159000000000e00, + 3.258000000000e-01, + 1.027000000000e-01, + 1.407000000000e00, + 1.407000000000e00, + 1.407000000000e00, + 3.880000000000e-01, + 3.880000000000e-01, + 3.880000000000e-01, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + 1.057000000000e00, + ] + ), + ) + assert_allclose(data["mo_occs"], ([1] + [0] * 27) * 2) + assert_allclose( + data["mo_energies"], + np.array( + [ + -4.340830854172e-01, + 5.810590098068e-02, + 1.957476339319e-01, + 4.705943952631e-01, + 5.116003517961e-01, + 5.116003517961e-01, + 9.109680450208e-01, + 9.372078887497e-01, + 9.372078887497e-01, + 1.367198523024e00, + 2.035656924620e00, + 2.093459617091e00, + 2.882582109554e00, + 2.882582109559e00, + 3.079758295551e00, + 3.079758295551e00, + 3.356387932344e00, + 3.600856684661e00, + 3.600856684661e00, + 3.793185027287e00, + 3.793185027400e00, + 3.807665977092e00, + 3.807665977092e00, + 4.345665616275e00, + 5.386560784523e00, + 5.386560784523e00, + 5.448122593462e00, + 6.522366660004e00, + -4.340830854172e-01, + 5.810590098068e-02, + 1.957476339319e-01, + 4.705943952631e-01, + 5.116003517961e-01, + 5.116003517961e-01, + 9.109680450208e-01, + 9.372078887497e-01, + 9.372078887497e-01, + 1.367198523024e00, + 2.035656924620e00, + 2.093459617091e00, + 2.882582109554e00, + 2.882582109559e00, + 3.079758295551e00, + 3.079758295551e00, + 3.356387932344e00, + 3.600856684661e00, + 3.600856684661e00, + 3.793185027287e00, + 3.793185027400e00, + 3.807665977092e00, + 3.807665977092e00, + 4.345665616275e00, + 5.386560784523e00, + 5.386560784523e00, + 5.448122593462e00, + 6.522366660004e00, + ] + ), + ) + assert_allclose( + data["atgradient"], + [ + [9.744384163503e-17, -2.088844408785e-16, -7.185657679987e-09], + [-9.744384163503e-17, 2.088844408785e-16, 7.185657679987e-09], + ], + ) + assert data["mo_coeffs"].shape == (34, 56) + assert_allclose( + data["mo_coeffs"][:, 0], + [ + 5.054717669172e-02, + 9.116391481072e-02, + 1.344211391235e-01, + 8.321037376208e-02, + 1.854203733451e-02, + -5.552096650015e-17, + 1.685043781907e-17, + -2.493514848195e-02, + 5.367769875676e-18, + -8.640401342563e-21, + -4.805966923740e-03, + -3.124765025063e-04, + -3.124765025063e-04, + 6.249530050126e-04, + 6.560467295881e-16, + -8.389003686496e-17, + 1.457172009403e-16, + 5.054717669172e-02, + 9.116391481072e-02, + 1.344211391235e-01, + 8.321037376215e-02, + 1.854203733451e-02, + 1.377812848830e-16, + -5.365229184139e-18, + 2.493514848197e-02, + -2.522774106094e-17, + 2.213188439119e-17, + 4.805966923784e-03, + -3.124765025186e-04, + -3.124765025186e-04, + 6.249530050373e-04, + -6.548275062740e-16, + 4.865003740982e-17, + -1.099855647247e-16, + ], + ) + assert_allclose(data["mo_coeffs"][2, 9], 1.779549601504e-02) + assert_allclose(data["mo_coeffs"][19, 14], -1.027984391469e-15) + assert_allclose(data["mo_coeffs"][26, 36], -5.700424557682e-01) def test_load_data_wfx_water(): """Test load_data_wfx with water_sto3g_hf.wfx.""" - data = helper_load_data_wfx('water_sto3g_hf.wfx') + data = helper_load_data_wfx("water_sto3g_hf.wfx") # check loaded data - assert data['title'] == 'H2O HF/STO-3G//HF/STO-3G' - assert data['keywords'] == 'GTO' - assert data['model_name'] == 'Restricted HF' - assert data['num_atoms'] == 3 - assert data['num_primitives'] == 21 - assert data['num_occ_mo'] == 5 - assert data['num_perturbations'] == 0 - assert data['num_electrons'] == 10 - assert data['num_alpha_electron'] == 5 - assert data['num_beta_electron'] == 5 - assert data['charge'] == 0.0 + assert data["title"] == "H2O HF/STO-3G//HF/STO-3G" + assert data["keywords"] == "GTO" + assert data["model_name"] == "Restricted HF" + assert data["num_atoms"] == 3 + assert data["num_primitives"] == 21 + assert data["num_occ_mo"] == 5 + assert data["num_perturbations"] == 0 + assert data["num_electrons"] == 10 + assert data["num_alpha_electron"] == 5 + assert data["num_beta_electron"] == 5 + assert data["charge"] == 0.0 # assert_equal(num_spin_multi, np.array(None)) - assert_allclose(data['energy'], -7.49659011707870E+001) - assert_allclose(data['virial_ratio'], 2.00599838291596E+000) + assert_allclose(data["energy"], -7.49659011707870e001) + assert_allclose(data["virial_ratio"], 2.00599838291596e000) # assert_allclose(data['nuclear_virial'], np.array(None)) - assert_allclose(data['full_virial_ratio'], 2.00600662884992E+000) - assert_equal(data['nuclear_names'], ['O1', 'H2', 'H3']) - assert_equal(data['atnums'], np.array([8, 1, 1])) - assert_equal(data['mo_spins'], np.array(['Alpha and Beta'] * 5).T) - assert_allclose(data['atcoords'], [ - [0.00000000000000, 0.00000000000000, 2.40242907000000E-1], - [0.00000000000000, 1.43244242000000, -9.60971627000000E-1], - [-1.75417809000000e-16, -1.43244242000000, -9.60971627000000E-1]]) - assert_allclose(data['centers'], np.array([ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3])) - assert_allclose(data['types'], np.array( - [1, 1, 1, 1, 1, 1, 2, 3, 4, 2, 3, 4, 2, 3, 4, 1, 1, 1, 1, 1, 1])) - assert_allclose(data['exponents'], np.array([ - 1.30709321000000E+002, 2.38088661000000E+001, 6.44360831000000E+000, - 5.03315132000000E+000, 1.16959612000000E+000, 3.80388960000000E-001, - 5.03315132000000E+000, 5.03315132000000E+000, 5.03315132000000E+000, - 1.16959612000000E+000, 1.16959612000000E+000, 1.16959612000000E+000, - 3.80388960000000E-001, 3.80388960000000E-001, 3.80388960000000E-001, - 3.42525091000000E+000, 6.23913730000000E-001, 1.68855404000000E-001, - 3.42525091000000E+000, 6.23913730000000E-001, 1.68855404000000E-001])) - assert_allclose(data['mo_occs'], np.array([ - 2.00000000000000E+000, 2.00000000000000E+000, - 2.00000000000000E+000, 2.00000000000000E+000, - 2.00000000000000E+000])) - assert_allclose(data['mo_energies'], np.array([ - -2.02515479000000E+001, -1.25760928000000E+000, -5.93941119000000E-001, - -4.59728723000000E-001, -3.92618460000000E-001])) - assert_allclose(data['atgradient'][:2, :], [ - [6.09070231000000E-016, -5.55187875000000E-016, -2.29270172000000E-004], - [-2.46849911000000E-016, -1.18355659000000E-004, 1.14635086000000E-004]]) - assert data['mo_coeffs'].shape == (21, 5) - assert_allclose(data['mo_coeffs'][:, 0], [ - 4.22735025664585E+000, 4.08850914632625E+000, 1.27420971692421E+000, - -6.18883321546465E-003, 8.27806436882009E-003, 6.24757868903820E-003, - 0.00000000000000E+000, 0.00000000000000E+000, -6.97905144921135E-003, - 0.00000000000000E+000, 0.00000000000000E+000, -4.38861481239680E-003, - 0.00000000000000E+000, 0.00000000000000E+000, -6.95230322147800E-004, - -1.54680714141406E-003, -1.49600452906993E-003, -4.66239267760156E-004, - -1.54680714141406E-003, -1.49600452906993E-003, -4.66239267760156E-004 - ], rtol=0.0, atol=1.e-8) - assert_allclose(data['mo_coeffs'][1, 3], -4.27845789719456E-001) + assert_allclose(data["full_virial_ratio"], 2.00600662884992e000) + assert_equal(data["nuclear_names"], ["O1", "H2", "H3"]) + assert_equal(data["atnums"], np.array([8, 1, 1])) + assert_equal(data["mo_spins"], np.array(["Alpha and Beta"] * 5).T) + assert_allclose( + data["atcoords"], + [ + [0.00000000000000, 0.00000000000000, 2.40242907000000e-1], + [0.00000000000000, 1.43244242000000, -9.60971627000000e-1], + [-1.75417809000000e-16, -1.43244242000000, -9.60971627000000e-1], + ], + ) + assert_allclose( + data["centers"], np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3]) + ) + assert_allclose( + data["types"], np.array([1, 1, 1, 1, 1, 1, 2, 3, 4, 2, 3, 4, 2, 3, 4, 1, 1, 1, 1, 1, 1]) + ) + assert_allclose( + data["exponents"], + np.array( + [ + 1.30709321000000e002, + 2.38088661000000e001, + 6.44360831000000e000, + 5.03315132000000e000, + 1.16959612000000e000, + 3.80388960000000e-001, + 5.03315132000000e000, + 5.03315132000000e000, + 5.03315132000000e000, + 1.16959612000000e000, + 1.16959612000000e000, + 1.16959612000000e000, + 3.80388960000000e-001, + 3.80388960000000e-001, + 3.80388960000000e-001, + 3.42525091000000e000, + 6.23913730000000e-001, + 1.68855404000000e-001, + 3.42525091000000e000, + 6.23913730000000e-001, + 1.68855404000000e-001, + ] + ), + ) + assert_allclose( + data["mo_occs"], + np.array( + [ + 2.00000000000000e000, + 2.00000000000000e000, + 2.00000000000000e000, + 2.00000000000000e000, + 2.00000000000000e000, + ] + ), + ) + assert_allclose( + data["mo_energies"], + np.array( + [ + -2.02515479000000e001, + -1.25760928000000e000, + -5.93941119000000e-001, + -4.59728723000000e-001, + -3.92618460000000e-001, + ] + ), + ) + assert_allclose( + data["atgradient"][:2, :], + [ + [6.09070231000000e-016, -5.55187875000000e-016, -2.29270172000000e-004], + [-2.46849911000000e-016, -1.18355659000000e-004, 1.14635086000000e-004], + ], + ) + assert data["mo_coeffs"].shape == (21, 5) + assert_allclose( + data["mo_coeffs"][:, 0], + [ + 4.22735025664585e000, + 4.08850914632625e000, + 1.27420971692421e000, + -6.18883321546465e-003, + 8.27806436882009e-003, + 6.24757868903820e-003, + 0.00000000000000e000, + 0.00000000000000e000, + -6.97905144921135e-003, + 0.00000000000000e000, + 0.00000000000000e000, + -4.38861481239680e-003, + 0.00000000000000e000, + 0.00000000000000e000, + -6.95230322147800e-004, + -1.54680714141406e-003, + -1.49600452906993e-003, + -4.66239267760156e-004, + -1.54680714141406e-003, + -1.49600452906993e-003, + -4.66239267760156e-004, + ], + rtol=0.0, + atol=1.0e-8, + ) + assert_allclose(data["mo_coeffs"][1, 3], -4.27845789719456e-001) def test_parse_wfx_missing_tag_h2o(): @@ -365,7 +614,8 @@ def test_load_data_wfx_h2o_error(): with pytest.raises(IOError) as error: load_one(str(fn_wfx)) assert str(error.value).endswith( - "Expecting line </Number of Nuclei> but got </Number of Primitives>.") + "Expecting line </Number of Nuclei> but got </Number of Primitives>." + ) def test_load_truncated_h2o(tmpdir): @@ -375,91 +625,128 @@ def test_load_truncated_h2o(tmpdir): with pytest.raises(IOError) as error: load_one(str(fn_truncated)) assert str(error.value).endswith( - "Section <Full Virial Ratio, -(V - W)/T> is not closed at end of file.") + "Section <Full Virial Ratio, -(V - W)/T> is not closed at end of file." + ) def test_load_one_h2o(): """Test load_one with h2o sto-3g WFX input.""" with as_file(files("iodata.test.data").joinpath("water_sto3g_hf.wfx")) as file_wfx: mol = load_one(str(file_wfx)) - assert_allclose(mol.atcoords, - np.array([[0.00000000e+00, 0.00000000e+00, 2.40242907e-01], - [0.00000000e+00, 1.43244242e+00, -9.60971627e-01], - [-1.75417809e-16, -1.43244242e+00, -9.60971627e-01]]), - rtol=0., atol=1.e-6) - assert_allclose(mol.atgradient, - np.array([[6.09070231e-16, -5.55187875e-16, -2.29270172e-04], - [-2.46849911e-16, -1.18355659e-04, 1.14635086e-04], - [-3.62220320e-16, 1.18355659e-04, 1.14635086e-04]]), - rtol=0, atol=1.e-6) + assert_allclose( + mol.atcoords, + np.array( + [ + [0.00000000e00, 0.00000000e00, 2.40242907e-01], + [0.00000000e00, 1.43244242e00, -9.60971627e-01], + [-1.75417809e-16, -1.43244242e00, -9.60971627e-01], + ] + ), + rtol=0.0, + atol=1.0e-6, + ) + assert_allclose( + mol.atgradient, + np.array( + [ + [6.09070231e-16, -5.55187875e-16, -2.29270172e-04], + [-2.46849911e-16, -1.18355659e-04, 1.14635086e-04], + [-3.62220320e-16, 1.18355659e-04, 1.14635086e-04], + ] + ), + rtol=0, + atol=1.0e-6, + ) assert_equal(mol.atnums, np.array([8, 1, 1])) assert mol.mo.coeffs.shape == (21, 5) - assert_allclose(mol.energy, -74.965901170787, rtol=0, atol=1.e-6) - assert mol.extra['keywords'] == 'GTO' - assert mol.extra['virial_ratio'] == 2.00599838291596 + assert_allclose(mol.energy, -74.965901170787, rtol=0, atol=1.0e-6) + assert mol.extra["keywords"] == "GTO" + assert mol.extra["virial_ratio"] == 2.00599838291596 assert mol.mo.kind == "restricted" - assert_allclose(mol.mo.energies, - np.array([-20.2515479, -1.25760928, - -0.59394112, -0.45972872, - -0.39261846]), rtol=0, atol=1.e-6) - assert_equal(mol.mo.occs, np.array([2., 2., 2., 2., 2.])) - assert_equal(mol.mo.occsa, np.array([1., 1., 1., 1., 1.])) + assert_allclose( + mol.mo.energies, + np.array([-20.2515479, -1.25760928, -0.59394112, -0.45972872, -0.39261846]), + rtol=0, + atol=1.0e-6, + ) + assert_equal(mol.mo.occs, np.array([2.0, 2.0, 2.0, 2.0, 2.0])) + assert_equal(mol.mo.occsa, np.array([1.0, 1.0, 1.0, 1.0, 1.0])) assert mol.mo.spinpol == 0.0 assert mol.mo.nbasis == 21 assert mol.obasis.nbasis == 21 - assert mol.obasis.primitive_normalization == 'L2' + assert mol.obasis.primitive_normalization == "L2" assert [shell.icenter for shell in mol.obasis.shells] == [0] * 9 + [1] * 3 + [2] * 3 - assert [shell.kinds for shell in mol.obasis.shells] == [['c']] * 15 - assert_allclose([shell.exponents for shell in mol.obasis.shells[:6]], - [[130.709321], [23.8088661], [6.44360831], [5.03315132], - [1.16959612], [0.38038896]]) - assert_allclose([shell.exponents for shell in mol.obasis.shells[6:9]], - [[5.03315132], [1.16959612], [0.38038896]]) - assert_allclose([shell.exponents for shell in mol.obasis.shells[9:15]], - [[3.42525091], [0.62391373], [0.168855404], - [3.42525091], [0.62391373], [0.168855404]]) + assert [shell.kinds for shell in mol.obasis.shells] == [["c"]] * 15 + assert_allclose( + [shell.exponents for shell in mol.obasis.shells[:6]], + [[130.709321], [23.8088661], [6.44360831], [5.03315132], [1.16959612], [0.38038896]], + ) + assert_allclose( + [shell.exponents for shell in mol.obasis.shells[6:9]], + [[5.03315132], [1.16959612], [0.38038896]], + ) + assert_allclose( + [shell.exponents for shell in mol.obasis.shells[9:15]], + [[3.42525091], [0.62391373], [0.168855404], [3.42525091], [0.62391373], [0.168855404]], + ) assert_allclose([shell.coeffs for shell in mol.obasis.shells], [[[1]]] * 15) assert mol.obasis_name is None - assert mol.title == 'H2O HF/STO-3G//HF/STO-3G' + assert mol.title == "H2O HF/STO-3G//HF/STO-3G" # check orthonormal mo olp = compute_overlap(mol.obasis, mol.atcoords) - check_orthonormal(mol.mo.coeffsa, olp, 1.e-5) + check_orthonormal(mol.mo.coeffsa, olp, 1.0e-5) def test_load_one_h2(): """Test load_one with h2 ub3lyp_ccpvtz WFX input.""" with as_file(files("iodata.test.data").joinpath("h2_ub3lyp_ccpvtz.wfx")) as file_wfx: mol = load_one(str(file_wfx)) - assert_allclose(mol.atcoords, - np.array([[0.0, 0.0, 0.7019452462164], - [0.0, 0.0, -0.7019452462164]]), - rtol=0, atol=1.e-6) - assert_allclose(mol.atgradient, - np.array([[9.74438416e-17, -2.08884441e-16, -7.18565768e-09], - [-9.74438416e-17, 2.08884441e-16, 7.18565768e-09]]), - rtol=0, atol=1.e-6) + assert_allclose( + mol.atcoords, + np.array([[0.0, 0.0, 0.7019452462164], [0.0, 0.0, -0.7019452462164]]), + rtol=0, + atol=1.0e-6, + ) + assert_allclose( + mol.atgradient, + np.array( + [ + [9.74438416e-17, -2.08884441e-16, -7.18565768e-09], + [-9.74438416e-17, 2.08884441e-16, 7.18565768e-09], + ] + ), + rtol=0, + atol=1.0e-6, + ) assert_equal(mol.atnums, np.array([1, 1])) - assert_allclose(mol.energy, -1.179998789924, rtol=0, atol=1.e-6) - assert mol.extra['keywords'] == 'GTO' - assert mol.extra['num_perturbations'] == 0 + assert_allclose(mol.energy, -1.179998789924, rtol=0, atol=1.0e-6) + assert mol.extra["keywords"] == "GTO" + assert mol.extra["num_perturbations"] == 0 assert mol.mo.coeffs.shape == (34, 56) - assert_allclose(mol.mo.energies[:7], - np.array([-0.43408309, 0.0581059, 0.19574763, 0.4705944, - 0.51160035, 0.51160035, 0.91096805]), rtol=0, atol=1.e-6) + assert_allclose( + mol.mo.energies[:7], + np.array( + [-0.43408309, 0.0581059, 0.19574763, 0.4705944, 0.51160035, 0.51160035, 0.91096805] + ), + rtol=0, + atol=1.0e-6, + ) assert mol.mo.occs.sum() == 2.0 assert mol.mo.occsa.sum() == 1.0 assert mol.mo.spinpol == 0.0 assert mol.mo.nbasis == 34 assert mol.mo.kind == "unrestricted" assert mol.obasis.nbasis == 34 - assert mol.obasis.primitive_normalization == 'L2' + assert mol.obasis.primitive_normalization == "L2" assert [shell.icenter for shell in mol.obasis.shells] == [0] * 8 + [1] * 8 - assert [shell.kinds for shell in mol.obasis.shells] == [['c']] * 16 - assert_allclose([shell.exponents for shell in mol.obasis.shells], - 2 * [[33.87], [5.095], [1.159], [0.3258], [0.1027], [1.407], [0.388], [1.057]]) + assert [shell.kinds for shell in mol.obasis.shells] == [["c"]] * 16 + assert_allclose( + [shell.exponents for shell in mol.obasis.shells], + 2 * [[33.87], [5.095], [1.159], [0.3258], [0.1027], [1.407], [0.388], [1.057]], + ) assert_allclose([shell.coeffs for shell in mol.obasis.shells], [[[1]]] * 16) assert mol.obasis_name is None - assert mol.title == 'h2 ub3lyp/cc-pvtz opt-stable-freq' + assert mol.title == "h2 ub3lyp/cc-pvtz opt-stable-freq" # check orthonormal mo olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp, 1e-5) @@ -470,19 +757,42 @@ def test_load_one_lih_cation_cisd(): with as_file(files("iodata.test.data").joinpath("lih_cation_cisd.wfx")) as file_wfx: mol = load_one(str(file_wfx)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 11 assert mol.mo.norbb == 11 assert mol.mo.norb == 22 - assert_allclose(mol.mo.occsa, [ - 9.99999999804784E-1, 9.99999998539235E-1, 2.26431690664012E-10, - 5.38480519435475E-11, 0.0, 0.0, 0.0, 0.0, -5.97822590358596E-13, - -6.01428138426375E-12, -9.12834417514434E-12]) - assert_allclose(mol.mo.occsb, [ - 9.99999997403326E-1, 6.03380142010587E-11, 4.36865874834240E-12, - 4.14106040987552E-13, 0.0, 0.0, 0.0, 0.0, -5.69902692731031E-13, - -1.60216470544366E-11, -3.25430470734432E-10, - ]) + assert_allclose( + mol.mo.occsa, + [ + 9.99999999804784e-1, + 9.99999998539235e-1, + 2.26431690664012e-10, + 5.38480519435475e-11, + 0.0, + 0.0, + 0.0, + 0.0, + -5.97822590358596e-13, + -6.01428138426375e-12, + -9.12834417514434e-12, + ], + ) + assert_allclose( + mol.mo.occsb, + [ + 9.99999997403326e-1, + 6.03380142010587e-11, + 4.36865874834240e-12, + 4.14106040987552e-13, + 0.0, + 0.0, + 0.0, + 0.0, + -5.69902692731031e-13, + -1.60216470544366e-11, + -3.25430470734432e-10, + ], + ) # check orthonormal mo olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffsa, olp, 1e-5) @@ -493,7 +803,7 @@ def test_load_one_lih_cation_uhf(): with as_file(files("iodata.test.data").joinpath("lih_cation_uhf.wfx")) as file_wfx: mol = load_one(str(file_wfx)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'unrestricted' + assert mol.mo.kind == "unrestricted" assert mol.mo.norba == 2 assert mol.mo.norbb == 1 assert mol.mo.norb == 3 @@ -509,7 +819,7 @@ def test_load_one_lih_cation_rohf(): with as_file(files("iodata.test.data").joinpath("lih_cation_rohf.wfx")) as file_wfx: mol = load_one(str(file_wfx)) # check number of orbitals and occupation numbers - assert mol.mo.kind == 'restricted' + assert mol.mo.kind == "restricted" assert mol.mo.norba == 2 assert mol.mo.norbb == 2 assert mol.mo.norb == 2 diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index 5fa1eb2a..31e4f4cf 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -34,30 +34,33 @@ def test_load_water_number(): # test xyz with atomic numbers - with as_file(files('iodata.test.data').joinpath('water_number.xyz')) as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn_xyz: mol = load_one(str(fn_xyz)) check_water(mol) def test_load_water_element(): # test xyz file with atomic symbols - with as_file(files('iodata.test.data').joinpath('water_element.xyz')) as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_element.xyz")) as fn_xyz: mol = load_one(str(fn_xyz)) check_water(mol) def check_water(mol): """Test some things on a water file.""" - assert mol.title == 'Water' + assert mol.title == "Water" assert_equal(mol.atnums, [1, 8, 1]) # check bond length print(np.linalg.norm(mol.atcoords[0] - mol.atcoords[2]) / angstrom) - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.960, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.960, atol=1.e-5) - assert_allclose(np.linalg.norm( - mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.e-3) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[1]) / angstrom, 0.960, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[2] - mol.atcoords[1]) / angstrom, 0.960, atol=1.0e-5 + ) + assert_allclose( + np.linalg.norm(mol.atcoords[0] - mol.atcoords[2]) / angstrom, 1.568, atol=1.0e-3 + ) FCC_ATOM_COLUMNS = DEFAULT_ATOM_COLUMNS + [ @@ -66,9 +69,14 @@ def check_water(mol): ("extra", "zs", (), int, int, "{:2d}".format), # Note that in IOData, the energy gradient is stored, which contains the # negative forces. - ("atgradient", None, (3,), float, - (lambda word: -float(word)), - (lambda value: "{:15.10f}".format(-value))) + ( + "atgradient", + None, + (3,), + float, + (lambda word: -float(word)), + (lambda value: "{:15.10f}".format(-value)), + ), ] @@ -93,7 +101,7 @@ def check_load_dump_consistency(tmpdir, fn, atom_columns=None): else: mol0 = load_one(str(fn)) # write xyz file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test.xyz') + fn_tmp = os.path.join(tmpdir, "test.xyz") dump_one(mol0, fn_tmp, atom_columns=atom_columns) mol1 = load_one(fn_tmp, atom_columns=atom_columns) # check two xyz files @@ -168,11 +176,11 @@ def test_load_dump_many_consistency(tmpdir): with as_file(files("iodata.test.data").joinpath("water_trajectory.xyz")) as fn_xyz: mols0 = list(load_many(str(fn_xyz))) # write xyz file in a temporary folder & then read it - fn_tmp = os.path.join(tmpdir, 'test') - dump_many(mols0, fn_tmp, fmt='xyz') - mols1 = list(load_many(fn_tmp, fmt='xyz')) + fn_tmp = os.path.join(tmpdir, "test") + dump_many(mols0, fn_tmp, fmt="xyz") + mols1 = list(load_many(fn_tmp, fmt="xyz")) assert len(mols0) == len(mols1) for mol0, mol1 in zip(mols0, mols1): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) - assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.0e-5) diff --git a/iodata/utils.py b/iodata/utils.py index 6035d952..34499633 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -18,7 +18,6 @@ # -- """Utility functions module.""" - from typing import Tuple import warnings @@ -30,25 +29,32 @@ from .attrutils import validate_shape -__all__ = ['LineIterator', 'Cube', 'set_four_index_element', 'volume', - 'derive_naturals', 'check_dm', 'strtobool'] +__all__ = [ + "LineIterator", + "Cube", + "set_four_index_element", + "volume", + "derive_naturals", + "check_dm", + "strtobool", +] # The unit conversion factors below can be used as follows: # - Conversion to atomic units: distance = 5*angstrom # - Conversion from atomic units: print(distance/angstrom) -angstrom: float = spc.angstrom / spc.value('atomic unit of length') -electronvolt: float = 1 / spc.value('hartree-electron volt relationship') +angstrom: float = spc.angstrom / spc.value("atomic unit of length") +electronvolt: float = 1 / spc.value("hartree-electron volt relationship") # Unit conversion for Gromacs gro files -meter: float = 1 / spc.value('Bohr radius') +meter: float = 1 / spc.value("Bohr radius") nanometer: float = 1e-9 * meter -second: float = 1 / spc.value('atomic unit of time') +second: float = 1 / spc.value("atomic unit of time") picosecond: float = 1e-12 * second # atomic mass unit (not atomic unit of mass!) -amu: float = 1e-3 / (spc.value('electron mass') * spc.value('Avogadro constant')) -kcalmol: float = 1e3 * spc.calorie / spc.value('Avogadro constant') / spc.value('Hartree energy') -calmol: float = spc.calorie / spc.value('Avogadro constant') / spc.value('Hartree energy') -kjmol: float = 1e3 / spc.value('Avogadro constant') / spc.value('Hartree energy') +amu: float = 1e-3 / (spc.value("electron mass") * spc.value("Avogadro constant")) +kcalmol: float = 1e3 * spc.calorie / spc.value("Avogadro constant") / spc.value("Hartree energy") +calmol: float = spc.calorie / spc.value("Avogadro constant") / spc.value("Hartree energy") +kjmol: float = 1e3 / spc.value("Avogadro constant") / spc.value("Hartree energy") class FileFormatError(IOError): @@ -111,8 +117,7 @@ def warn(self, msg: str): Message to raise alongside filename and line number. """ - warnings.warn("{}:{} {}".format(self.filename, self.lineno, msg), - FileFormatWarning, 2) + warnings.warn("{}:{} {}".format(self.filename, self.lineno, msg), FileFormatWarning, 2) def back(self, line): """Go one line back and decrease the lineno attribute by one.""" @@ -120,8 +125,7 @@ def back(self, line): self.lineno -= 1 -@attr.s(auto_attribs=True, slots=True, - on_setattr=[attr.setters.validate, attr.setters.convert]) +@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) class Cube: """The volumetric data from a cube (or similar) file. @@ -148,8 +152,9 @@ def shape(self): return self.data.shape -def set_four_index_element(four_index_object: np.ndarray, i: int, j: int, k: int, l: int, - value: float): +def set_four_index_element( + four_index_object: np.ndarray, i: int, j: int, k: int, l: int, value: float +): """Assign values to a four index object, account for 8-fold index symmetry. This function assumes physicists' notation. @@ -228,7 +233,7 @@ def derive_naturals(dm: np.ndarray, overlap: np.ndarray) -> Tuple[np.ndarray, np # Diagonalize and compute eigenvalues evals, evecs = eigh(sds, overlap) coeffs = np.zeros_like(overlap) - coeffs = evecs[:, :coeffs.shape[1]] + coeffs = evecs[:, : coeffs.shape[1]] occs = evals return coeffs, occs @@ -258,26 +263,30 @@ def check_dm(dm: np.ndarray, overlap: np.ndarray, eps: float = 1e-4, occ_max: fl # construct natural orbitals occupations = derive_naturals(dm, overlap)[1] if occupations.min() < -eps: - raise ValueError('The density matrix has eigenvalues considerably smaller than ' - 'zero. error=%e' % (occupations.min())) + raise ValueError( + "The density matrix has eigenvalues considerably smaller than " + "zero. error=%e" % (occupations.min()) + ) if occupations.max() > occ_max + eps: - raise ValueError('The density matrix has eigenvalues considerably larger than ' - 'max. error=%e' % (occupations.max() - 1)) + raise ValueError( + "The density matrix has eigenvalues considerably larger than " + "max. error=%e" % (occupations.max() - 1) + ) STRTOBOOL = { - 'y': True, - 'yes': True, - 't': True, - 'true': True, - 'on': True, - '1': True, - 'n': False, - 'no': False, - 'f': False, - 'false': False, - 'off': False, - '0': False + "y": True, + "yes": True, + "t": True, + "true": True, + "on": True, + "1": True, + "n": False, + "no": False, + "f": False, + "false": False, + "off": False, + "0": False, } diff --git a/tools/harmonics.py b/tools/harmonics.py index 3c22f57b..ba8881b2 100644 --- a/tools/harmonics.py +++ b/tools/harmonics.py @@ -19,7 +19,6 @@ # -- """Build transformation matrices from Cartesian to pure basis functions.""" - import argparse import numpy as np @@ -190,9 +189,7 @@ def get_cart_l2_norm(alpha, nx, ny, nz): def get_pure_l2_norm(alpha, l): """Compute the norm of a pure gaussian primitive.""" return sp.sqrt( - int(fac2(2 * l - 1)) - / (2 * alpha / sp.pi) ** sp.Rational(3, 2) - / (4 * alpha) ** l + int(fac2(2 * l - 1)) / (2 * alpha / sp.pi) ** sp.Rational(3, 2) / (4 * alpha) ** l ) @@ -215,6 +212,7 @@ def include_l2_norm(tfs): def print_latex(tfs): """Print transformation matrices in Latex code.""" + def iter_pure_labels(l): """Iterate over labels for pure functions.""" yield "C_{{{}0}}".format(l) @@ -257,6 +255,7 @@ def tostr(v): def print_python(tfs): """Print transformation matrices in Python code.""" + def tostr(v): """Format an sympy expression as a float with sufficient digits.""" s = repr(v.evalf(17)) @@ -269,9 +268,7 @@ def tostr(v): print("tf{} = np.array([".format(l)) for ipure in range(npure): print( - " [{}],".format( - ", ".join([tostr(tf[ipure, icart]) for icart in range(ncart)]) - ) + " [{}],".format(", ".join([tostr(tf[ipure, icart]) for icart in range(ncart)])) ) print("])") print("tfs = [{}]".format(", ".join("tf{}".format(l) for l in range(len(tfs))))) @@ -285,31 +282,24 @@ def test_manual(): assert rrsh[1] == z assert rrsh[2] == x assert rrsh[3] == y - assert rrsh[4].expand() == (sp.Rational(3, 2) * z ** 2 - r2 / 2).expand() + assert rrsh[4].expand() == (sp.Rational(3, 2) * z**2 - r2 / 2).expand() assert rrsh[5].expand() == (sp.sqrt(3) * x * z) assert rrsh[6].expand() == (sp.sqrt(3) * y * z) - assert rrsh[7].expand() == (sp.sqrt(3) / 2 * (x ** 2 - y ** 2)).expand() + assert rrsh[7].expand() == (sp.sqrt(3) / 2 * (x**2 - y**2)).expand() assert rrsh[8].expand() == (sp.sqrt(3) * x * y) - assert ( - rrsh[9].expand() - == (sp.Rational(5, 2) * z ** 3 - sp.Rational(3, 2) * r2 * z).expand() - ) + assert rrsh[9].expand() == (sp.Rational(5, 2) * z**3 - sp.Rational(3, 2) * r2 * z).expand() assert ( rrsh[10].expand() - == ( - x / sp.sqrt(6) * (sp.Rational(15, 2) * z ** 2 - sp.Rational(3, 2) * r2) - ).expand() + == (x / sp.sqrt(6) * (sp.Rational(15, 2) * z**2 - sp.Rational(3, 2) * r2)).expand() ) assert ( rrsh[11].expand() - == ( - y / sp.sqrt(6) * (sp.Rational(15, 2) * z ** 2 - sp.Rational(3, 2) * r2) - ).expand() + == (y / sp.sqrt(6) * (sp.Rational(15, 2) * z**2 - sp.Rational(3, 2) * r2)).expand() ) - assert rrsh[12].expand() == (sp.sqrt(15) * z / 2 * (x ** 2 - y ** 2)).expand() + assert rrsh[12].expand() == (sp.sqrt(15) * z / 2 * (x**2 - y**2)).expand() assert rrsh[13].expand() == (sp.sqrt(15) * x * y * z).expand() - assert rrsh[14].expand() == (sp.sqrt(10) / 4 * (x ** 3 - 3 * x * y ** 2)).expand() - assert rrsh[15].expand() == (sp.sqrt(10) / 4 * (3 * x ** 2 * y - y ** 3)).expand() + assert rrsh[14].expand() == (sp.sqrt(10) / 4 * (x**3 - 3 * x * y**2)).expand() + assert rrsh[15].expand() == (sp.sqrt(10) / 4 * (3 * x**2 * y - y**3)).expand() def test_library(): @@ -337,7 +327,7 @@ def _get_cartfn(l, m): # Undo the Condon-Shortley phase ref *= (-sp.Integer(1)) ** abs(m) # Convert to regular solid harmonics - ref = (sp.sqrt(4 * sp.pi / (2 * l + 1)) * ref * r ** l).expand() + ref = (sp.sqrt(4 * sp.pi / (2 * l + 1)) * ref * r**l).expand() # From spherical to Cartesian coordinates ref = ref.subs(sp.cos(phi), x / (r * sp.sin(theta))) ref = ref.subs(sp.sin(phi), y / (r * sp.sin(theta)))