From f826aeed27afd77731127c8416db5e017963ed26 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Thu, 25 Mar 2021 15:05:06 +0100 Subject: [PATCH 001/144] Overlap integrals for a pair of basis functions --- iodata/overlap.py | 121 +++++++++++++++++++++++++----------- iodata/test/test_overlap.py | 53 +++++++++++++++- 2 files changed, 136 insertions(+), 38 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index dd7e4b5bd..06725cd6a 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -18,6 +18,8 @@ # -- """Module for computing overlap of atomic orbital basis functions.""" +from typing import Optional + import attr import numpy as np from scipy.special import binom, factorial2 @@ -29,46 +31,83 @@ __all__ = ['OVERLAP_CONVENTIONS', 'compute_overlap', 'gob_cart_normalization'] -# pylint: disable=too-many-nested-blocks,too-many-statements -def compute_overlap(obasis: MolecularBasis, atcoords: np.ndarray) -> np.ndarray: - r"""Compute overlap matrix for the given molecular basis set. +# 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: + r"""Compute overlap matrix for the given molecular basis set(s). .. math:: \braket{\psi_{i}}{\psi_{j}} + When only one basis set is given, the overlap matrix of that basis (with + itself) is computed. If a second basis set (with its atomic coordinates) is + provided, the overlap between the two basis sets is computed. + This function takes into account the requested order of the basis functions - in ``obasis.conventions``. Note that only L2 normalized primitives are - supported at the moment. + in ``obasis0.conventions`` (and ``obasis1.conventions``). Note that only L2 + normalized primitives are supported at the moment. Parameters ---------- - obasis + obasis0 The orbital basis set. - atcoords + atcoords0 The atomic Cartesian coordinates (including those of ghost atoms). + obasis1 + An optional second orbital basis set. + atcoords1 + An optional second array with atomic Cartesian coordinates + (including those of ghost atoms). Returns ------- overlap - The matrix with overlap integrals, shape=(obasis.nbasis, obasis.nbasis). + The matrix with overlap integrals, ``shape=(obasis0.nbasis, obasis1.nbasis)``. """ - if obasis.primitive_normalization != 'L2': + if obasis0.primitive_normalization != 'L2': raise ValueError('The overlap integrals are only implemented for L2 ' 'normalization.') - # Initialize result - overlap = np.zeros((obasis.nbasis, obasis.nbasis)) - # Get a segmented basis, for simplicity - obasis = obasis.get_segmented() + obasis0 = obasis0.get_segmented() + + # 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.") + obasis1 = obasis0 + atcoords1 = atcoords0 + identical = True + else: + 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.") + # Get a segmented basis, for simplicity + obasis1 = obasis1.get_segmented() + identical = False + + # Initialize result + overlap = np.zeros((obasis0.nbasis, obasis1.nbasis)) # Compute the normalization constants of the Cartesian primitives, with the # contraction coefficients multiplied in. - scales = [_compute_cart_shell_normalizations(shell) * shell.coeffs - for shell in obasis.shells] - - n_max = np.max([np.max(shell.angmoms) for shell in obasis.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] + + n_max = max(np.max(shell.angmoms) for shell in obasis0.shells) + if not identical: + n_max = max(n_max, max(np.max(shell.angmoms) for shell in obasis1.shells)) go = GaussianOverlap(n_max) # define a python ufunc (numpy function) for broadcasted calling over angular momentums @@ -78,21 +117,25 @@ def compute_overlap(obasis: MolecularBasis, atcoords: np.ndarray) -> np.ndarray: begin0 = 0 # pylint: disable=too-many-nested-blocks - for i0, shell0 in enumerate(obasis.shells): - r0 = atcoords[shell0.icenter] + for i0, shell0 in enumerate(obasis0.shells): + r0 = atcoords0[shell0.icenter] end0 = begin0 + shell0.nbasis # Loop over shell1 (lower triangular only, including diagonal) begin1 = 0 - for i1, shell1 in enumerate(obasis.shells[:i0 + 1]): - r1 = atcoords[shell1.icenter] + if identical: + nshell1 = i0 + 1 + else: + nshell1 = len(obasis1.shells) + for i1, shell1 in enumerate(obasis1.shells[:nshell1]): + r1 = atcoords1[shell1.icenter] end1 = begin1 + shell1.nbasis # prepare some constants to save FLOPS later on rij = r0 - r1 rij_norm_sq = np.dot(rij, rij) - # Check if the result is going to signifcant + # Check if the result is going to significant. a0_min = np.min(shell0.exponents) a1_min = np.min(shell1.exponents) prefactor_max = np.exp(-a0_min * a1_min * rij_norm_sq / (a0_min + a1_min)) @@ -102,14 +145,14 @@ def compute_overlap(obasis: MolecularBasis, atcoords: np.ndarray) -> np.ndarray: # arrays of angular momentums [[2, 0, 0], [0, 2, 0], ..., [0, 1, 1]] n0 = np.array(list(iter_cart_alphabet(shell0.angmoms[0]))) n1 = np.array(list(iter_cart_alphabet(shell1.angmoms[0]))) - result = np.zeros((n0.shape[0], n1.shape[0])) + shell_overlap = np.zeros((n0.shape[0], n1.shape[0])) # Loop over primitives in shell0 (Cartesian) - for scales0, a0 in zip(scales[i0], shell0.exponents): + for shell_scales0, a0 in zip(scales0[i0], shell0.exponents): a0_r0 = a0 * r0 # Loop over primitives in shell1 (Cartesian) - for scales1, a1 in zip(scales[i1], shell1.exponents): + for shell_scales1, a1 in zip(scales1[i1], shell1.exponents): at = a0 + a1 prefactor = np.exp(-a0 * a1 / at * rij_norm_sq) if prefactor < 1e-15: @@ -127,27 +170,35 @@ def compute_overlap(obasis: MolecularBasis, atcoords: np.ndarray) -> np.ndarray: # array operations. vs = compute_overlap_1d(rn_0, rn_1, n0[:, None, :], n1[None, :, :], two_at) v = np.prod(vs.astype(float), axis=2) - result += v * prefactor * scales0[:, None] * scales1[None, :] + v *= prefactor + v *= shell_scales0[:, None] + v *= shell_scales1[None, :] + shell_overlap += v # END of Cartesian coordinate system (if going to pure coordinates) # cart to pure if shell0.kinds[0] == 'p': - result = np.dot(tfs[shell0.angmoms[0]], result) + shell_overlap = np.dot(tfs[shell0.angmoms[0]], shell_overlap) if shell1.kinds[0] == 'p': - result = np.dot(result, tfs[shell1.angmoms[0]].T) + shell_overlap = np.dot(shell_overlap, tfs[shell1.angmoms[0]].T) # store lower triangular result - overlap[begin0:end0, begin1:end1] = result - # store upper triangular result - overlap[begin1:end1, begin0:end0] = result.T + overlap[begin0:end0, begin1:end1] = shell_overlap + if identical: + # store upper triangular result + overlap[begin1:end1, begin0:end0] = shell_overlap.T begin1 = end1 begin0 = end0 - permutation, signs = convert_conventions(obasis, OVERLAP_CONVENTIONS, reverse=True) - overlap = overlap[permutation] * signs.reshape(-1, 1) - overlap = overlap[:, permutation] * signs + permutation0, signs0 = convert_conventions(obasis0, OVERLAP_CONVENTIONS, reverse=True) + overlap = overlap[permutation0] * signs0.reshape(-1, 1) + if identical: + permutation1, signs1 = permutation0, signs0 + else: + permutation1, signs1 = convert_conventions(obasis1, OVERLAP_CONVENTIONS, reverse=True) + overlap = overlap[:, permutation1] * signs1 return overlap @@ -176,7 +227,7 @@ 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) + integ = self.facts[m] / two_at ** (m / 2) # TODO // 2 value += pf_i * self.binomials[n2][j] * x2 ** (n2 - j) * integ return value diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index 797d7de49..c444bb6c4 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -18,13 +18,15 @@ # -- """Test iodata.overlap & iodata.overlap_accel modules.""" +import itertools + import attr import numpy as np from numpy.testing import assert_allclose -from pytest import raises +import pytest from ..api import load_one -from ..basis import MolecularBasis, Shell +from ..basis import MolecularBasis, Shell, convert_conventions from ..overlap import compute_overlap, OVERLAP_CONVENTIONS try: @@ -83,5 +85,50 @@ def test_load_fchk_o2_cc_pvtz_cart_num(): def test_overlap_l1(): dbasis = MolecularBasis([], {}, 'L1') atcoords = np.zeros((1, 3)) - with raises(ValueError): + with pytest.raises(ValueError): _ = compute_overlap(dbasis, atcoords) + + +FNS_TWO_BASIS = [ + "h_sto3g.fchk", + "hf_sto3g.fchk", + "2h-azirine-cc.fchk", + "water_ccpvdz_pure_hf_g03.fchk" +] + + +@pytest.mark.parametrize("fn", FNS_TWO_BASIS) +def test_overlap_two_basis_same(fn): + with path('iodata.test.data', fn) as pth: + mol = load_one(pth) + olp_a = compute_overlap(mol.obasis, mol.atcoords, mol.obasis, mol.atcoords) + olp_b = compute_overlap(mol.obasis, mol.atcoords) + assert_allclose(olp_a, olp_b, rtol=0, atol=1e-14) + + +@pytest.mark.parametrize("fn0,fn1", itertools.combinations_with_replacement(FNS_TWO_BASIS, 2)) +def test_overlap_two_basis_different(fn0, fn1): + with path('iodata.test.data', fn0) as pth0: + mol0 = load_one(pth0) + with path('iodata.test.data', fn1) as pth1: + mol1 = load_one(pth1) + # Direct computation of the off-diagonal block. + olp_a = compute_overlap(mol0.obasis, mol0.atcoords, mol1.obasis, mol1.atcoords) + # Poor-man's approach: combine two molecules into one and compute its + # 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 + ] + 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]:] + 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) + permutation1, signs1 = convert_conventions(mol1.obasis, OVERLAP_CONVENTIONS, reverse=True) + olp_b = olp_b[:, permutation1] * signs1 + # Finally compare the numbers. + assert_allclose(olp_a, olp_b, rtol=0, atol=1e-14) From 536ee5c69381aafdfc977644b4f1e32b4bf34ad8 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Sun, 28 Mar 2021 16:38:41 +0200 Subject: [PATCH 002/144] Fix coverage issues --- iodata/test/test_overlap.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index c444bb6c4..20cc30c4b 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -87,6 +87,18 @@ def test_overlap_l1(): atcoords = np.zeros((1, 3)) with pytest.raises(ValueError): _ = compute_overlap(dbasis, atcoords) + obasis = MolecularBasis([], {}, 'L2') + with pytest.raises(ValueError): + _ = compute_overlap(obasis, atcoords, dbasis, atcoords) + + +def test_overlap_two_basis_exceptions(): + with path('iodata.test.data', 'hf_sto3g.fchk') as fn_fchk: + data = load_one(fn_fchk) + with pytest.raises(TypeError): + compute_overlap(data.obasis, data.atcoords, data.obasis, None) + with pytest.raises(TypeError): + compute_overlap(data.obasis, data.atcoords, None, data.atcoords) FNS_TWO_BASIS = [ From fc5329b5d993f06cf68b6caf9db109a6c8d619f6 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Mon, 29 Mar 2021 21:46:58 +0200 Subject: [PATCH 003/144] Add support for bonds to SDF format --- iodata/formats/sdf.py | 48 +++++++++++++++++++++++++---------------- iodata/periodic.py | 6 ++++-- iodata/test/test_sdf.py | 23 ++++++++++++++++++-- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index a25135e1c..e7e0a33ed 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -41,7 +41,7 @@ PATTERNS = ['*.sdf'] -@document_load_one("SDF", ['atcoords', 'atnums', '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() @@ -49,15 +49,22 @@ def load_one(lit: LineIterator) -> dict: next(lit) next(lit) words = next(lit).split() - size = int(words[0]) - atcoords = np.empty((size, 3), float) - atnums = np.empty(size, int) - for i in range(size): + natom = int(words[0]) + nbond = int(words[1]) + atcoords = np.empty((natom, 3), float) + atnums = np.empty(natom, int) + for iatom in range(natom): words = next(lit).split() - atcoords[i, 0] = float(words[0]) * angstrom - atcoords[i, 1] = float(words[1]) * angstrom - atcoords[i, 2] = float(words[2]) * angstrom - atnums[i] = sym2num.get(words[3].title()) + atcoords[iatom, 0] = float(words[0]) * angstrom + atcoords[iatom, 1] = float(words[1]) * angstrom + atcoords[iatom, 2] = float(words[2]) * angstrom + atnums[iatom] = sym2num.get(words[3].title()) + bonds = np.empty((nbond, 3), int) + for ibond in range(nbond): + words = next(lit).split() + bonds[ibond, 0] = int(words[0]) - 1 + bonds[ibond, 1] = int(words[1]) - 1 + bonds[ibond, 2] = int(words[2]) while True: try: words = next(lit) @@ -68,11 +75,12 @@ def load_one(lit: LineIterator) -> dict: return { 'title': title, 'atcoords': atcoords, - 'atnums': atnums + 'atnums': atnums, + 'bonds': bonds, } -@document_load_many("SDF", ['atcoords', 'atnums', '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,' @@ -84,21 +92,25 @@ def load_many(lit: LineIterator) -> Iterator[dict]: return -@document_dump_one("SDF", ['atcoords', 'atnums'], ['title']) +@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.natom, file=f) - for i in range(data.natom): - n = num2sym[data.atnums[i]] - x, y, z = data.atcoords[i] / angstrom - print(f'{x:15.10f} {y:15.10f} {z:15.10f} {n:2s}', 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) + if data.bonds is not None: + for iatom, jatom, bondtype in data.bonds: + print(f'{iatom + 1:3d}{jatom + 1:3d}{bondtype:3d} 0 0 0 0', file=f) print('$$$$', file=f) -@document_dump_many("SDF", ['atcoords', 'atnums'], ['title']) +@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/periodic.py b/iodata/periodic.py index 5198a28dc..9813acb1e 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -155,9 +155,11 @@ 1: "1", 2: "2", 3: "3", - # The following symbols are used in the MOL2 format: - 4: "am", + # The following symbols are used in the MOL2 format. + # The order deviates slightly from the MOL2 format for consistency with + # the indexes used in the SDF format. 5: "ar", + 4: "am", 6: "du", 7: "un", 8: "nc" diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 74eaf201c..69562675f 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -32,13 +32,24 @@ from importlib.resources import path -def test_sdf_load_one(): +def test_sdf_load_one_example(): # test sdf one structure with path('iodata.test.data', 'example.sdf') as fn_sdf: mol = load_one(str(fn_sdf)) check_example(mol) +def test_sdf_load_one_formamide(): + # test sdf one structure + with path('iodata.test.data', 'formamide.sdf') as fn_sdf: + mol = load_one(str(fn_sdf)) + assert mol.title == "713" + assert mol.natom == 6 + assert len(mol.bonds) == 5 + assert_equal(mol.atnums, [8, 7, 6, 1, 1, 1]) + assert_equal(mol.bonds, [[0, 2, 2], [1, 2, 1], [1, 3, 1], [1, 4, 1], [2, 5, 1]]) + + def test_sdf_formaterror(tmpdir): # test if sdf file has the wrong ending without $$$$ with path('iodata.test.data', 'example.sdf') as fn_test: @@ -50,7 +61,8 @@ def test_sdf_formaterror(tmpdir): def check_example(mol): """Test some things on example file.""" assert mol.title == '24978498' - assert_equal(mol.natom, 16) + 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]) # check coordinates atcoords_ang = mol.atcoords / angstrom @@ -58,6 +70,9 @@ def check_example(mol): assert_allclose(atcoords_ang[1], [5.4641, 1.0600, 0.0000]) assert_allclose(atcoords_ang[14], [6.0010, 1.3700, 0.0000]) assert_allclose(atcoords_ang[15], [2.0000, -2.5600, 0.0000]) + assert_equal(mol.bonds[0], [0, 3, 1]) + assert_equal(mol.bonds[4], [2, 8, 2]) + assert_equal(mol.bonds[14], [7, 11, 1]) def check_load_dump_consistency(tmpdir, fn): @@ -71,11 +86,14 @@ def check_load_dump_consistency(tmpdir, fn): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_equal(mol0.bonds, mol1.bonds) def test_load_dump_consistency(tmpdir): with path('iodata.test.data', 'example.sdf') as fn_sdf: check_load_dump_consistency(tmpdir, fn_sdf) + with path('iodata.test.data', 'formamide.sdf') as fn_sdf: + check_load_dump_consistency(tmpdir, fn_sdf) def test_load_many(): @@ -101,3 +119,4 @@ def test_load_dump_many_consistency(tmpdir): assert mol0.title == mol1.title assert_equal(mol0.atnums, mol1.atnums) assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) + assert_equal(mol0.bonds, mol1.bonds) From 97722f5dfcba4765159569dd73c61722dbc8d022 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Mon, 29 Mar 2021 22:06:21 +0200 Subject: [PATCH 004/144] Add missing test file --- iodata/test/data/formamide.sdf | 107 +++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 iodata/test/data/formamide.sdf diff --git a/iodata/test/data/formamide.sdf b/iodata/test/data/formamide.sdf new file mode 100644 index 000000000..79644ba83 --- /dev/null +++ b/iodata/test/data/formamide.sdf @@ -0,0 +1,107 @@ +713 + -OEChem-03292115373D + + 6 5 0 0 0 0 0 0 0999 V2000 + 1.1280 0.2091 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 + -1.1878 0.1791 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 + 0.0598 -0.3882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + -1.3085 1.1864 0.0001 H 0 0 0 0 0 0 0 0 0 0 0 0 + -2.0305 -0.3861 -0.0001 H 0 0 0 0 0 0 0 0 0 0 0 0 + -0.0014 -1.4883 -0.0001 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 3 2 0 0 0 0 + 2 3 1 0 0 0 0 + 2 4 1 0 0 0 0 + 2 5 1 0 0 0 0 + 3 6 1 0 0 0 0 +M END +> +713 + +> +0.4 + +> +1 + +> +6 +1 -0.57 +2 -0.8 +3 0.57 +4 0.37 +5 0.37 +6 0.06 + +> +0 + +> +2 +1 1 acceptor +1 2 donor + +> +3 + +> +0 + +> +0 + +> +0 + +> +0 + +> +0 + +> +1 + +> +2 + +> +000002C900000001 + +> +1.0328 + +> +10.148 + +> +139733 1 18410856568235096357 +21015797 1 18047756184745781958 + +> +50.89 +1.35 +0.63 +0.55 +0.08 +0.06 +0 +-0.17 +0 +-0.01 +0 +0 +0.02 +0 + +> +82.114 + +> +35.3 + +> +2 +5 +10 + +$$$$ From 24bbf38e0fe029aaf5b50acb77c255ee3f189799 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Mon, 29 Mar 2021 22:30:38 +0200 Subject: [PATCH 005/144] SDF refinements --- iodata/formats/sdf.py | 21 ++++++++++++++++++--- iodata/periodic.py | 16 +++++++++------- iodata/test/test_sdf.py | 3 +++ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index e7e0a33ed..da6fc9fcb 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -21,6 +21,12 @@ Usually, the different frames in a trajectory describe different geometries of the same molecule, with atoms in the same order. The ``load_many`` and ``dump_many`` functions below can also handle an SDF file with different molecules, e.g. a molecular database. + +The SDF format is somewhat documented on the following page: +http://www.nonlinear.com/progenesis/sdf-studio/v0.9/faq/sdf-file-format-guidance.aspx + +This format is one of the chemical table file formats: +https://en.wikipedia.org/wiki/Chemical_table_file """ @@ -31,7 +37,7 @@ 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 ..periodic import sym2num, num2sym, bond2num, num2bond from ..utils import angstrom, LineIterator @@ -41,6 +47,10 @@ PATTERNS = ['*.sdf'] +SDF2BOND = {1: "1", 2: "2", 3: "3", 4: "ar", 5: "sd", 6: "sar", 7: "dar", 8: "un"} +BOND2SDF = dict((val, key) for key, val in SDF2BOND.items()) + + @document_load_one("SDF", ['atcoords', 'atnums', 'bonds', 'title']) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" @@ -51,6 +61,8 @@ def load_one(lit: LineIterator) -> dict: words = next(lit).split() natom = int(words[0]) nbond = int(words[1]) + if words[-1] != "V2000": + lit.error("Only V2000 SDF files are supported.") atcoords = np.empty((natom, 3), float) atnums = np.empty(natom, int) for iatom in range(natom): @@ -64,7 +76,7 @@ def load_one(lit: LineIterator) -> dict: words = next(lit).split() bonds[ibond, 0] = int(words[0]) - 1 bonds[ibond, 1] = int(words[1]) - 1 - bonds[ibond, 2] = int(words[2]) + bonds[ibond, 2] = bond2num[SDF2BOND.get(int(words[2]), "un")] while True: try: words = next(lit) @@ -106,7 +118,10 @@ def dump_one(f: TextIO, data: IOData): 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(f'{iatom + 1:3d}{jatom + 1:3d}{bondtype:3d} 0 0 0 0', file=f) + print('{:3d}{:3d}{:3d} 0 0 0 0'.format( + iatom + 1, jatom + 1, + BOND2SDF.get(num2bond[bondtype], 8), + ), file=f) print('$$$$', file=f) diff --git a/iodata/periodic.py b/iodata/periodic.py index 9813acb1e..552485c20 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -156,13 +156,15 @@ 2: "2", 3: "3", # The following symbols are used in the MOL2 format. - # The order deviates slightly from the MOL2 format for consistency with - # the indexes used in the SDF format. - 5: "ar", - 4: "am", - 6: "du", - 7: "un", - 8: "nc" + 4: "am", # amide + 5: "ar", # aromatic + 6: "du", # dummy + 7: "un", # unknown or any + 8: "nc", # not connected + # The following are defined in the SDF or chemical table format + 9: "sd", # single or double + 10: "sar", # single or aromatic + 11: "dar", # double or aromatic } bond2num: Dict[str, int] = dict((value, key) for key, value in num2bond.items()) diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 69562675f..08456a9f2 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -94,6 +94,9 @@ def test_load_dump_consistency(tmpdir): check_load_dump_consistency(tmpdir, fn_sdf) with path('iodata.test.data', 'formamide.sdf') as fn_sdf: check_load_dump_consistency(tmpdir, fn_sdf) + # The benzene mol2 file has aromatic bonds, which are less common in SDF files. + with path('iodata.test.data', 'benzene.mol2') as fn_sdf: + check_load_dump_consistency(tmpdir, fn_sdf) def test_load_many(): From bfe312ea01643ce9b445f74aee11b1b3f75fa613 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Tue, 30 Mar 2021 06:28:48 +0200 Subject: [PATCH 006/144] Temporarily change install instructions in README --- README.rst | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index e4d6fa49e..a25b694bb 100644 --- a/README.rst +++ b/README.rst @@ -51,29 +51,42 @@ Please use the following citation in any publication using IOData library: Installation ------------ -To install IOData using the conda package management system, install -`miniconda `__ or -`anaconda `__ first, and then: - -.. code-block:: bash - - # Create a horton3 conda environment. (optional, recommended) - conda create -n horton3 - source activate horton3 +.. - # Install the stable release. - conda install -c theochem iodata + : To install IOData using the conda package management system, install + : `miniconda `__ or + : `anaconda `__ first, and then: + : + : .. code-block:: bash + : + : # Create a horton3 conda environment. (optional, recommended) + : conda create -n horton3 + : source activate horton3 + : + : # Install the stable release. + : conda install -c theochem iodata + : + : To install IOData with pip, you may want to create a `virtual environment`_, + : and then: + : + : .. code-block:: bash + : + : # Install the stable release. + : pip install qc-iodata -To install IOData with pip, you may want to create a `virtual environment`_, -and then: +In anticipation of the 1.0 release of IOData, install the latest git revision +as follows: .. code-block:: bash - # Install the stable release. - pip install qc-iodata + python -m pip install git+https://github.com/theochem/iodata.git + +Add the ``--user`` argument if you are not working in a virtual or conda +environment. Note that there may be API changes between subsequent revisions. See https://iodata.readthedocs.io/en/latest/install.html for full details. + .. |GithubActions| image:: https://github.com/theochem/iodata/actions/workflows/ci.yml/badge.svg?branch=master :target: https://github.com/theochem/iodata/actions/workflows/ci.yml .. |Version| image:: https://img.shields.io/pypi/pyversions/iodata.svg From 166817d058ced8851160fd98a3ef8a3c5241af0f Mon Sep 17 00:00:00 2001 From: tovrstra Date: Tue, 30 Mar 2021 10:58:33 +0200 Subject: [PATCH 007/144] PDB format: Load and dump CONECT lines --- iodata/formats/pdb.py | 126 +++++++++++++----- iodata/test/data/ch5plus.pdb | 17 +++ iodata/test/data/water_single_model.pdb | 3 + iodata/test/data/water_single_no_end.pdb | 3 + .../test/data/water_trajectory_no_model.pdb | 15 +++ iodata/test/test_pdb.py | 27 +++- 6 files changed, 156 insertions(+), 35 deletions(-) create mode 100644 iodata/test/data/ch5plus.pdb diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 0f91fff40..5b6b4a1c6 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -41,9 +41,7 @@ PATTERNS = ['*.pdb'] -@document_load_one("PDB", ['atcoords', 'atnums', 'atffparams', 'extra'], ['title']) -def load_one(lit: LineIterator) -> dict: - """Do not edit this docstring. It will be overwritten.""" +def _parse_pdb_atom_line(line): # Overview of ATOM records # COLUMNS DATA TYPE FIELD DEFINITION # ------------------------------------------------------------------------------------- @@ -62,66 +60,116 @@ def load_one(lit: LineIterator) -> dict: # 61 - 66 Real(6.2) tempFactor Temperature factor. # 77 - 78 LString(2) element Element symbol, right-justified. # 79 - 80 LString(2) charge Charge on the atom. + + # Get element symbol from position 77:78 in pdb format + words = line[76:78].split() + if not words: + # If not present, guess it from position 13:16 (atom name) + words = line[12:16].split() + # assign atomic number + symbol = words[0].title() + atnum = sym2num.get(symbol, sym2num.get(symbol[0], None)) + # atom name, residue name, chain id, & residue sequence number + attype = line[12:16].strip() + restype = line[17:20].strip() + chainid = line[21] + resnum = int(line[22:26]) + # add x, y, and z + atcoord = [ + float(line[30:38]) * angstrom, + float(line[38:46]) * angstrom, + float(line[46:54]) * angstrom, + ] + # get occupancies & temperature factor + occupancy = float(line[54:60]) + bfactor = float(line[60:66]) + return atnum, attype, restype, chainid, resnum, atcoord, occupancy, bfactor + + +def _parse_pdb_conect_line(line): + # Overview of CONECT records + # COLUMNS DATA TYPE FIELD DEFINITION + # ------------------------------------------------------------------------- + # 1 - 6 Record name "CONECT" + # 7 - 11 Integer serial Atom serial number + # 12 - 16 Integer serial Serial number of bonded atom + # 17 - 21 Integer serial Serial number of bonded atom + # 22 - 26 Integer serial Serial number of bonded atom + # 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() + if serial_str != "": + iatom1 = int(serial_str) - 1 + if iatom1 > iatom0: + yield iatom0, iatom1 + + +@document_load_one("PDB", ['atcoords', 'atnums', 'atffparams', 'extra'], ['title', 'bonds']) +def load_one(lit: LineIterator) -> dict: + """Do not edit this docstring. It will be overwritten.""" + title = "PDB file from IOData" atnums = [] attypes = [] restypes = [] chainids = [] resnums = [] - coords = [] + atcoords = [] occupancies = [] bfactors = [] + bonds = [] molecule_found = False end_reached = False - title = "PDB file from IOData" while True: try: line = next(lit) except StopIteration: break - # If the PDB file has a title replace it. + # If the PDB file has a title, replace the default. if line.startswith("TITLE") or line.startswith("COMPND"): title = line[10:].rstrip() if line.startswith("ATOM") or line.startswith("HETATM"): - # get element symbol from position 77:78 in pdb format - words = line[76:78].split() - if not words: - # If not present, guess it from position 13:16 (atom name) - words = line[12:16].split() - # assign atomic number - symbol = words[0].title() - atnums.append(sym2num.get(symbol, sym2num.get(symbol[0], None))) - # atom name, residue name, chain id, & residue sequence number - attypes.append(line[12:16].strip()) - restypes.append(line[17:20].strip()) - chainids.append(line[21]) - resnums.append(int(line[22:26])) - # add x, y, and z - coords.append([float(line[30:38]), float(line[38:46]), float(line[46:54])]) - # get occupancies & temperature factor - occupancies.append(float(line[54:60])) - bfactors.append(float(line[60:66])) + (atnum, attype, restype, chainid, resnum, atcoord, occupancy, + bfactor) = _parse_pdb_atom_line(line) + atnums.append(atnum) + attypes.append(attype) + restypes.append(restype) + chainids.append(chainid) + resnums.append(resnum) + atcoords.append(atcoord) + occupancies.append(occupancy) + bfactors.append(bfactor) molecule_found = True + if line.startswith("CONECT"): + for iatom0, iatom1 in _parse_pdb_conect_line(line): + bonds.append([iatom0, iatom1, 1]) if line.startswith("END") and molecule_found: end_reached = True break - if molecule_found is False: + if not molecule_found: lit.error("Molecule could not be read!") if not end_reached: lit.warn("The END is not found, but the parsed data is returned!") - atffparams = {"attypes": np.array(attypes), "restypes": np.array(restypes), - "resnums": np.array(resnums)} + atffparams = { + "attypes": np.array(attypes), + "restypes": np.array(restypes), + "resnums": np.array(resnums), + } extra = {"occupancies": np.array(occupancies), "bfactors": np.array(bfactors)} # add chain id, if it wasn't all empty if not np.all(chainids == [' '] * len(chainids)): extra["chainids"] = np.array(chainids) result = { - 'atcoords': np.array(coords) * angstrom, + 'atcoords': np.array(atcoords), 'atnums': np.array(atnums), 'atffparams': atffparams, 'title': title, - 'extra': extra + 'extra': extra, } + # assign bonds only if some were present + if len(bonds) > 0: + result["bonds"] = np.array(bonds) return result @@ -137,16 +185,18 @@ def load_many(lit: LineIterator) -> Iterator[dict]: return -@document_dump_one("PDB", ['atcoords', 'atnums', 'extra'], ['atffparams', 'title']) +@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.""" print(str("TITLE " + data.title) or "TITLE Created with IOData", file=f) + # 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) + # Write ATOM lines. for i in range(data.natom): n = num2sym[data.atnums[i]] resnum = -1 if resnums is None else resnums[i] @@ -159,6 +209,22 @@ def dump_one(f: TextIO, data: IOData): 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) + # Prepare for CONECT lines. + connections = [[] for iatom in range(data.natom)] + if data.bonds is not None: + for iatom0, iatom1 in data.bonds[:, :2]: + connections[iatom0].append(iatom1) + connections[iatom1].append(iatom0) + # Write CONECT lines. + for iatom0, iatoms1 in enumerate(connections): + if len(iatoms1) > 0: + # Write connection in groups of max 4 + for ichunk in range(len(iatoms1) // 4 + 1): + print("CONECT{:5d}{}".format( + iatom0 + 1, "".join( + "{:5d}".format(iatom1 + 1) + for iatom1 in iatoms1[ichunk * 4:ichunk * 4 + 4]) + ), file=f) print("END", file=f) diff --git a/iodata/test/data/ch5plus.pdb b/iodata/test/data/ch5plus.pdb new file mode 100644 index 000000000..0732405f5 --- /dev/null +++ b/iodata/test/data/ch5plus.pdb @@ -0,0 +1,17 @@ +COMPND UNNAMED +AUTHOR GENERATED BY OPEN BABEL 2.4.1 +HETATM 1 C UNL 1 1.084 0.018 -0.200 1.00 0.00 C1+ +HETATM 2 H UNL 1 1.536 0.152 -1.213 1.00 0.00 H +HETATM 3 H UNL 1 0.314 0.733 -0.579 1.00 0.00 H +HETATM 4 H UNL 1 0.636 -0.992 -0.139 1.00 0.00 H +HETATM 5 H UNL 1 0.925 0.544 0.772 1.00 0.00 H +HETATM 6 H UNL 1 2.147 -0.036 0.138 1.00 0.00 H +CONECT 1 2 3 4 5 +CONECT 1 6 +CONECT 2 1 +CONECT 3 1 +CONECT 4 1 +CONECT 5 1 +CONECT 6 1 +MASTER 0 0 0 0 0 0 0 0 6 0 6 0 +END diff --git a/iodata/test/data/water_single_model.pdb b/iodata/test/data/water_single_model.pdb index 78dd6b647..17753ffbb 100644 --- a/iodata/test/data/water_single_model.pdb +++ b/iodata/test/data/water_single_model.pdb @@ -2,5 +2,8 @@ MODEL HETATM 1 H HOH 0 0.784 -0.492 0.000 1.00 0.00 H HETATM 2 O HOH 1 0.000 0.062 0.000 1.00 0.00 O HETATM 3 H HOH 0 -0.784 -0.492 0.000 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 ENDMDL END diff --git a/iodata/test/data/water_single_no_end.pdb b/iodata/test/data/water_single_no_end.pdb index 6866315c1..088fb1e8a 100644 --- a/iodata/test/data/water_single_no_end.pdb +++ b/iodata/test/data/water_single_no_end.pdb @@ -3,3 +3,6 @@ AUTHOR GENERATED BY OPEN BABEL 2.4.1 HETATM 1 H HOH 0 0.784 -0.492 0.000 1.00 0.00 H HETATM 2 O HOH 1 0.000 0.062 0.000 1.00 0.00 O HETATM 3 H HOH 0 -0.784 -0.492 0.000 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 diff --git a/iodata/test/data/water_trajectory_no_model.pdb b/iodata/test/data/water_trajectory_no_model.pdb index ed1eb016e..9ce881cfe 100644 --- a/iodata/test/data/water_trajectory_no_model.pdb +++ b/iodata/test/data/water_trajectory_no_model.pdb @@ -1,20 +1,35 @@ HETATM 1 O HOH 1 3.341 0.264 2.538 1.00 0.00 O HETATM 2 H HOH 0 3.391 -0.627 2.172 1.00 0.00 H HETATM 3 H HOH 0 2.864 0.114 3.364 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 END HETATM 1 O HOH 1 0.345 2.460 -3.307 1.00 0.00 O HETATM 2 H HOH 0 -0.015 3.354 -3.352 1.00 0.00 H HETATM 3 H HOH 0 0.165 2.218 -2.391 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 END HETATM 1 O HOH 1 -0.233 -0.790 -3.248 1.00 0.00 O HETATM 2 H HOH 0 0.409 -0.077 -3.354 1.00 0.00 H HETATM 3 H HOH 0 -0.872 -0.395 -2.642 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 END HETATM 1 O HOH 1 1.359 3.030 -0.351 1.00 0.00 O HETATM 2 H HOH 0 1.799 3.355 0.444 1.00 0.00 H HETATM 3 H HOH 0 1.936 3.363 -1.049 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 END HETATM 1 O HOH 1 -1.382 -3.328 -2.737 1.00 0.00 O HETATM 2 H HOH 0 -2.123 -3.355 -3.354 1.00 0.00 H HETATM 3 H HOH 0 -0.627 -3.266 -3.336 1.00 0.00 H +CONECT 1 2 +CONECT 2 1 3 +CONECT 3 2 END diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index 0f8ca9322..827cb3269 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -58,6 +58,7 @@ def check_water(mol): 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_equal(mol.bonds[:, :2], [[0, 1], [1, 2]]) def check_load_dump_consistency(tmpdir, fn): @@ -79,11 +80,19 @@ def check_load_dump_consistency(tmpdir, fn): 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')) - - -@pytest.mark.parametrize("case", ["single", "single_model"]) -def test_load_dump_consistency(case, tmpdir): - with path('iodata.test.data', f'water_{case}.pdb') as fn_pdb: + if mol0.bonds is None: + assert mol1.bonds is None + else: + assert_equal(mol0.bonds, mol1.bonds) + + +@pytest.mark.parametrize("fn_base", [ + "water_single.pdb", + "water_single_model.pdb", + "ch5plus.pdb", +]) +def test_load_dump_consistency(fn_base, tmpdir): + with path('iodata.test.data', fn_base) as fn_pdb: check_load_dump_consistency(tmpdir, fn_pdb) @@ -107,6 +116,8 @@ def check_load_dump_xyz_consistency(tmpdir, fn): # check if resnums are correct resnums = mol1.atffparams.get('resnums') assert_equal(resnums[0], -1) + # There should be no bonds + assert mol1.bonds is None def test_load_dump_xyz_consistency(tmpdir): @@ -180,6 +191,12 @@ def test_load_2bcw(): assert (mol.extra["chainids"] == ["A"] * 65 + ["B"] * 68 + ["C"] * 58).all() +def test_load_ch5plus_bonds(): + with path("iodata.test.data", "ch5plus.pdb") as fn_pdb: + mol = load_one(fn_pdb) + assert_equal(mol.bonds[:, :2], [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5]]) + + def test_load_pdb_dump_pdb(tmpdir): # test dump pdb with single chain with path("iodata.test.data", "2luv.pdb") as fn_pdb: From 56659c681d4dbcc6b37475f7049c7825106a0d5b Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Tue, 30 Mar 2021 03:39:46 -0700 Subject: [PATCH 008/144] Fix commented section in README.rst --- README.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/README.rst b/README.rst index a25b694bb..3279a8d0c 100644 --- a/README.rst +++ b/README.rst @@ -52,7 +52,6 @@ Installation ------------ .. - : To install IOData using the conda package management system, install : `miniconda `__ or : `anaconda `__ first, and then: From c85e546ef5ac2ea4c9ac16cda5462e04571936e7 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Tue, 30 Mar 2021 12:41:34 +0200 Subject: [PATCH 009/144] Use more pytest.mark.parametrize --- iodata/test/test_pdb.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index 827cb3269..ca2f43fd1 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -90,6 +90,8 @@ def check_load_dump_consistency(tmpdir, fn): "water_single.pdb", "water_single_model.pdb", "ch5plus.pdb", + "2luv.pdb", + "2bcw.pdb", ]) def test_load_dump_consistency(fn_base, tmpdir): with path('iodata.test.data', fn_base) as fn_pdb: @@ -195,13 +197,3 @@ def test_load_ch5plus_bonds(): with path("iodata.test.data", "ch5plus.pdb") as fn_pdb: mol = load_one(fn_pdb) assert_equal(mol.bonds[:, :2], [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5]]) - - -def test_load_pdb_dump_pdb(tmpdir): - # test dump pdb with single chain - with path("iodata.test.data", "2luv.pdb") as fn_pdb: - check_load_dump_consistency(tmpdir, fn_pdb) - - # test dump pdb with multiple chain - with path('iodata.test.data', "2bcw.pdb") as fn_pdb: - check_load_dump_consistency(tmpdir, fn_pdb) From 3663461f887a7832bc36c9f4ae5162ad61261ecf Mon Sep 17 00:00:00 2001 From: tovrstra Date: Tue, 30 Mar 2021 13:18:26 +0200 Subject: [PATCH 010/144] Make V2000 test case insensitive --- iodata/formats/sdf.py | 2 +- iodata/test/data/formamide.sdf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index da6fc9fcb..4bd193c88 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -61,7 +61,7 @@ def load_one(lit: LineIterator) -> dict: words = next(lit).split() natom = int(words[0]) nbond = int(words[1]) - if words[-1] != "V2000": + if words[-1].upper() != "V2000": lit.error("Only V2000 SDF files are supported.") atcoords = np.empty((natom, 3), float) atnums = np.empty(natom, int) diff --git a/iodata/test/data/formamide.sdf b/iodata/test/data/formamide.sdf index 79644ba83..784fdbcb7 100644 --- a/iodata/test/data/formamide.sdf +++ b/iodata/test/data/formamide.sdf @@ -1,7 +1,7 @@ 713 -OEChem-03292115373D - 6 5 0 0 0 0 0 0 0999 V2000 + 6 5 0 0 0 0 0 0 0999 v2000 1.1280 0.2091 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0 -1.1878 0.1791 0.0000 N 0 0 0 0 0 0 0 0 0 0 0 0 0.0598 -0.3882 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 From 81a97849703d694cbe33b0911246fd6c6aea9cf6 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Tue, 30 Mar 2021 13:24:28 +0200 Subject: [PATCH 011/144] Add test for error message regarding v2000 format --- iodata/test/data/molv3000.sdf | 257 ++++++++++++++++++++++++++++++++++ iodata/test/test_sdf.py | 9 +- 2 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 iodata/test/data/molv3000.sdf diff --git a/iodata/test/data/molv3000.sdf b/iodata/test/data/molv3000.sdf new file mode 100644 index 000000000..5370aa619 --- /dev/null +++ b/iodata/test/data/molv3000.sdf @@ -0,0 +1,257 @@ +levobupivacaine + -INDIGO-05122016302D + + 0 0 0 0 0 999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 21 22 0 0 0 +M V30 BEGIN ATOM +M V30 1 C -0.0038 -5.9956 0 0 +M V30 2 N -0.0038 -4.4556 0 0 +M V30 3 O -1.3375 -6.7637 0 0 +M V30 4 C 1.3298 -6.7637 0 0 CFG=1 +M V30 5 C -1.3336 -3.6837 0 0 +M V30 6 N 1.3298 -8.3037 0 0 +M V30 7 C 2.6634 -5.9956 0 0 +M V30 8 C -1.3336 -2.1437 0 0 +M V30 9 C -2.6711 -4.4556 0 0 +M V30 10 C -0.0038 -9.0756 0 0 +M V30 11 C 2.6634 -9.0756 0 0 +M V30 12 C 4.0009 -6.7637 0 0 +M V30 13 C -0.0038 -1.3757 0 0 +M V30 14 C -2.6711 -1.3757 0 0 +M V30 15 C -2.6711 -5.9956 0 0 +M V30 16 C -4.0009 -3.6837 0 0 +M V30 17 C -1.3375 -8.3037 0 0 +M V30 18 C 4.0009 -8.3037 0 0 +M V30 19 C -4.0009 -2.1437 0 0 +M V30 20 C -2.6711 -9.0756 0 0 +M V30 21 C -4.0047 -8.3037 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 2 1 3 +M V30 3 1 2 5 +M V30 4 1 4 1 CFG=3 +M V30 5 1 4 6 +M V30 6 1 4 7 +M V30 7 2 5 8 +M V30 8 1 5 9 +M V30 9 1 6 10 +M V30 10 1 6 11 +M V30 11 1 7 12 +M V30 12 1 8 13 +M V30 13 1 8 14 +M V30 14 1 9 15 +M V30 15 2 9 16 +M V30 16 1 10 17 +M V30 17 1 11 18 +M V30 18 1 12 18 +M V30 19 2 14 19 +M V30 20 1 16 19 +M V30 21 1 17 20 +M V30 22 1 20 21 +M V30 END BOND +M V30 END CTAB +M END +> +4 + +> +levobupivacaine + +> +27262-47-1 + +> +levobupivacaine +chirocain +levobupivacaine hydrochloride +levobupivacaine HCl + +> +http://drugcentral.org/drugcard/4/view + +$$$$ +(S)-nicardipine + -INDIGO-05122016302D + + 0 0 0 0 0 999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 35 37 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 1.9985 -5.9956 0 0 +M V30 2 O 0.6649 -6.7637 0 0 +M V30 3 O 1.9985 -4.4556 0 0 +M V30 4 C 3.3322 -6.7637 0 0 +M V30 5 C -0.6687 -5.9956 0 0 +M V30 6 C 3.3322 -8.3036 0 0 CFG=2 +M V30 7 C 4.6696 -5.9956 0 0 +M V30 8 C -0.6687 -4.4556 0 0 +M V30 9 C 4.6696 -9.0756 0 0 +M V30 10 C 1.9985 -9.0756 0 0 +M V30 11 C 4.6696 -4.4556 0 0 +M V30 12 N 5.9994 -6.7637 0 0 +M V30 13 N -2.0024 -3.6837 0 0 +M V30 14 C 4.6696 -10.6155 0 0 +M V30 15 C 5.9994 -8.3036 0 0 +M V30 16 C 0.6649 -8.3036 0 0 +M V30 17 C 1.9985 -10.6155 0 0 +M V30 18 C -3.336 -4.4556 0 0 +M V30 19 C -2.0024 -2.1437 0 0 +M V30 20 O 5.9994 -11.3836 0 0 +M V30 21 O 3.3322 -11.3836 0 0 +M V30 22 C 7.333 -9.0756 0 0 +M V30 23 C -0.6687 -9.0756 0 0 +M V30 24 C 0.6649 -11.3836 0 0 +M V30 25 C -4.6658 -3.6837 0 0 +M V30 26 C 7.333 -10.6155 0 0 +M V30 27 N -2.0024 -8.3036 0 0 CHG=1 +M V30 28 C -0.6687 -10.6155 0 0 +M V30 29 C -4.6658 -2.1437 0 0 +M V30 30 C -5.9994 -4.4556 0 0 +M V30 31 O -2.0024 -6.7637 0 0 +M V30 32 O -3.336 -9.0756 0 0 CHG=-1 +M V30 33 C -5.9994 -1.3757 0 0 +M V30 34 C -7.333 -3.6837 0 0 +M V30 35 C -7.333 -2.1437 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 2 1 3 +M V30 3 1 1 4 +M V30 4 1 2 5 +M V30 5 1 6 4 +M V30 6 2 4 7 +M V30 7 1 5 8 +M V30 8 1 6 9 +M V30 9 1 6 10 CFG=3 +M V30 10 1 7 11 +M V30 11 1 7 12 +M V30 12 1 8 13 +M V30 13 1 9 14 +M V30 14 2 9 15 +M V30 15 2 10 16 +M V30 16 1 10 17 +M V30 17 1 12 15 +M V30 18 1 13 18 +M V30 19 1 13 19 +M V30 20 1 14 20 +M V30 21 2 14 21 +M V30 22 1 15 22 +M V30 23 1 16 23 +M V30 24 2 17 24 +M V30 25 1 18 25 +M V30 26 1 20 26 +M V30 27 1 23 27 +M V30 28 2 23 28 +M V30 29 1 24 28 +M V30 30 2 25 29 +M V30 31 1 25 30 +M V30 32 2 27 31 +M V30 33 1 27 32 +M V30 34 1 29 33 +M V30 35 2 30 34 +M V30 36 2 33 35 +M V30 37 1 34 35 +M V30 END BOND +M V30 END CTAB +M END +> +5 + +> +(S)-nicardipine + +> +76093-36-2 + +> +(S)-nicardipine +(-)-Nicardipine + +> +http://drugcentral.org/drugcard/5/view + +$$$$ +(S)-nitrendipine + -INDIGO-05122016302D + + 0 0 0 0 0 999 V3000 +M V30 BEGIN CTAB +M V30 COUNTS 26 27 0 0 0 +M V30 BEGIN ATOM +M V30 1 C 0.0038 -6.7638 0 0 +M V30 2 O 1.3375 -5.9957 0 0 +M V30 3 O 0.0038 -8.3038 0 0 +M V30 4 C -1.3298 -5.9957 0 0 +M V30 5 C 2.6864 -6.7408 0 0 +M V30 6 C -1.3298 -4.4557 0 0 CFG=2 +M V30 7 C -2.6635 -6.7638 0 0 +M V30 8 C 2.6864 -8.2808 0 0 +M V30 9 C -2.6635 -3.6838 0 0 +M V30 10 C 0.0038 -3.6838 0 0 +M V30 11 C -2.6635 -8.3038 0 0 +M V30 12 N -3.9971 -5.9957 0 0 +M V30 13 C -2.6635 -2.1438 0 0 +M V30 14 C -3.9971 -4.4557 0 0 +M V30 15 C 1.3375 -4.4557 0 0 +M V30 16 C 0.0038 -2.1438 0 0 +M V30 17 O -3.9971 -1.3757 0 0 +M V30 18 O -1.3298 -1.3757 0 0 +M V30 19 C -5.3308 -3.6838 0 0 +M V30 20 C 2.6711 -3.6838 0 0 +M V30 21 C 1.3375 -1.3757 0 0 +M V30 22 C -5.3461 -2.1208 0 0 +M V30 23 N 4.0086 -4.4557 0 0 CHG=1 +M V30 24 C 2.6711 -2.1438 0 0 +M V30 25 O 4.0086 -5.9957 0 0 CHG=-1 +M V30 26 O 5.3422 -3.6838 0 0 +M V30 END ATOM +M V30 BEGIN BOND +M V30 1 1 1 2 +M V30 2 2 1 3 +M V30 3 1 1 4 +M V30 4 1 2 5 +M V30 5 1 6 4 +M V30 6 2 4 7 +M V30 7 1 5 8 +M V30 8 1 6 9 +M V30 9 1 6 10 CFG=3 +M V30 10 1 7 11 +M V30 11 1 7 12 +M V30 12 1 9 13 +M V30 13 2 9 14 +M V30 14 2 10 15 +M V30 15 1 10 16 +M V30 16 1 12 14 +M V30 17 1 13 17 +M V30 18 2 13 18 +M V30 19 1 14 19 +M V30 20 1 15 20 +M V30 21 2 16 21 +M V30 22 1 17 22 +M V30 23 1 20 23 +M V30 24 2 20 24 +M V30 25 1 21 24 +M V30 26 1 23 25 +M V30 27 2 23 26 +M V30 END BOND +M V30 END CTAB +M END +> +6 + +> +(S)-nitrendipine + +> +80873-62-7 + +> +(S)-nitrendipine +(-)-Nitrendipine + +> +http://drugcentral.org/drugcard/6/view + +$$$$ diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 08456a9f2..2efe47be5 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -25,7 +25,8 @@ from .common import truncated_file from ..api import load_one, load_many, dump_one, dump_many -from ..utils import angstrom +from ..utils import angstrom, FileFormatError + try: from importlib_resources import path except ImportError: @@ -123,3 +124,9 @@ def test_load_dump_many_consistency(tmpdir): assert_equal(mol0.atnums, mol1.atnums) assert_allclose(mol0.atcoords, mol1.atcoords, atol=1.e-5) assert_equal(mol0.bonds, mol1.bonds) + + +def test_v2000_check(): + with path('iodata.test.data', 'molv3000.sdf') as fn_sdf: + with pytest.raises(FileFormatError): + load_one(fn_sdf) From 6943abf1582e05a64ae9f0e7e09c49461ac44e48 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Thu, 1 Apr 2021 17:18:30 +0200 Subject: [PATCH 012/144] Eliminate SDF2BONDS --- iodata/formats/sdf.py | 14 ++++++-------- iodata/periodic.py | 29 ++++++++++++++++------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index 4bd193c88..6243bcb27 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -37,7 +37,7 @@ 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 ..periodic import sym2num, num2sym from ..utils import angstrom, LineIterator @@ -47,10 +47,6 @@ PATTERNS = ['*.sdf'] -SDF2BOND = {1: "1", 2: "2", 3: "3", 4: "ar", 5: "sd", 6: "sar", 7: "dar", 8: "un"} -BOND2SDF = dict((val, key) for key, val in SDF2BOND.items()) - - @document_load_one("SDF", ['atcoords', 'atnums', 'bonds', 'title']) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" @@ -76,7 +72,10 @@ def load_one(lit: LineIterator) -> dict: words = next(lit).split() bonds[ibond, 0] = int(words[0]) - 1 bonds[ibond, 1] = int(words[1]) - 1 - bonds[ibond, 2] = bond2num[SDF2BOND.get(int(words[2]), "un")] + # Bond types 1 to 8 (inclusive) are defined in the SDF format. + # Anything outside that range is not modified, just not to lose any + # information, but could be potentially meaningless. + bonds[ibond, 2] = int(words[2]) while True: try: words = next(lit) @@ -119,8 +118,7 @@ def dump_one(f: TextIO, data: IOData): 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, - BOND2SDF.get(num2bond[bondtype], 8), + iatom + 1, jatom + 1, bondtype ), file=f) print('$$$$', file=f) diff --git a/iodata/periodic.py b/iodata/periodic.py index 552485c20..ae9f2bb03 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -152,19 +152,22 @@ # Labels used for bond types. num2bond = { - 1: "1", - 2: "2", - 3: "3", - # The following symbols are used in the MOL2 format. - 4: "am", # amide - 5: "ar", # aromatic - 6: "du", # dummy - 7: "un", # unknown or any - 8: "nc", # not connected - # The following are defined in the SDF or chemical table format - 9: "sd", # single or double - 10: "sar", # single or aromatic - 11: "dar", # double or aromatic + # The following symbols are used in the SDF format. The numering is assumed + # to be consistent with the SDF spec. See + # http://www.nonlinear.com/progenesis/sdf-studio/v0.9/faq/sdf-file-format-guidance.aspx + # https://en.wikipedia.org/wiki/Chemical_table_file + 1: "1", # single + 2: "2", # double + 3: "3", # triple + 4: "ar", # aromatic + 5: "sd", # single or double + 6: "sar", # single or aromatic + 7: "dar", # double or aromatic + 8: "un", # unknown/any + # The following symbols are exclusively used in the MOL2 format. + 9: "am", # amide + 10: "du", # dummy + 11: "nc", # not connected } bond2num: Dict[str, int] = dict((value, key) for key, value in num2bond.items()) From 69fe3c4bc64a116189fac77574036d4d5059c496 Mon Sep 17 00:00:00 2001 From: Ali-Tehrani Date: Sat, 3 Apr 2021 07:52:20 +0200 Subject: [PATCH 013/144] Cleanups in iodata.basis --- iodata/basis.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/iodata/basis.py b/iodata/basis.py index 26667121e..92f4c90e7 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -16,7 +16,13 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see # -- -"""Utility functions for working with basis sets.""" +"""Utility functions for working with basis sets. + +Notes +----- +Basis set conventions and terminology are documented in :ref:`basis_conventions`. + +""" from functools import wraps from numbers import Integral @@ -119,7 +125,7 @@ class Shell: coeffs: np.ndarray = attr.ib(validator=validate_shape(("exponents", 0), ("kinds", 0))) @property - def nbasis(self) -> int: # noqa: D401 + 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): @@ -132,12 +138,12 @@ def nbasis(self) -> int: # noqa: D401 return result @property - def nprim(self) -> int: # noqa: D401 + def nprim(self) -> int: # noqa: D401 """Number of primitives, also known as the contraction length.""" return len(self.exponents) @property - def ncon(self) -> int: # noqa: D401 + def ncon(self) -> int: # noqa: D401 """Number of contractions. This is usually 1; e.g., it would be 2 for an SP shell.""" return len(self.angmoms) @@ -197,7 +203,7 @@ class MolecularBasis: primitive_normalization: str @property - def nbasis(self) -> int: # noqa: D401 + def nbasis(self) -> int: # noqa: D401 """Number of basis functions.""" return sum(shell.nbasis for shell in self.shells) From 6afbc34440d9b5958db223e7e3e1894e662510bd Mon Sep 17 00:00:00 2001 From: William Adams Date: Tue, 23 Mar 2021 13:45:32 -0400 Subject: [PATCH 014/144] Add QCSchema Input load_one --- iodata/formats/json.py | 280 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 262 insertions(+), 18 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 2775e3de3..3bc014f73 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -20,10 +20,10 @@ """QCSchema JSON file format. QCSchema defines four different subschema: -* Molecule - specifying a molecular system -* Input - specifying QC program input for a specific Molecule -* Output - specifying QC program output for a specific Molecule -* Basis - specifying a basis set for a specific Molecule +- Molecule: specifying a molecular system +- Input: specifying QC program input for a specific Molecule +- Output: specifying QC program output for a specific Molecule +- Basis: specifying a basis set for a specific Molecule The QCSchema subschema are in various levels of maturity, and are subject to change at any time without warning, as they are also used as the internal data representation for the QCElemental @@ -164,7 +164,7 @@ def _load_qcschema_molecule(result: dict, lit: LineIterator) -> dict: Parameters ---------- result - The JSON dict loaded from file, same as the `molecule` key in QCSchema input/output files. + The JSON dict loaded from file. lit The line iterator holding the file data. @@ -198,7 +198,8 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: Parameters ---------- mol - The 'molecule' key from the QCSchema file. + The 'molecule' key from the QCSchema input or output file, or the full result for a QCSchema + Molecule file. lit The line iterator holding the file data. @@ -410,7 +411,24 @@ def _load_qcschema_basis(result: dict, lit: LineIterator) -> dict: raise NotImplementedError("qcschema_basis is not yet implemented in IOData.") -# pylint: disable=unused-argument +def _parse_basis_keys(basis: dict, lit: LineIterator) -> dict: + """Parse basis keys for a QCSchema input, output, or basis file. + + Parameters + ---------- + basis + The basis dictionary from a QCSchema basis file or QCSchema input or output 'method' key. + lit + The line iterator holding the file data. + + Returns + ------- + basis_dict + Dictionary containing ... + """ + raise NotImplementedError("") + + def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: """Load qcschema_input properties. @@ -426,9 +444,242 @@ def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: input_dict ... """ - # basis_dict = dict() - # return basis_dict - raise NotImplementedError("qcschema_input is not yet implemented in IOData.") + extra_dict = dict() + input_dict = _parse_input_keys(result, lit) + extra_dict["input"] = input_dict["extra"] + + if "molecule" not in result: + raise FileFormatError("{}: QCSchema Input requires 'molecule' key".format(lit.filename)) + molecule_dict = _parse_topology_keys(result["molecule"], lit) + input_dict.update(molecule_dict) + extra_dict["molecule"] = molecule_dict["extra"] + input_dict["extra"] = extra_dict + input_dict["extra"]["schema_name"] = "qcschema_input" + + return input_dict + + +def _parse_input_keys(result: dict, lit: LineIterator) -> dict: + """Parse input keys for QCSchema input or output files. + + Parameters + ---------- + result + The JSON dict loaded from file. + lit + The line iterator holding the file data. + + Returns + ------- + input_dict + Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. + It may contain ``obasis`` and ``extra`` keys and corresponding values as well. + + """ + # QCEngineRecords input files don't actually specify a name or version + should_be_required_keys = {"schema_name", "schema_version"} + input_keys = {"molecule", "driver", "model"} + for key in should_be_required_keys: + if key not in result: + warn( + "{}: QCSchema files should have a '{}' key.".format(lit.filename, key), + FileFormatWarning, + 2, + ) + for key in input_keys: + if key not in result: + raise FileFormatError( + "{}: QCSchema `qcschema_input` file requires '{}' key".format(lit.filename, key) + ) + # Store all extra keys in extra_dict and gather at end + input_dict = dict() + extra_dict = dict() + + # Save schema name & version + extra_dict["schema_name"] = "qcschema_input" + try: + version = result["schema_version"] + except KeyError: + version = -1 + if float(version) < 1.0: + warn( + "{}: Unknown `qcschema_input` version {}, loading may produce invalid results".format( + lit.filename, version + ), + FileFormatWarning, + 2, + ) + extra_dict["schema_version"] = version + + # Load driver + extra_dict["driver"] = _parse_driver(result["driver"], lit) + + # Load model & call basis helper if needed + model = _parse_model(result["model"], lit) + input_dict.update(model) + extra_dict["model"] = model["extra"] + + # Load keywords & store + # Currently, only the IOData run_type attribute is specifically parsed from keywords, but this + # is a good space for passing additional IOData-specific keywords, given that the official spec + # treats this as program-specific territory. + if "keywords" in result: + keywords_dict = result["keywords"] + if "run_type" in keywords_dict: + input_dict["run_type"] = keywords_dict["run_type"] + extra_dict["keywords"] = keywords_dict + # Check for extras + if "extras" in result: + extra_dict["extras"] = result["extras"] + # Check for ID + if "id" in result: + extra_dict["id"] = result["id"] + # Load protocols + if "protocols" in result: + extra_dict["protocols"] = _parse_protocols(result["protocols"], lit) + # Check for provenance + if "provenance" in result: + extra_dict["provenance"] = _parse_provenance(result["provenance"], lit, "qcschema_input") + + input_dict["extra"] = extra_dict + return input_dict + + +def _parse_driver(driver: str, lit: LineIterator) -> str: + """Load driver properties from QCSchema. + + Parameters + ---------- + driver + The `driver` key from the QCSchema input. + lit + The line iterator holding the file data. + + Returns + ------- + driver_dict + The driver for the QCSchema file, specifying what type of calculation is being performed. + + Raises + ------ + FileFormatError + If driver is not one of {"energy", "gradient", "hessian", "properties"}. + + Notes + ----- + This keyword is similar to, but not really interchangeable with, the `run_type` IOData + attribute. In order to specify the `run_type`, add it to the `keywords` dictionary. + + """ + if driver not in ["energy", "gradient", "hessian", "properties"]: + raise FileFormatError( + "{}: QCSchema driver must be one of `energy`, `gradient`, `hessian`, " + "or `properties`".format( + lit.filename + ) + ) + else: + return driver + + +def _parse_model(model: dict, lit: LineIterator) -> dict: + """Load model properties from QCSchema. + + Parameters + ---------- + model + The dictionary corresponding to the 'model' key for a QCSchema input or output file. + lit + The line iterator holding the file data. + + Returns + ------- + model_dict + Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. + It may contain ``obasis`` key and corresponding values as well. + + """ + model_dict = dict() + extra_dict = dict() + + if "method" not in model: + raise FileFormatError("{}: QCSchema `model` requires a `method`".format(lit.filename)) + else: + model_dict["lot"] = model["method"] + # QCEngineRecords doesn't give an empty string for basis-free methods, omits req'd key instead + if "basis" not in model: + warn( + "{}: Model `basis` key should be given. Assuming basis-free method.".format( + lit.filename + ) + ) + elif isinstance(model["basis"], str): + if model["basis"] == "": + warn( + "{}: QCSchema `basis` could not be read and will be omitted." + "Unless model is for a basis-free method, check input file.".format(lit.filename), + FileFormatWarning, + 2, + ) + else: + model_dict["obasis_name"] = model["basis"] + elif isinstance(model["basis"], dict): + basis = _parse_basis_keys(model["basis"], lit) + model_dict.update(basis) + extra_dict["basis"] = basis["extra"] + pass + + model_dict["extra"] = extra_dict + return model_dict + + +def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: + """Load protocols properties from QCSchema. + + Parameters + ---------- + protocols + Protocols key from a QCSchema input or output file. + lit + The line iterator holding the file data. + + Returns + ------- + protocols_dict + Protocols dictionary containing instructions for the manipulation of output generated from + this input. + + """ + if "wavefunction" not in protocols: + warn( + "{}: Protocols `wavefunction` key not specified, no properties will be kept.", + FileFormatWarning, + 2 + ) + wavefunction = "none" + else: + wavefunction = protocols["wavefunction"] + if "stdout" not in protocols: + warn( + "{}: Protocols `stdout` key not specified, stdout will be kept.", + FileFormatWarning, + 2 + ) + keep_stdout = True + else: + keep_stdout = protocols["stdout"] + protocols_dict = dict() + if wavefunction not in {"all", "orbitals_and_eigenvalues", "return_results", "none"}: + raise FileFormatError( + "{}: Invalid `protocols` `wavefunction` keyword.".format(lit.filename) + ) + else: + protocols_dict["keep_wavefunction"] = wavefunction + if not isinstance(keep_stdout, bool): + raise FileFormatError("{}: `protocols` `stdout` option must be a boolean.") + else: + protocols_dict["keep_stdout"] = keep_stdout + return protocols_dict # pylint: disable=unused-argument @@ -613,12 +864,5 @@ def _dump_qcschema_molecule(data: IOData) -> dict: if "unparsed" in data.extra: for k in data.extra["unparsed"]: molecule_dict[k] = data.extra["unparsed"][k] - # print(molecule_dict) - # for k,v in molecule_dict.items(): - # if isinstance(v, list): - # types = "{}[{}]".format(type(v), type(v[0])) - # else: - # types = type(v) - # print("{}: {} | {}".format(k, v, types)) - # print(type(molecule_dict["connectivity"][0][0])) + return molecule_dict From 3fcef4545180b50b711c561fa893550d547089d6 Mon Sep 17 00:00:00 2001 From: William Adams Date: Wed, 24 Mar 2021 13:52:04 -0400 Subject: [PATCH 015/144] Add QCSchema Input tests --- .../data/H2O_HF_STO3G_Gaussian_input.json | 36 ++++ .../test/data/LiCl_STO4G_Gaussian_input.json | 54 ++++++ .../data/LiCl_STO4G_Gaussian_input_extra.json | 55 ++++++ ...l_STO4G_Gaussian_input_extra_molecule.json | 55 ++++++ ...iCl_STO4G_Gaussian_input_nested_extra.json | 62 +++++++ .../test/data/LiCl_explicit_STO4G_input.json | 165 ++++++++++++++++++ iodata/test/data/LiCl_string_STO4G_input.json | 28 +++ iodata/test/data/water_mp2_input.json | 22 +++ iodata/test/test_json.py | 84 +++++++++ 9 files changed, 561 insertions(+) create mode 100644 iodata/test/data/H2O_HF_STO3G_Gaussian_input.json create mode 100644 iodata/test/data/LiCl_STO4G_Gaussian_input.json create mode 100644 iodata/test/data/LiCl_STO4G_Gaussian_input_extra.json create mode 100644 iodata/test/data/LiCl_STO4G_Gaussian_input_extra_molecule.json create mode 100644 iodata/test/data/LiCl_STO4G_Gaussian_input_nested_extra.json create mode 100644 iodata/test/data/LiCl_explicit_STO4G_input.json create mode 100644 iodata/test/data/LiCl_string_STO4G_input.json create mode 100644 iodata/test/data/water_mp2_input.json diff --git a/iodata/test/data/H2O_HF_STO3G_Gaussian_input.json b/iodata/test/data/H2O_HF_STO3G_Gaussian_input.json new file mode 100644 index 000000000..f5b00852d --- /dev/null +++ b/iodata/test/data/H2O_HF_STO3G_Gaussian_input.json @@ -0,0 +1,36 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["O", "H", "H"], + "geometry": [0.0, 0.0, -0.1295, 0.0, -1.4942, 1.0274, 0.0, 1.4942, 1.0274], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + } + ] + }, + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-3G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "energy" + }, + "provenance": [ + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.com" + } + ] +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_STO4G_Gaussian_input.json b/iodata/test/data/LiCl_STO4G_Gaussian_input.json new file mode 100644 index 000000000..60652f20c --- /dev/null +++ b/iodata/test/data/LiCl_STO4G_Gaussian_input.json @@ -0,0 +1,54 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "id": "LiCl_HF_STO4G", + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "freq", + "geom": "AllCheck", + "mem": "7GB", + "nprocshared": "2", + "oldchk": "LiCl_OPT.chk", + "chk": "LiCl_HF_STO4G.chk" + }, + "protocols": { + "wavefunction": "none", + "stdout": false + }, + "extras": { + "lewis_acid_group": "alkali_metals", + "lewis_base_group": "halides" + }, + "provenance": [ + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.com" + } + ] +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_STO4G_Gaussian_input_extra.json b/iodata/test/data/LiCl_STO4G_Gaussian_input_extra.json new file mode 100644 index 000000000..6e8ef63f6 --- /dev/null +++ b/iodata/test/data/LiCl_STO4G_Gaussian_input_extra.json @@ -0,0 +1,55 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "id": "LiCl_HF_STO4G", + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "freq", + "geom": "AllCheck", + "mem": "7GB", + "nprocshared": "2", + "oldchk": "LiCl_OPT.chk", + "chk": "LiCl_HF_STO4G.chk" + }, + "protocols": { + "wavefunction": "none", + "stdout": false + }, + "extras": { + "lewis_acid_group": "alkali_metals", + "lewis_base_group": "halides" + }, + "provenance": [ + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.com" + } + ], + "another_field": true +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_STO4G_Gaussian_input_extra_molecule.json b/iodata/test/data/LiCl_STO4G_Gaussian_input_extra_molecule.json new file mode 100644 index 000000000..7b1e6ef5d --- /dev/null +++ b/iodata/test/data/LiCl_STO4G_Gaussian_input_extra_molecule.json @@ -0,0 +1,55 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ], + "another_field": true + }, + "id": "LiCl_HF_STO4G", + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "freq", + "geom": "AllCheck", + "mem": "7GB", + "nprocshared": "2", + "oldchk": "LiCl_OPT.chk", + "chk": "LiCl_HF_STO4G.chk" + }, + "protocols": { + "wavefunction": "none", + "stdout": false + }, + "extras": { + "lewis_acid_group": "alkali_metals", + "lewis_base_group": "halides" + }, + "provenance": [ + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.com" + } + ] +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_STO4G_Gaussian_input_nested_extra.json b/iodata/test/data/LiCl_STO4G_Gaussian_input_nested_extra.json new file mode 100644 index 000000000..2575cea93 --- /dev/null +++ b/iodata/test/data/LiCl_STO4G_Gaussian_input_nested_extra.json @@ -0,0 +1,62 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "id": "LiCl_HF_STO4G", + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "freq", + "geom": "AllCheck", + "mem": "7GB", + "nprocshared": "2", + "oldchk": "LiCl_OPT.chk", + "chk": "LiCl_HF_STO4G.chk" + }, + "protocols": { + "wavefunction": "none", + "stdout": false + }, + "extras": { + "lewis_acid_group": "alkali_metals", + "lewis_base_group": "halides" + }, + "provenance": [ + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.com" + } + ], + "related_projects": { + "HSAB": { + "id": "HSAB_2019_LALB" + }, + "4PB3": { + "id": "4PB3_2020_Group1" + } + } +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_explicit_STO4G_input.json b/iodata/test/data/LiCl_explicit_STO4G_input.json new file mode 100644 index 000000000..61f3a07df --- /dev/null +++ b/iodata/test/data/LiCl_explicit_STO4G_input.json @@ -0,0 +1,165 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "driver": "energy", + "model": { + "method": "HF", + "basis": { + "schema_name": "qcschema_basis", + "schema_version": 2.0, + "name": "STO-4G", + "description": "STO-4G Minimal Basis (4 functions/AO)", + "center_data": { + "li_STO-4G": { + "electron_shells": [ + { + "angular_momentum": [ + 0 + ], + "exponents": [ + 0.3774960873E+02, + 0.6907713307E+01, + 0.1919038397E+01, + 0.6369115922E+00 + ], + "coefficients": [ + [ + 0.5675242080E-01, + 0.2601413550E+00, + 0.5328461143E+00, + 0.2916254405E+00 + ] + ], + "function_type": "gto_cartesian" + }, + { + "angular_momentum": [ + 0, + 1 + ], + "exponents": [ + 0.1487042352E+01, + 0.3219127620E+00, + 0.1046660300E+00, + 0.4019868296E-01 + ], + "coefficients": [ + [ + -0.6220714565E-01, + 0.2976804596E-04, + 0.5588549221E+00, + 0.4977673218E+00 + ], + [ + 0.4368434884E-01, + 0.2863793984E+00, + 0.5835753141E+00, + 0.2463134378E+00 + ] + ], + "function_type": "gto_cartesian" + } + ] + }, + "cl_STO-4G": { + "electron_shells": [ + { + "angular_momentum": [ + 0 + ], + "exponents": [ + 0.1408260576E+04, + 0.2576943351E+03, + 0.7159030805E+02, + 0.2376017966E+02 + ], + "coefficients": [ + [ + 0.5675242080E-01, + 0.2601413550E+00, + 0.5328461143E+00, + 0.2916254405E+00 + ] + ], + "function_type": "gto_cartesian" + }, + { + "angular_momentum": [ + 0, + 1 + ], + "exponents": [ + 0.9105253261E+02, + 0.1971091961E+02, + 0.6408766434E+01, + 0.2461390482E+01 + ], + "coefficients": [ + [ + -0.6220714565E-01, + 0.2976804596E-04, + 0.5588549221E+00, + 0.4977673218E+00 + ], + [ + 0.4368434884E-01, + 0.2863793984E+00, + 0.5835753141E+00, + 0.2463134378E+00 + ] + ], + "function_type": "gto_cartesian" + }, + { + "angular_momentum": [ + 0, + 1 + ], + "exponents": [ + 0.4064728946E+01, + 0.1117815984E+01, + 0.4399626124E+00, + 0.1958035957E+00 + ], + "coefficients": [ + [ + -0.8529019644E-01, + -0.2132074034E+00, + 0.5920843928E+00, + 0.6115584746E+00 + ], + [ + -0.2504945181E-01, + 0.1686604461E+00, + 0.6409553151E+00, + 0.2779508957E+00 + ] + ], + "function_type": "gto_cartesian" + } + ] + } + }, + "atom_map": ["li_STO-4G", "cl_STO-4G"] + } + } +} \ No newline at end of file diff --git a/iodata/test/data/LiCl_string_STO4G_input.json b/iodata/test/data/LiCl_string_STO4G_input.json new file mode 100644 index 000000000..9d3fda82f --- /dev/null +++ b/iodata/test/data/LiCl_string_STO4G_input.json @@ -0,0 +1,28 @@ +{ + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "driver": "energy", + "model": { + "method": "B3LYP", + "basis": "Def2TZVP" + } +} \ No newline at end of file diff --git a/iodata/test/data/water_mp2_input.json b/iodata/test/data/water_mp2_input.json new file mode 100644 index 000000000..04701836c --- /dev/null +++ b/iodata/test/data/water_mp2_input.json @@ -0,0 +1,22 @@ +{ + "schema_name": "qc_schema_input", + "schema_version": 1, + "molecule": { + "geometry": [ + 0.0, 0.0, -0.1294, + 0.0, -1.4941, 1.0274, + 0.0, 1.4941, 1.0274 + ], + "symbols": ["O", "H", "H"] + }, + "driver": "energy", + "model": { + "method": "MP2", + "basis": "cc-pVDZ" + }, + "keywords": {}, + "provenance": { + "creator": "HORTON3", + "routine": "Copied from QCSchema docs' Water MP2 example" + } +} \ No newline at end of file diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 54ada5685..f3d7b1de7 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -46,6 +46,8 @@ [4.253724, -2.762010, 0.382764], ] ), + "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]]) } # These molecule examples were manually generated for testing # MOL_FILES: (filename, atnums, charge, spinpol, geometry) @@ -221,3 +223,85 @@ def test_ghost(tmpdir): with open(fn_tmp, "r") as mol2_in: mol2 = json.load(mol2_in) assert mol2["real"] == [True] * 3 + [False] * 6 + + +# input_files: (filename, explicit_basis, lot, obasis_name, run_type, geometry) +INPUT_FILES = [ + ("H2O_HF_STO3G_Gaussian_input.json", False, "HF", "STO-3G", "energy", GEOMS["H2O"]), + ("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"]) +] + + +@pytest.mark.parametrize( + "filename, explicit_basis, lot, obasis_name, run_type, geometry", INPUT_FILES +) +def test_qcschema_input(filename, explicit_basis, lot, obasis_name, run_type, geometry): + with path('iodata.test.data', filename) as qcschema_input: + try: + mol = load_one(str(qcschema_input)) + assert mol.lot == lot + if obasis_name: + assert mol.obasis_name == obasis_name + if run_type: + assert mol.run_type == run_type + np.testing.assert_allclose(mol.atcoords, geometry) + # This will change if QCSchema Basis gets supported + except NotImplementedError: + assert explicit_basis + + +# Test passthrough for input files using modified versions of CuSCN_molecule.json +# PASSTHROUGH_INPUT_FILES: {filename, unparsed_dict, location} +PASSTHROUGH_INPUT_FILES = [ + ("LiCl_STO4G_Gaussian_input_extra.json", UNPARSED["extra"], "input"), + ("LiCl_STO4G_Gaussian_input_nested_extra.json", UNPARSED["nested_extra"], "input"), + ("LiCl_STO4G_Gaussian_input_extra_molecule.json", UNPARSED["extra"], "molecule"), +] + + +@pytest.mark.parametrize("filename, unparsed_dict, location", PASSTHROUGH_INPUT_FILES) +def test_passthrough_qcschema_input(filename, unparsed_dict, location): + """Test qcschema_molecule parsing for passthrough of unparsed keys.""" + with path("iodata.test.data", filename) as qcschema_input: + mol = load_one(str(qcschema_input)) + + assert mol.extra[location]["unparsed"] == unparsed_dict + + +INOUT_INPUT_FILES = [ + ("H2O_HF_STO3G_Gaussian_input.json", 0), + ("LiCl_string_STO4G_input.json", 0), + ("LiCl_STO4G_Gaussian_input.json", 0), + ("LiCl_STO4G_Gaussian_input_extra.json", 0), + ("LiCl_STO4G_Gaussian_input_nested_extra.json", 0), + ("LiCl_STO4G_Gaussian_input_extra_molecule.json", 0), +] + + +@pytest.mark.parametrize("filename, nwarn", INOUT_INPUT_FILES) +def test_inout_qcschema_input(tmpdir, filename, nwarn): + """Test that loading and dumping qcschema_molecule files retains all data.""" + with path("iodata.test.data", filename) as qcschema_input: + if nwarn == 0: + mol = load_one(str(qcschema_input)) + else: + with pytest.warns(FileFormatWarning) as record: + mol = load_one(str(qcschema_input)) + assert len(record) == nwarn + mol1 = json.loads(qcschema_input.read_bytes()) + + fn_tmp = os.path.join(tmpdir, 'test_input_mol.json') + dump_one(mol, fn_tmp) + + with open(fn_tmp, "r") as mol2_in: + mol2 = json.load(mol2_in) + + # IOData prints the most recent version, and it's not worth updating all test files each time + if "provenance" in mol1: + del mol1["provenance"] + if "provenance" in mol2: + del mol2["provenance"] + assert mol1 == mol2 From faafa4b8be27d5c3d3b281e02fd6e057149e203f Mon Sep 17 00:00:00 2001 From: William Adams Date: Wed, 31 Mar 2021 16:48:16 -0400 Subject: [PATCH 016/144] Correct for qc_schema vs qcschema in schema name --- iodata/formats/json.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 3bc014f73..2e8aacb02 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -101,7 +101,9 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: # Determine schema type if "schema_name" in result: - if result["schema_name"] not in { + # Correct for qc_schema vs. qcschema, due to inconsistencies in prior versions + schema_name = result["schema_name"].replace("qc_schema", "qcschema") + if schema_name not in { "qcschema_molecule", "qcschema_basis", "qcschema_input", @@ -134,8 +136,6 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: schema_name = "qcschema_input" else: raise FileFormatError("{}: Could not determine `schema_name`.".format(lit.filename)) - else: - schema_name = result["schema_name"] if "schema_version" not in result: warn( "{}: QCSchema files should have a `schema_version` key." From 2d442e6fd69deaf0a0858754eeafa8933f20b084 Mon Sep 17 00:00:00 2001 From: William Adams Date: Mon, 5 Apr 2021 15:07:35 -0400 Subject: [PATCH 017/144] Add helper function for detecting unparsed keys --- iodata/formats/json.py | 53 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 2e8aacb02..0c101729d 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -381,15 +381,41 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "id", "extras", } - parsed_keys = molecule_keys.intersection(mol.keys()) - for key in parsed_keys: - del mol[key] - if len(mol) > 0: - topology_dict["extra"]["unparsed"] = mol + passthrough_keys = _find_passthrough_keys(mol, molecule_keys) + if passthrough_keys: + topology_dict["extra"]["unparsed"] = passthrough_keys return topology_dict +def _find_passthrough_keys(result: dict, keys: set) -> dict: + """Find all keys not specified for a given schema. + + Parameters + ---------- + result + The JSON dict loaded from file. + keys + The set of expected keys for a given schema type. + + Returns + ------- + passthrough_dict + All unparsed keys remaining in the parsed dict. + """ + # Avoid altering original dict + result = result.copy() + + passthrough_dict = dict() + parsed_keys = keys.intersection(result.keys()) + for key in parsed_keys: + del result[key] + if len(result) > 0: + passthrough_dict = result + + return passthrough_dict + + # pylint: disable=unused-argument def _load_qcschema_basis(result: dict, lit: LineIterator) -> dict: """Load qcschema_basis properties. @@ -542,6 +568,23 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: extra_dict["provenance"] = _parse_provenance(result["provenance"], lit, "qcschema_input") input_dict["extra"] = extra_dict + + input_keys = { + "schema_name", + "schema_version", + "molecule", + "driver", + "model", + "extras", + "id", + "keywords", + "protocols", + "provenance", + } + passthrough_keys = _find_passthrough_keys(result, input_keys) + if passthrough_keys: + input_dict["extra"]["unparsed"] = passthrough_keys + return input_dict From c814d7ad8ea05201c1f1527fb825411d7f117584 Mon Sep 17 00:00:00 2001 From: William Adams Date: Mon, 5 Apr 2021 16:02:06 -0400 Subject: [PATCH 018/144] Store qcschema_molecule under molecule key in extra dict This change aligns the QCSchema Molecule load_one with the behaviour of the Input (and later, Output) load_one functions, by storing the molecule data under the 'molecule' keyword in the extra dict. The `schema_name` key remains at the top level of the extra dict. --- iodata/formats/json.py | 74 ++++++++++++++++++++++------------------ iodata/test/test_json.py | 2 +- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 0c101729d..9f1da262d 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -180,6 +180,11 @@ def _load_qcschema_molecule(result: dict, lit: LineIterator) -> dict: # All Topology properties are found in the "molecule" key molecule_dict = _parse_topology_keys(result, lit) + # Move extra keys to molecule dict, for consistency with input/output + extra_dict = {"molecule": molecule_dict["extra"]} + molecule_dict["extra"] = extra_dict + molecule_dict["extra"]["schema_name"] = "qcschema_molecule" + return molecule_dict @@ -864,48 +869,49 @@ def _dump_qcschema_molecule(data: IOData) -> dict: molecule_dict["fix_symmetry"] = data.g_rot # Check for other QCSchema keys from IOData extra dict - if "qcel_validated" in data.extra: - molecule_dict["validated"] = data.extra["qcel_validated"] - if "identifiers" in data.extra: - molecule_dict["identifiers"] = data.extra["identifiers"] - if "comment" in data.extra: - molecule_dict["comment"] = data.extra["comment"] - if "atom_labels" in data.extra: - molecule_dict["atom_labels"] = data.extra["atom_labels"] - if "atomic_numbers" in data.extra: - molecule_dict["atomic_numbers"] = data.extra["atomic_numbers"].tolist() - if "masses" in data.extra: - molecule_dict["masses"] = data.extra["masses"].tolist() - if "mass_numbers" in data.extra: - molecule_dict["mass_numbers"] = data.extra["mass_numbers"].tolist() - if "fragments" in data.extra: - if "indices" in data.extra["fragments"]: + if "qcel_validated" in data.extra["molecule"]: + molecule_dict["validated"] = data.extra["molecule"]["qcel_validated"] + if "identifiers" in data.extra["molecule"]: + molecule_dict["identifiers"] = data.extra["molecule"]["identifiers"] + if "comment" in data.extra["molecule"]: + molecule_dict["comment"] = data.extra["molecule"]["comment"] + if "atom_labels" in data.extra["molecule"]: + molecule_dict["atom_labels"] = data.extra["molecule"]["atom_labels"] + if "atomic_numbers" in data.extra["molecule"]: + molecule_dict["atomic_numbers"] = data.extra["molecule"]["atomic_numbers"].tolist() + if "masses" in data.extra["molecule"]: + molecule_dict["masses"] = data.extra["molecule"]["masses"].tolist() + if "mass_numbers" in data.extra["molecule"]: + molecule_dict["mass_numbers"] = data.extra["molecule"]["mass_numbers"].tolist() + if "fragments" in data.extra["molecule"]: + if "indices" in data.extra["molecule"]["fragments"]: molecule_dict["fragments"] = [ - fragment.tolist() for fragment in data.extra["fragments"]["indices"] + fragment.tolist() for fragment in data.extra["molecule"]["fragments"]["indices"] ] - if "indices" in data.extra["fragments"]: - molecule_dict["fragment_charges"] = data.extra["fragments"]["charges"].tolist() - if "indices" in data.extra["fragments"]: + if "indices" in data.extra["molecule"]["fragments"]: + molecule_dict["fragment_charges"] = \ + data.extra["molecule"]["fragments"]["charges"].tolist() + if "indices" in data.extra["molecule"]["fragments"]: molecule_dict["fragment_multiplicities"] = \ - data.extra["fragments"]["multiplicities"].tolist() - if "fix_com" in data.extra: - molecule_dict["fix_com"] = data.extra["fix_com"] - if "fix_orientation" in data.extra: - molecule_dict["fix_orientation"] = data.extra["fix_orientation"] - if "provenance" in data.extra: - molecule_dict["provenance"] = data.extra["provenance"] + 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"]: + molecule_dict["fix_orientation"] = data.extra["molecule"]["fix_orientation"] + if "provenance" in data.extra["molecule"]: + molecule_dict["provenance"] = data.extra["molecule"]["provenance"] else: molecule_dict["provenance"] = { "creator": "IOData", "version": __version__, "routine": "iodata.formats.json", } - if "id" in data.extra: - molecule_dict["id"] = data.extra["id"] - if "extras" in data.extra: - molecule_dict["extras"] = data.extra["extras"] - if "unparsed" in data.extra: - for k in data.extra["unparsed"]: - molecule_dict[k] = data.extra["unparsed"][k] + if "id" in data.extra["molecule"]: + molecule_dict["id"] = data.extra["molecule"]["id"] + if "extras" in data.extra["molecule"]: + molecule_dict["extras"] = data.extra["molecule"]["extras"] + if "unparsed" in data.extra["molecule"]: + for k in data.extra["molecule"]["unparsed"]: + molecule_dict[k] = data.extra["molecule"]["unparsed"][k] return molecule_dict diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index f3d7b1de7..0ac030973 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -140,7 +140,7 @@ def test_passthrough_qcschema_molecule(filename, unparsed_dict): with pytest.warns(FileFormatWarning) as record: mol = load_one(str(qcschema_molecule)) - assert mol.extra["unparsed"] == unparsed_dict + assert mol.extra["molecule"]["unparsed"] == unparsed_dict assert len(record) == 1 From a116556ac6626c50f3edf29b4c3eb32b0da792c8 Mon Sep 17 00:00:00 2001 From: William Adams Date: Mon, 5 Apr 2021 17:03:09 -0400 Subject: [PATCH 019/144] Fix typos --- iodata/formats/json.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 9f1da262d..178e7ed6b 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -808,9 +808,8 @@ def dump_one(f: TextIO, data: IOData): raise NotImplementedError("{} not yet implemented in IOData.".format(schema_name)) # return_dict = _dump_qcschema_basis(data) elif schema_name == "qcschema_input": - raise NotImplementedError("{} not yet implemented in IOData.".format(schema_name)) - # return_dict = _dump_qcschema_input(data) - elif schema_name == "qcschema_input": + return_dict = _dump_qcschema_input(data) + elif schema_name == "qcschema_output": raise NotImplementedError("{} not yet implemented in IOData.".format(schema_name)) # return_dict = _dump_qcschema_output(data) else: @@ -835,7 +834,7 @@ def _dump_qcschema_molecule(data: IOData) -> dict: The dict that will produce the QCSchema JSON file. """ - molecule_dict = {"schema_name": "qcschema_molecule", "schema_version": 2} + molecule_dict = {"schema_name": "qcschema_molecule", "schema_version": 2.0} # Gather required field data if data.atnums is None or data.atcoords is None: From 1dcc9441d2d6e1091de7ca4119a5a2a0aabadcb8 Mon Sep 17 00:00:00 2001 From: William Adams Date: Mon, 5 Apr 2021 17:04:21 -0400 Subject: [PATCH 020/144] Add QCSchema Input dump_one --- iodata/formats/json.py | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 178e7ed6b..b6ecd0852 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -914,3 +914,74 @@ def _dump_qcschema_molecule(data: IOData) -> dict: molecule_dict[k] = data.extra["molecule"]["unparsed"][k] return molecule_dict + + +def _dump_qcschema_input(data): + """Dump relevant attributes from IOData to qcschema_input. + + Using this function requires keywords to be stored in two locations in the `extra` dict: + a `molecule` dict for the QCSchema Molecule extra keys, and an `input` dict for the QCSchema + Input extra keys. + + Parameters + ---------- + data + The IOData instance to dump to file. + + Returns + ------- + input_dict + The dict that will produce the QCSchema JSON file. + + """ + input_dict = {"schema_name": "qcschema_input", "schema_version": 2.0} + + # Gather required field data + input_dict["molecule"] = _dump_qcschema_molecule(data) + if "driver" not in data.extra["input"]: + raise FileFormatError("qcschema_input requires `driver` field in extra['input'].") + if data.extra["input"]["driver"] not in {"energy", "gradient", "hessian", "properties"}: + raise FileFormatError( + "QCSchema driver must be one of `energy`, `gradient`, `hessian`, or `properties`" + ) + input_dict["driver"] = data.extra["input"]["driver"] + if "model" not in data.extra["input"]: + raise FileFormatError("qcschema_input requires `model` field in extra['input'].") + input_dict["model"] = dict() + if data.lot is None: + raise FileFormatError("qcschema_input requires specifed `lot`.") + input_dict["model"]["method"] = data.lot + if data.obasis_name is None and "basis" not in data.extra["input"]["model"]: + warn( + "No basis name given. QCSchema assumes this signifies a basis-free method; to" + "avoid this warning, specify `obasis_name` as an empty string.", + FileFormatWarning, + 2, + ) + if "basis" in data.extra["input"]["model"]: + raise NotImplementedError("qcschema_basis is not yet supported in IOData.") + input_dict["model"]["basis"] = data.obasis_name + if "keywords" in data.extra["input"]: + input_dict["keywords"] = data.extra["input"]["keywords"] + if "extras" in data.extra["input"]: + input_dict["extras"] = data.extra["input"]["extras"] + if "id" in data.extra["input"]: + input_dict["id"] = data.extra["input"]["id"] + if "protocols" in data.extra["input"]: + input_dict["protocols"] = dict() + # Remove 'keep_' from protocols keys (added in IOData for readability) + for keep in data.extra["input"]["protocols"]: + input_dict["protocols"][keep[5:]] = data.extra["input"]["protocols"][keep] + if "provenance" in data.extra["input"]: + input_dict["provenance"] = data.extra["input"]["provenance"] + else: + input_dict["provenance"] = { + "creator": "IOData", + "version": __version__, + "routine": "iodata.formats.json", + } + if "unparsed" in data.extra["input"]: + for k in data.extra["input"]["unparsed"]: + input_dict[k] = data.extra["input"]["unparsed"][k] + + return input_dict From 77759db4f5e80d6c8bf1b434d4a6e38ed5d9bbbf Mon Sep 17 00:00:00 2001 From: William Adams Date: Mon, 5 Apr 2021 18:23:56 -0400 Subject: [PATCH 021/144] Fix linting issues --- iodata/formats/json.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index b6ecd0852..c05bd56bd 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -457,7 +457,7 @@ def _parse_basis_keys(basis: dict, lit: LineIterator) -> dict: basis_dict Dictionary containing ... """ - raise NotImplementedError("") + raise NotImplementedError("qcschema_basis is not yet implemented in IOData.") def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: @@ -626,8 +626,7 @@ def _parse_driver(driver: str, lit: LineIterator) -> str: lit.filename ) ) - else: - return driver + return driver def _parse_model(model: dict, lit: LineIterator) -> dict: @@ -652,8 +651,7 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: if "method" not in model: raise FileFormatError("{}: QCSchema `model` requires a `method`".format(lit.filename)) - else: - model_dict["lot"] = model["method"] + model_dict["lot"] = model["method"] # QCEngineRecords doesn't give an empty string for basis-free methods, omits req'd key instead if "basis" not in model: warn( @@ -675,7 +673,6 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: basis = _parse_basis_keys(model["basis"], lit) model_dict.update(basis) extra_dict["basis"] = basis["extra"] - pass model_dict["extra"] = extra_dict return model_dict @@ -721,12 +718,10 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: raise FileFormatError( "{}: Invalid `protocols` `wavefunction` keyword.".format(lit.filename) ) - else: - protocols_dict["keep_wavefunction"] = wavefunction + protocols_dict["keep_wavefunction"] = wavefunction if not isinstance(keep_stdout, bool): raise FileFormatError("{}: `protocols` `stdout` option must be a boolean.") - else: - protocols_dict["keep_stdout"] = keep_stdout + protocols_dict["keep_stdout"] = keep_stdout return protocols_dict From d8c6330cb05172b38c8849187f9f8ef552a77d52 Mon Sep 17 00:00:00 2001 From: William Adams Date: Tue, 6 Apr 2021 18:42:28 -0400 Subject: [PATCH 022/144] Add QCSchema Output load_one and dump_one --- iodata/formats/json.py | 220 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 207 insertions(+), 13 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index c05bd56bd..06408ab57 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -739,11 +739,120 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: Returns ------- output_dict - ... + Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. + It may contain ``energy``, ``obasis`` and ``extra`` keys and corresponding values as well. + """ - # basis_dict = dict() - # return basis_dict - raise NotImplementedError("qcschema_output is not yet implemented in IOData.") + extra_dict = dict() + output_dict = _parse_output_keys(result, lit) + extra_dict["output"] = output_dict["extra"] + + if "molecule" not in result: + raise FileFormatError("{}: QCSchema Input requires 'molecule' key".format(lit.filename)) + molecule_dict = _parse_topology_keys(result["molecule"], lit) + output_dict.update(molecule_dict) + extra_dict["molecule"] = molecule_dict["extra"] + + input_dict = _parse_input_keys(result, lit) + output_dict.update(input_dict) + extra_dict["input"] = input_dict["extra"] + output_dict["extra"] = extra_dict + output_dict["extra"]["schema_name"] = "qcschema_output" + + return output_dict + + +def _parse_output_keys(result: dict, lit: LineIterator) -> dict: + """Parse input keys for QCSchema input or output files. + + Parameters + ---------- + result + The JSON dict loaded from file. + lit + The line iterator holding the file data. + + Returns + ------- + input_dict + Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. + It may contain ``energy`` keys and corresponding values as well. + + """ + should_be_required_keys = {"schema_name", "schema_version"} + output_keys = {"provenance", "properties", "success", "return_result"} + for key in should_be_required_keys: + if key not in result: + warn( + "{}: QCSchema files should have a '{}' key.".format(lit.filename, key), + FileFormatWarning, + 2, + ) + for key in output_keys: + if key not in result: + raise FileFormatError( + "{}: QCSchema `qcschema_output` file requires '{}' key".format(lit.filename, key) + ) + + # Store all extra keys in extra_dict and gather at end + output_dict = dict() + extra_dict = dict() + + extra_dict["schema_name"] = "qcschema_output" + try: + version = result["schema_version"] + except KeyError: + version = -1 + if float(version) < 1.0: + warn( + "{}: Unknown `qcschema_input` version {}, loading may produce invalid results".format( + lit.filename, version + ), + FileFormatWarning, + 2, + ) + extra_dict["schema_version"] = version + + extra_dict["return_result"] = result["return_result"] + extra_dict["success"] = result["success"] + + # Parse properties + properties = result["properties"] + if "return_energy" in properties: + output_dict["energy"] = properties["return_energy"] + extra_dict["properties"] = properties + + if "error" in result: + extra_dict["error"] = result["error"] + if "stderr" in result: + extra_dict["stderr"] = result["stderr"] + if "stdout" in result: + extra_dict["stderr"] = result["stdout"] + if "wavefunction" in result: + extra_dict["wavefunction"] = result["wavefunction"] + + output_dict["extra"] = extra_dict + + output_keys = { + "schema_name", + "schema_version", + "molecule", + "driver", + "model", + "extras", + "id", + "keywords", + "protocols", + "provenance", + "properties", + "success", + "return_result" + } + passthrough_keys = _find_passthrough_keys(result, output_keys) + if passthrough_keys: + output_dict["extra"]["unparsed"] = passthrough_keys + + return output_dict def _parse_provenance( @@ -805,8 +914,7 @@ def dump_one(f: TextIO, data: IOData): elif schema_name == "qcschema_input": return_dict = _dump_qcschema_input(data) elif schema_name == "qcschema_output": - raise NotImplementedError("{} not yet implemented in IOData.".format(schema_name)) - # return_dict = _dump_qcschema_output(data) + return_dict = _dump_qcschema_output(data) else: raise FileFormatError( "'schema_name' must be one of 'qcschema_molecule', 'qcschema_basis'" @@ -911,7 +1019,7 @@ def _dump_qcschema_molecule(data: IOData) -> dict: return molecule_dict -def _dump_qcschema_input(data): +def _dump_qcschema_input(data: IOData) -> dict: """Dump relevant attributes from IOData to qcschema_input. Using this function requires keywords to be stored in two locations in the `extra` dict: @@ -947,12 +1055,7 @@ def _dump_qcschema_input(data): raise FileFormatError("qcschema_input requires specifed `lot`.") input_dict["model"]["method"] = data.lot if data.obasis_name is None and "basis" not in data.extra["input"]["model"]: - warn( - "No basis name given. QCSchema assumes this signifies a basis-free method; to" - "avoid this warning, specify `obasis_name` as an empty string.", - FileFormatWarning, - 2, - ) + input_dict["model"]["basis"] = "" if "basis" in data.extra["input"]["model"]: raise NotImplementedError("qcschema_basis is not yet supported in IOData.") input_dict["model"]["basis"] = data.obasis_name @@ -980,3 +1083,94 @@ def _dump_qcschema_input(data): input_dict[k] = data.extra["input"]["unparsed"][k] return input_dict + + +def _dump_qcschema_output(data: IOData) -> dict: + """Dump relevant attributes from IOData to qcschema_output. + + Using this function requires keywords to be stored in three locations in the `extra` dict: + a `molecule` dict for the QCSchema Molecule extra keys, an `input` dict for the QCSchema + Input extra keys, and an `output` dict for the QCSchema Output extra keys. + + Parameters + ---------- + data + The IOData instance to dump to file. + + Returns + ------- + output_dict + The dict that will produce the QCSchema JSON file. + + """ + output_dict = {"schema_name": "qcschema_output", "schema_version": 2.0} + + # Gather required field data + # Gather required field data + output_dict["molecule"] = _dump_qcschema_molecule(data) + if "driver" not in data.extra["input"]: + raise FileFormatError("qcschema_output requires `driver` field in extra['input'].") + if data.extra["input"]["driver"] not in {"energy", "gradient", "hessian", "properties"}: + raise FileFormatError( + "QCSchema driver must be one of `energy`, `gradient`, `hessian`, or `properties`" + ) + output_dict["driver"] = data.extra["input"]["driver"] + if "model" not in data.extra["input"]: + raise FileFormatError("qcschema_output requires `model` field in extra['input'].") + output_dict["model"] = dict() + if data.lot is None: + raise FileFormatError("qcschema_output requires specifed `lot`.") + output_dict["model"]["method"] = data.lot + if data.obasis_name is None and "basis" not in data.extra["input"]["model"]: + warn( + "No basis name given. QCSchema assumes this signifies a basis-free method; to" + "avoid this warning, specify `obasis_name` as an empty string.", + FileFormatWarning, + 2, + ) + if "basis" in data.extra["input"]["model"]: + raise NotImplementedError("qcschema_basis is not yet supported in IOData.") + output_dict["model"]["basis"] = data.obasis_name + if "properties" not in data.extra["output"]: + raise FileFormatError("qcschema_output requires `properties` field in extra['output'].") + output_dict["properties"] = data.extra["output"]["properties"] + if data.energy is not None: + output_dict["properties"]["return_energy"] = data.energy + if output_dict["driver"] == "energy": + output_dict["return_result"] = data.energy + if "return_result" not in output_dict and "return_result" not in data.extra["output"]: + raise FileFormatError("qcschema_output requires `return_result` field in extra['output'].") + elif "return_result" in data.extra["output"]: + output_dict["return_result"] = data.extra["output"]["return_result"] + if "keywords" in data.extra["input"]: + output_dict["keywords"] = data.extra["input"]["keywords"] + if "extras" in data.extra["input"]: + output_dict["extras"] = data.extra["input"]["extras"] + if "id" in data.extra["input"]: + output_dict["id"] = data.extra["input"]["id"] + if "protocols" in data.extra["input"]: + output_dict["protocols"] = dict() + # Remove 'keep_' from protocols keys (added in IOData for readability) + for keep in data.extra["input"]["protocols"]: + output_dict["protocols"][keep[5:]] = data.extra["input"]["protocols"][keep] + if "error" in data.extra["output"]: + output_dict["error"] = data.extra["output"]["error"] + if "stderr" in data.extra["output"]: + output_dict["stderr"] = data.extra["output"]["stderr"] + if "stdout" in data.extra["output"]: + output_dict["stderr"] = data.extra["output"]["stdout"] + if "wavefunction" in data.extra["output"]: + output_dict["wavefunction"] = data.extra["output"]["wavefunction"] + if "provenance" in data.extra["input"]: + output_dict["provenance"] = data.extra["input"]["provenance"] + else: + output_dict["provenance"] = { + "creator": "IOData", + "version": __version__, + "routine": "iodata.formats.json", + } + if "unparsed" in data.extra["input"]: + for k in data.extra["input"]["unparsed"]: + output_dict[k] = data.extra["input"]["unparsed"][k] + + return output_dict From d34c8463322cab93f0aa13b774d0dcfb0cf2b8a8 Mon Sep 17 00:00:00 2001 From: William Adams Date: Tue, 6 Apr 2021 18:45:28 -0400 Subject: [PATCH 023/144] Add QCSchema output tests --- .../test/data/H2O_CCSDprTpr_STO3G_output.json | 70 +++++++++ .../test/data/LiCl_STO4G_Gaussian_output.json | 90 ++++++++++++ .../turbomole_water_energy_hf_output.json | 112 +++++++++++++++ ...turbomole_water_gradient_rimp2_output.json | 134 ++++++++++++++++++ iodata/test/data/xtb_water_no_basis.json | 64 +++++++++ iodata/test/test_json.py | 69 ++++++++- 6 files changed, 538 insertions(+), 1 deletion(-) create mode 100644 iodata/test/data/H2O_CCSDprTpr_STO3G_output.json create mode 100644 iodata/test/data/LiCl_STO4G_Gaussian_output.json create mode 100644 iodata/test/data/turbomole_water_energy_hf_output.json create mode 100644 iodata/test/data/turbomole_water_gradient_rimp2_output.json create mode 100644 iodata/test/data/xtb_water_no_basis.json diff --git a/iodata/test/data/H2O_CCSDprTpr_STO3G_output.json b/iodata/test/data/H2O_CCSDprTpr_STO3G_output.json new file mode 100644 index 000000000..e37202048 --- /dev/null +++ b/iodata/test/data/H2O_CCSDprTpr_STO3G_output.json @@ -0,0 +1,70 @@ +{ + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": [ + "O", + "H", + "H" + ], + "geometry": [ + 0.0, + 0.0, + -0.12947694, + 0.0, + -1.49418734, + 1.02744651, + 0.0, + 1.49418734, + 1.02744651 + ], + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "connectivity": [ + [0, 1, 1], + [0, 2, 1] + ], + "fix_orientation": true, + "fix_symmetry": "c1", + "provenance": { + "creator": "HORTON3", + "routine": "Extracted from molpro_ccsd_prt_pr_water_energy_output.json" + }, + "real": [true, true, true] + }, + "driver": "energy", + "model": { + "method": "CCSD(T)", + "basis": "sto-3g" + }, + "id": "1", + "schema_name": "qcschema_output", + "schema_version": 2.0, + "provenance": [ + { + "creator": "QCElemental", + "version": "v0.2.6", + "routine": "qcelemental.models.results" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ], + "success": true, + "return_result": -75.0197177197499, + "properties": { + "nuclear_repulsion_energy": 8.80146205625184, + "scf_dipole_moment": [0.0, 0.0, 0.65662910269302], + "scf_total_energy": -74.9646625166939, + "ccsd_same_spin_correlation_energy": -0.0019035720854254133, + "ccsd_opposite_spin_correlation_energy": -0.0530757170419502, + "ccsd_correlation_energy": -0.0549792917918461, + "ccsd_total_energy": -75.0196418084857, + "ccsd_prt_pr_correlation_energy": -0.0550552030560323, + "ccsd_prt_pr_total_energy": -75.0197177197499, + "calcinfo_nalpha": 5, + "calcinfo_nbeta": 5, + "calcinfo_nbasis": 7 + } +} diff --git a/iodata/test/data/LiCl_STO4G_Gaussian_output.json b/iodata/test/data/LiCl_STO4G_Gaussian_output.json new file mode 100644 index 000000000..5ad461ade --- /dev/null +++ b/iodata/test/data/LiCl_STO4G_Gaussian_output.json @@ -0,0 +1,90 @@ +{ + "schema_name": "qcschema_output", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "real": [true, true], + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "HORTON3", + "routine": "Manual validation" + } + ] + }, + "id": "LiCl_HF_STO4G", + "driver": "energy", + "return_result": -464.626219879, + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "keywords": { + "program": "Gaussian", + "verbose": "normal", + "run_type": "Freq", + "geom": "AllCheck", + "mem": "7GB", + "nprocshared": "2", + "oldchk": "LiCl_OPT.chk", + "chk": "LiCl_HF_STO4G.chk" + }, + "protocols": { + "wavefunction": "none", + "stdout": false + }, + "properties": { + "calcinfo_nbasis": 14, + "calcinfo_nmo": 14, + "calcinfo_nalpha": 10, + "calcinfo_nbeta": 10, + "nuclear_repulsion_energy": 14.0583267322, + "return_energy": -464.626219879, + "scf_moments": [ + [0.0000, 0.0000, -5.3718], + [-15.0269, 0.0000, 0.0000, 0.0000, -15.0269, 0.0000, 0.0000, 0.0000, -4.4203], + [ + 0.0000, 0.0000, 0.4291, 0.0000, 0.0000, 0.0000, 0.4291, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.4291, 0.0000, 0.4291, 0.0000, + 0.4291, 0.0000, 0.0000, 0.0000, 0.4291, 0.0000, 0.0000, 0.0000, -19.3542 + ], + [ + -18.9062, 0.0000, 0.0000, 0.0000, -6.3021, 0.0000, 0.0000, 0.0000, -11.1046, + 0.0000, -6.3021, 0.0000, -6.3021, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, -11.1046, 0.0000, 0.0000, 0.0000, -11.1046, 0.0000, 0.0000, + 0.0000, -6.3021, 0.0000, -6.3021, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + -6.3021, 0.0000, 0.0000, 0.0000, -18.9062, 0.0000, 0.0000, 0.0000, -11.1046, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, -11.1046, 0.0000, -11.1046, 0.0000, + 0.0000, 0.0000, -11.1046, 0.0000, 0.0000, 0.0000, -11.1046, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, -11.1046, 0.0000, -11.1046, 0.0000, + -11.1046, 0.0000, 0.0000, 0.0000, -11.1046, 0.0000, 0.0000, 0.0000, -9.9113 + ] + ], + "scf_energy": -464.626219879, + "scf_iterations": 9 + }, + "success": true, + "extras": { + "lewis_acid_group": "alkali_metals", + "lewis_base_group": "halides" + }, + "provenance": [ + { + "creator": "Gaussian 16", + "version": "ES64L-G16RevC.01" + }, + { + "creator": "IOData", + "version": "X.X.X", + "routine": "iodata.dump_one.json" + } + ] +} \ No newline at end of file diff --git a/iodata/test/data/turbomole_water_energy_hf_output.json b/iodata/test/data/turbomole_water_energy_hf_output.json new file mode 100644 index 000000000..6f24e1581 --- /dev/null +++ b/iodata/test/data/turbomole_water_energy_hf_output.json @@ -0,0 +1,112 @@ +{ + "id": null, + "schema_name": "qcschema_output", + "schema_version": 1, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2, + "validated": true, + "symbols": [ + "O", + "H", + "H" + ], + "geometry": [ + 0.0, + 0.0, + -0.12947694, + 0.0, + -1.49418734, + 1.02744651, + 0.0, + 1.49418734, + 1.02744651 + ], + "name": "H2O", + "identifiers": null, + "comment": null, + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "masses": [ + 15.99491461957, + 1.00782503223, + 1.00782503223 + ], + "real": [ + true, + true, + true + ], + "atom_labels": [ + "", + "", + "" + ], + "atomic_numbers": [ + 8, + 1, + 1 + ], + "mass_numbers": [ + 16, + 1, + 1 + ], + "connectivity": null, + "fragments": [ + [ + 0, + 1, + 2 + ] + ], + "fragment_charges": [ + 0.0 + ], + "fragment_multiplicities": [ + 1 + ], + "fix_com": false, + "fix_orientation": false, + "fix_symmetry": null, + "provenance": { + "creator": "QCElemental", + "version": "v0.8.0+11.g7c6a220", + "routine": "qcelemental.molparse.from_schema" + }, + "id": null, + "extras": null + }, + "driver": "energy", + "model": { + "method": "hf", + "basis": "def2-SVP" + }, + "keywords": {}, + "extras": { + "outfiles": { + "control": "$title\nQCEngine Turbomole\n$operating system unix\n$symmetry c1\n$user-defined bonds file=coord\n$coord file=coord\n$optimize\n internal off\n redundant off\n cartesian on\n global off\n basis off\n$atoms\no 1 \\\n basis =o def2-SVP\nh 2-3 \\\n basis =h def2-SVP\n$basis file=basis\n$rundimensions\n dim(fock,dens)=352\n natoms=3\n nshell=12\n nbf(CAO)=25\n dim(trafo[SAO<-->AO/CAO])=27\n rhfshells=1\n nbf(AO)=24\n$scfmo file=mos\n$closed shells\n a 1-5 ( 2 )\n$scfiterlimit 150\n$scfconv 8\n$thize 0.10000000E-04\n$thime 5\n$scfdamp start=0.300 step=0.050 min=0.100\n$scfdump\n$scfintunit\n unit=30 size=0 file=twoint\n$scfdiis\n$maxcor 500 MiB per_core\n$scforbitalshift automatic=.1\n$drvopt\n cartesian on\n basis off\n global off\n hessian on\n dipole on\n nuclear polarizability\n$interconversion off\n qconv=1.d-7\n maxiter=25\n$coordinateupdate\n dqmax=0.3\n interpolate on\n statistics 5\n$forceupdate\n ahlrichs numgeo=0 mingeo=3 maxgeo=4 modus= dynamic fail=0.3\n threig=0.005 reseig=0.005 thrbig=3.0 scale=1.00 damping=0.0\n$forceinit on\n diag=default\n$energy file=energy\n$grad file=gradient\n$forceapprox file=forceapprox\n$last step dscf\n$orbital_max_rnorm 0.30769395709238E-05\n$last SCF energy change = -75.955370\n$charge from dscf\n -0.000 (not to be modified here)\n$dipole from dscf\n x 0.00000000000002 y -0.00000000000000 z 0.85624894415066 a.u.\n | dipole | = 2.1763841798 debye\n$end\n", + "stderr": " dscf ended normally\n" + }, + "qcvars": { + "HF TOTAL ENERGY": "-75.95536954370", + "CURRENT ENERGY": "-75.95536954370" + } + }, + "provenance": { + "creator": "Turbomole", + "version": "7.3", + "routine": "turbomole", + "username": "johannes", + "cpu": "Intel(R) Core(TM) i5-6600 CPU @ 3.30GHz", + "wall_time": 0.309786319732666, + "qcengine_version": "v0.10.0+80.gd0ddc48.dirty", + "hostname": "southgeorgia" + }, + "properties": {}, + "return_result": -75.9553695437, + "stdout": "\n OpenMP run-time library returned nthreads = 2\n operating system is UNIX !\n\n dscf (southgeorgia) : TURBOMOLE V7.3 ( 22142 ) 4 Jul 2018 at 18:12:42\n Copyright (C) 2018 TURBOMOLE GmbH, Karlsruhe\n\n\n 2019-10-29 09:56:50.465 \n\n\n\n d s c f - program\n\n idea & directorship : reinhart ahlrichs\n program development : marco haeser\n michael baer\n dft version : oliver treutler\n\n\n quantum chemistry group\n universitaet karlsruhe\n germany\n\n\n\n\n References \n \n TURBOMOLE: \n R. Ahlrichs, M. Baer, M. Haeser, H. Horn, and\n C. Koelmel\n Electronic structure calculations on workstation\n computers: the program system TURBOMOLE\n Chem. Phys. Lett. 162: 165 (1989)\n Density Functional: \n O. Treutler and R. Ahlrichs \n Efficient Molecular Numerical Integration Schemes\n J. Chem. Phys. 102: 346 (1995) \n Parallel Version: \n Performance of parallel TURBOMOLE for Density \n Functional Calculations \n M. v. Arnim and R. Ahlrichs \n J. Comp. Chem. 19: 1746 (1998) \n \n\n\n\n\n +--------------------------------------------------+\n | Atomic coordinate, charge and isotop information |\n +--------------------------------------------------+\n\n atomic coordinates atom charge isotop\n 0.00000000 0.00000000 -0.12947694 o 8.000 0\n 0.00000000 -1.49418734 1.02744651 h 1.000 0\n 0.00000000 1.49418734 1.02744651 h 1.000 0\n \n center of nuclear mass : 0.00000000 0.00000000 -0.00001570\n center of nuclear charge: 0.00000000 0.00000000 0.10190775\n\n *************************************************************************\n QCEngine Turbomole \n *************************************************************************\n\n\n\n +--------------------------------------------------+\n | basis set information |\n +--------------------------------------------------+\n\n we will work with the 1s 3p 5d 7f 9g ... basis set\n ...i.e. with spherical basis functions...\n\n type atoms prim cont basis\n ---------------------------------------------------------------------------\n o 1 24 14 def2-SVP [3s2p1d|7s4p1d]\n h 2 7 5 def2-SVP [2s1p|4s1p]\n ---------------------------------------------------------------------------\n total: 3 38 24\n ---------------------------------------------------------------------------\n\n total number of primitive shells : 17\n total number of contracted shells : 12\n total number of cartesian basis functions : 25\n total number of SCF-basis functions : 24\n\n\n integral neglect threshold : 0.13E-10\n integral storage threshold THIZE : 0.10E-04\n integral storage threshold THIME : 5\n\n\n symmetry group of the molecule : c1 \n\n the group has the following generators :\n c1(z)\n\n 1 symmetry operations found\n\n there are 1 real representations : a \n\n maximum number of shells which are related by symmetry : 1\n\n\n mo occupation :\n irrep mo's occupied\n a 24 5\n \n number of basis functions : 24\n number of occupied orbitals : 5\n \n\n automatic virtual orbital shift switched on \n shift if e(lumo)-e(homo) < 0.10000000 \n\n\n ------------------------\n nuclear repulsion energy : 8.80146205625 \n ------------------------\n\n\n -----------------\n -S,T+V- integrals\n -----------------\n\n 1e-integrals will be neglected if expon. factor < 0.133678E-11\n \n Difference densities algorithm switched on.\n The maximal number of linear combinations of\n difference densities is 20 .\n\n DIIS switched on: error vector is FDS-SDF\n Max. Iterations for DIIS is : 5\n DIIS matrix (see manual) \n Scaling factor of diagonals : 1.200\n threshold for scaling factor : 0.000\n\n scf convergence criterion : increment of total energy < .1000000D-07\n and increment of one-electron energy < .1000000D-04\n\n MOs are in ASCII format !\n\n\n reading orbital data $scfmo from file mos\n orbital characterization : expanded\n mo provided and orthogonalized by Cholesky decomposition\n\n DSCF restart information will be dumped onto file mos\n\n \n current damping : 0.300\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 1 -75.741527779117 -123.80966488 39.266675041 0.000D+00 0.133D-10\n max. resid. norm for Fia-block= 2.815D-01 for orbital 4a \n max. resid. fock norm = 1.548D+00 for orbital 10a \n Delta Eig. = 47.9536513414 eV \n \n current damping : 0.250\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 2 -75.933097028971 -122.13127215 37.396713062 0.518D+00 0.885D-11\n Norm of current diis error: 0.34565 \n max. resid. norm for Fia-block= 9.171D-02 for orbital 1a \n max. resid. fock norm = 1.102D-01 for orbital 18a \n Delta Eig. = 9.1913160300 eV \n \n current damping : 0.200\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 3 -75.951594723455 -122.46272320 37.709666417 0.171D+00 0.698D-11\n Norm of current diis error: 0.12813 \n max. resid. norm for Fia-block= 3.429D-02 for orbital 4a \n max. resid. fock norm = 3.703D-02 for orbital 9a \n Delta Eig. = 2.6966294125 eV \n \n current damping : 0.250\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 4 -75.954873573064 -122.31745771 37.561122081 0.353D-01 0.611D-11\n Norm of current diis error: 0.40537E-01\n max. resid. norm for Fia-block= 1.251D-02 for orbital 4a \n max. resid. fock norm = 1.304D-02 for orbital 4a \n Delta Eig. = 0.3810747590 eV \n \n current damping : 0.300\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 5 -75.955289006509 -122.34196630 37.585215233 0.766D-02 0.559D-11\n Norm of current diis error: 0.14905E-01\n max. resid. norm for Fia-block= 6.019D-03 for orbital 4a \n max. resid. fock norm = 6.167D-03 for orbital 4a \n mo-orthogonalization: Cholesky decomposition\n Delta Eig. = 0.0719638390 eV \n \n current damping : 0.350\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 6 -75.955355674500 -122.33969472 37.582876992 0.103D-02 0.515D-11\n Norm of current diis error: 0.57821E-02\n max. resid. norm for Fia-block= 2.570D-03 for orbital 4a \n max. resid. fock norm = 2.635D-03 for orbital 4a \n Delta Eig. = 0.0172270810 eV \n \n current damping : 0.400\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 7 -75.955367047288 -122.33983310 37.583003997 0.142D-03 0.479D-11\n Norm of current diis error: 0.23805E-02\n max. resid. norm for Fia-block= 1.151D-03 for orbital 4a \n max. resid. fock norm = 1.174D-03 for orbital 4a \n Delta Eig. = 0.0054038082 eV \n \n current damping : 0.450\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 8 -75.955369100318 -122.33943730 37.582606142 0.188D-04 0.449D-11\n Norm of current diis error: 0.10017E-02\n max. resid. norm for Fia-block= 5.006D-04 for orbital 4a \n max. resid. fock norm = 5.110D-04 for orbital 4a \n Delta Eig. = 0.0028635244 eV \n \n current damping : 0.500\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 9 -75.955369471234 -122.33947599 37.582644467 0.718D-05 0.424D-11\n Norm of current diis error: 0.41334E-03\n max. resid. norm for Fia-block= 2.170D-04 for orbital 4a \n max. resid. fock norm = 2.202D-04 for orbital 4a \n Delta Eig. = 0.0011916788 eV \n \n current damping : 0.550\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 10 -75.955369532338 -122.33935554 37.582523947 0.125D-05 0.404D-11\n Norm of current diis error: 0.16682E-03\n max. resid. norm for Fia-block= 8.490D-05 for orbital 4a \n max. resid. fock norm = 8.646D-05 for orbital 4a \n mo-orthogonalization: Cholesky decomposition\n Delta Eig. = 0.0004737614 eV \n \n current damping : 0.600\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 11 -75.955369541949 -122.33936379 37.582532192 0.277D-06 0.389D-11\n Norm of current diis error: 0.68636E-04\n max. resid. norm for Fia-block= 3.492D-05 for orbital 4a \n max. resid. fock norm = 3.538D-05 for orbital 4a \n Delta Eig. = 0.0001759275 eV \n \n current damping : 0.650\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 12 -75.955369543455 -122.33935314 37.582521542 0.320D-06 0.380D-11\n Norm of current diis error: 0.28699E-04\n max. resid. norm for Fia-block= 1.386D-05 for orbital 4a \n max. resid. fock norm = 1.403D-05 for orbital 4a \n Delta Eig. = 0.0000713983 eV \n\n ENERGY CONVERGED !\n\n \n current damping : 0.500\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY NORM[dD(SAO)] TOL\n 13 -75.955369543698 -122.33935200 37.582520398 0.146D-05 0.380D-11\n max. resid. norm for Fia-block= 3.062D-06 for orbital 4a \n max. resid. fock norm = 3.077D-06 for orbital 4a \n\n convergence criteria satisfied after 13 iterations\n\n\n *************************************************************************\n QCEngine Turbomole \n *************************************************************************\n\n\n ------------------------------------------ \n | total energy = -75.95536954370 |\n ------------------------------------------ \n : kinetic energy = 75.63206605914 :\n : potential energy = -151.58743560284 :\n : virial theorem = 1.99574350719 :\n : wavefunction norm = 1.00000000000 :\n .......................................... \n\n\n : there is no data group $energy \n\n\n : $end is missing \n\n\n orbitals $scfmo will be written to file mos\n\n irrep 1a 2a 3a 4a 5a \n eigenvalues H -20.55250 -1.29794 -0.68127 -0.56035 -0.49510\n eV -559.2664 -35.3190 -18.5384 -15.2480 -13.4725\n occupation 2.0000 2.0000 2.0000 2.0000 2.0000 \n\n irrep 6a 7a 8a 9a 10a \n eigenvalues H 0.16932 0.24840 0.76485 0.82701 1.18642\n eV 4.6076 6.7593 20.8129 22.5044 32.2845\n \n \n \n \n ==============================================================================\n electrostatic moments\n ==============================================================================\n\n reference point for electrostatic moments: 0.00000 0.00000 0.00000\n\n \n nuc elec -> total\n ------------------------------------------------------------------------------\n charge \n ------------------------------------------------------------------------------\n 10.000000 -10.000000 -0.000000\n \n ------------------------------------------------------------------------------\n dipole moment \n ------------------------------------------------------------------------------\n x 0.000000 0.000000 0.000000\n y 0.000000 -0.000000 -0.000000\n z 1.019078 -0.162829 0.856249\n \n | dipole moment | = 0.8562 a.u. = 2.1764 debye \n \n ------------------------------------------------------------------------------\n quadrupole moment\n ------------------------------------------------------------------------------\n xx 0.000000 -5.305745 -5.305745\n yy 4.465192 -7.479074 -3.013882\n zz 2.245407 -6.491368 -4.245961\n xy 0.000000 0.000000 0.000000\n xz 0.000000 0.000000 0.000000\n yz 0.000000 -0.000000 -0.000000\n \n 1/3 trace= -4.188529\n anisotropy= 1.986680\n \n ==============================================================================\n \n\n\n ------------------------------------------------------------------------\n total cpu-time : 0.11 seconds\n total wall-time : 0.06 seconds\n ------------------------------------------------------------------------\n\n **** dscf : all done ****\n\n\n 2019-10-29 09:56:50.522 \n\n", + "stderr": null, + "success": true, + "error": null +} \ No newline at end of file diff --git a/iodata/test/data/turbomole_water_gradient_rimp2_output.json b/iodata/test/data/turbomole_water_gradient_rimp2_output.json new file mode 100644 index 000000000..5dfd9f064 --- /dev/null +++ b/iodata/test/data/turbomole_water_gradient_rimp2_output.json @@ -0,0 +1,134 @@ +{ + "id": null, + "schema_name": "qcschema_output", + "schema_version": 1, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2, + "validated": true, + "symbols": [ + "O", + "H", + "H" + ], + "geometry": [ + 0.0, + 0.0, + -0.12947694, + 0.0, + -1.49418734, + 1.02744651, + 0.0, + 1.49418734, + 1.02744651 + ], + "name": "H2O", + "identifiers": null, + "comment": null, + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "masses": [ + 15.99491461957, + 1.00782503223, + 1.00782503223 + ], + "real": [ + true, + true, + true + ], + "atom_labels": [ + "", + "", + "" + ], + "atomic_numbers": [ + 8, + 1, + 1 + ], + "mass_numbers": [ + 16, + 1, + 1 + ], + "connectivity": null, + "fragments": [ + [ + 0, + 1, + 2 + ] + ], + "fragment_charges": [ + 0.0 + ], + "fragment_multiplicities": [ + 1 + ], + "fix_com": false, + "fix_orientation": false, + "fix_symmetry": null, + "provenance": { + "creator": "QCElemental", + "version": "v0.8.0+11.g7c6a220", + "routine": "qcelemental.molparse.from_schema" + }, + "id": null, + "extras": null + }, + "driver": "gradient", + "model": { + "method": "rimp2", + "basis": "def2-SVP" + }, + "keywords": {}, + "extras": { + "outfiles": { + "control": "$title\nQCEngine Turbomole\n$operating system unix\n$symmetry c1\n$user-defined bonds file=coord\n$coord file=coord\n$optimize\n internal off\n redundant off\n cartesian on\n global off\n basis off\n$atoms\no 1 \\\n basis =o def2-SVP \\\n cbas =o def2-SVP\nh 2-3 \\\n basis =h def2-SVP \\\n cbas =h def2-SVP\n$basis file=basis\n$rundimensions\n dim(fock,dens)=352\n natoms=3\n nshell=12\n nbf(CAO)=25\n dim(trafo[SAO<-->AO/CAO])=27\n rhfshells=1\n nbf(AO)=24\n$scfmo file=mos\n$closed shells\n a 1-5 ( 2 )\n$scfiterlimit 150\n$scfconv 8\n$thize 0.10000000E-04\n$thime 5\n$scfdamp start=0.300 step=0.050 min=0.100\n$scfdump\n$scfintunit\n unit=30 size=0 file=twoint\n$scfdiis\n$maxcor 500 MiB per_core\n$scforbitalshift automatic=.1\n$drvopt\n cartesian on\n basis off\n global off\n hessian on\n dipole on\n nuclear polarizability\n$interconversion off\n qconv=1.d-7\n maxiter=25\n$coordinateupdate\n dqmax=0.3\n interpolate on\n statistics 5\n$forceupdate\n ahlrichs numgeo=0 mingeo=3 maxgeo=4 modus= dynamic fail=0.3\n threig=0.005 reseig=0.005 thrbig=3.0 scale=1.00 damping=0.0\n$forceinit on\n diag=default\n$energy file=energy\n$grad file=gradient\n$forceapprox file=forceapprox\n$denconv 0.10000000E-06\n$freeze\n implicit core= 1 virt= 0\n$cbas file=auxbasis\n$ricc2\n mp2\n geoopt model=mp2 state=(x)\n$last step ricc2\n$orbital_max_rnorm 0.30769401400734E-05\n$last SCF energy change = -75.955370\n$charge from dscf\n -0.000 (not to be modified here)\n$dipole from ricc2\n x 0.00000000000001 y -0.00000000000003 z 0.81508211917860 a.u.\n | dipole | = 2.0717477569 debye\n$last MP2 energy change= -.20399186\n$end\n", + "gradient": "$grad cartesian gradients\n cycle = 1 MP2 energy = -76.1593614075 |dE/dxyz| = 0.061576\n 0.00000000000000 0.00000000000000 -0.12947694000000 o\n 0.00000000000000 -1.49418734000000 1.02744651000000 h\n 0.00000000000000 1.49418734000000 1.02744651000000 h\n 0.77638926688872D-13 0.00000000000000D+00 -.35034209636233D-01\n -.38704950363896D-13 -.31228670589283D-01 0.17517104810727D-01\n -.38933976324976D-13 0.31228670589290D-01 0.17517104810736D-01\n$end\n", + "stderr": " dscf ended normally\n ricc2 ended normally\n" + }, + "qcvars": { + "HF TOTAL ENERGY": "-75.95536954370", + "CURRENT ENERGY": "-76.159361407452", + "CURRENT GRADIENT": [ + 7.7638926688872e-14, + 0.0, + -0.035034209636233, + -3.8704950363896e-14, + -0.031228670589283, + 0.017517104810727, + -3.8933976324976e-14, + 0.03122867058929, + 0.017517104810736 + ] + } + }, + "provenance": { + "creator": "Turbomole", + "version": "7.3", + "routine": "turbomole", + "username": "johannes", + "cpu": "Intel(R) Core(TM) i5-6600 CPU @ 3.30GHz", + "wall_time": 0.3380742073059082, + "qcengine_version": "v0.10.0+80.gd0ddc48.dirty", + "hostname": "southgeorgia" + }, + "properties": {}, + "return_result": [ + 7.7638926688872e-14, + 0.0, + -0.035034209636233, + -3.8704950363896e-14, + -0.031228670589283, + 0.017517104810727, + -3.8933976324976e-14, + 0.03122867058929, + 0.017517104810736 + ], + "stdout": "\n OpenMP run-time library returned nthreads = 2\n operating system is UNIX !\n\n dscf (southgeorgia) : TURBOMOLE V7.3 ( 22142 ) 4 Jul 2018 at 18:12:42\n Copyright (C) 2018 TURBOMOLE GmbH, Karlsruhe\n\n\n 2019-10-29 09:56:52.630 \n\n\n\n d s c f - program\n\n idea & directorship : reinhart ahlrichs\n program development : marco haeser\n michael baer\n dft version : oliver treutler\n\n\n quantum chemistry group\n universitaet karlsruhe\n germany\n\n\n\n\n References \n \n TURBOMOLE: \n R. Ahlrichs, M. Baer, M. Haeser, H. Horn, and\n C. Koelmel\n Electronic structure calculations on workstation\n computers: the program system TURBOMOLE\n Chem. Phys. Lett. 162: 165 (1989)\n Density Functional: \n O. Treutler and R. Ahlrichs \n Efficient Molecular Numerical Integration Schemes\n J. Chem. Phys. 102: 346 (1995) \n Parallel Version: \n Performance of parallel TURBOMOLE for Density \n Functional Calculations \n M. v. Arnim and R. Ahlrichs \n J. Comp. Chem. 19: 1746 (1998) \n \n\n\n\n\n +--------------------------------------------------+\n | Atomic coordinate, charge and isotop information |\n +--------------------------------------------------+\n\n atomic coordinates atom charge isotop\n 0.00000000 0.00000000 -0.12947694 o 8.000 0\n 0.00000000 -1.49418734 1.02744651 h 1.000 0\n 0.00000000 1.49418734 1.02744651 h 1.000 0\n \n center of nuclear mass : 0.00000000 0.00000000 -0.00001570\n center of nuclear charge: 0.00000000 0.00000000 0.10190775\n\n *************************************************************************\n QCEngine Turbomole \n *************************************************************************\n\n\n\n +--------------------------------------------------+\n | basis set information |\n +--------------------------------------------------+\n\n we will work with the 1s 3p 5d 7f 9g ... basis set\n ...i.e. with spherical basis functions...\n\n type atoms prim cont basis\n ---------------------------------------------------------------------------\n o 1 24 14 def2-SVP [3s2p1d|7s4p1d]\n h 2 7 5 def2-SVP [2s1p|4s1p]\n ---------------------------------------------------------------------------\n total: 3 38 24\n ---------------------------------------------------------------------------\n\n total number of primitive shells : 17\n total number of contracted shells : 12\n total number of cartesian basis functions : 25\n total number of SCF-basis functions : 24\n\n\n integral neglect threshold : 0.13E-10\n integral storage threshold THIZE : 0.10E-04\n integral storage threshold THIME : 5\n\n\n DENSITY CONVERGENCE CHECK SWITCHED ON !\n SCF CONVERGENCE IF RMS(delta[D]) < 0.1000000000E-06\n\n\n symmetry group of the molecule : c1 \n\n the group has the following generators :\n c1(z)\n\n 1 symmetry operations found\n\n there are 1 real representations : a \n\n maximum number of shells which are related by symmetry : 1\n\n\n mo occupation :\n irrep mo's occupied\n a 24 5\n \n number of basis functions : 24\n number of occupied orbitals : 5\n \n\n automatic virtual orbital shift switched on \n shift if e(lumo)-e(homo) < 0.10000000 \n\n\n ------------------------\n nuclear repulsion energy : 8.80146205625 \n ------------------------\n\n\n -----------------\n -S,T+V- integrals\n -----------------\n\n 1e-integrals will be neglected if expon. factor < 0.133678E-11\n \n Difference densities algorithm switched on.\n The maximal number of linear combinations of\n difference densities is 20 .\n\n DIIS switched on: error vector is FDS-SDF\n Max. Iterations for DIIS is : 5\n DIIS matrix (see manual) \n Scaling factor of diagonals : 1.200\n threshold for scaling factor : 0.000\n\n scf convergence criterion : increment of total energy < .1000000D-07\n and increment of one-electron energy < .1000000D-04\n\n MOs are in ASCII format !\n\n\n reading orbital data $scfmo from file mos\n orbital characterization : expanded\n mo provided and orthogonalized by Cholesky decomposition\n\n DSCF restart information will be dumped onto file mos\n\n \n current damping : 0.300\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 1 -75.741527779117 -123.80966488 39.266675041 0.000D+00 0.133D-10\n max. resid. norm for Fia-block= 2.815D-01 for orbital 4a \n max. resid. fock norm = 1.548D+00 for orbital 10a \n Delta Eig. = 47.9536513414 eV \n \n current damping : 0.250\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 2 -75.933097028971 -122.13127215 37.396713062 0.274D-01 0.885D-11\n Norm of current diis error: 0.34565 \n max. resid. norm for Fia-block= 9.171D-02 for orbital 1a \n max. resid. fock norm = 1.102D-01 for orbital 18a \n Delta Eig. = 9.1913160300 eV \n \n current damping : 0.200\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 3 -75.951594723455 -122.46272320 37.709666417 0.102D-01 0.698D-11\n Norm of current diis error: 0.12813 \n max. resid. norm for Fia-block= 3.429D-02 for orbital 4a \n max. resid. fock norm = 3.703D-02 for orbital 9a \n Delta Eig. = 2.6966294125 eV \n \n current damping : 0.250\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 4 -75.954873573064 -122.31745771 37.561122081 0.445D-02 0.611D-11\n Norm of current diis error: 0.40537E-01\n max. resid. norm for Fia-block= 1.251D-02 for orbital 4a \n max. resid. fock norm = 1.304D-02 for orbital 4a \n Delta Eig. = 0.3810747590 eV \n \n current damping : 0.300\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 5 -75.955289006509 -122.34196630 37.585215233 0.157D-02 0.559D-11\n Norm of current diis error: 0.14905E-01\n max. resid. norm for Fia-block= 6.019D-03 for orbital 4a \n max. resid. fock norm = 6.167D-03 for orbital 4a \n mo-orthogonalization: Cholesky decomposition\n Delta Eig. = 0.0719638390 eV \n \n current damping : 0.350\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 6 -75.955355674500 -122.33969472 37.582876992 0.647D-03 0.515D-11\n Norm of current diis error: 0.57821E-02\n max. resid. norm for Fia-block= 2.570D-03 for orbital 4a \n max. resid. fock norm = 2.635D-03 for orbital 4a \n Delta Eig. = 0.0172270810 eV \n \n current damping : 0.400\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 7 -75.955367047288 -122.33983310 37.583003997 0.267D-03 0.479D-11\n Norm of current diis error: 0.23805E-02\n max. resid. norm for Fia-block= 1.151D-03 for orbital 4a \n max. resid. fock norm = 1.174D-03 for orbital 4a \n Delta Eig. = 0.0054038082 eV \n \n current damping : 0.450\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 8 -75.955369100318 -122.33943730 37.582606142 0.114D-03 0.449D-11\n Norm of current diis error: 0.10017E-02\n max. resid. norm for Fia-block= 5.006D-04 for orbital 4a \n max. resid. fock norm = 5.110D-04 for orbital 4a \n Delta Eig. = 0.0028635244 eV \n \n current damping : 0.500\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 9 -75.955369471234 -122.33947599 37.582644467 0.487D-04 0.424D-11\n Norm of current diis error: 0.41334E-03\n max. resid. norm for Fia-block= 2.170D-04 for orbital 4a \n max. resid. fock norm = 2.202D-04 for orbital 4a \n Delta Eig. = 0.0011916788 eV \n \n current damping : 0.550\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 10 -75.955369532338 -122.33935554 37.582523947 0.198D-04 0.404D-11\n Norm of current diis error: 0.16682E-03\n max. resid. norm for Fia-block= 8.490D-05 for orbital 4a \n max. resid. fock norm = 8.646D-05 for orbital 4a \n mo-orthogonalization: Cholesky decomposition\n Delta Eig. = 0.0004737614 eV \n \n current damping : 0.600\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 11 -75.955369541949 -122.33936379 37.582532192 0.768D-05 0.389D-11\n Norm of current diis error: 0.68636E-04\n max. resid. norm for Fia-block= 3.492D-05 for orbital 4a \n max. resid. fock norm = 3.538D-05 for orbital 4a \n Delta Eig. = 0.0001759275 eV \n \n current damping : 0.650\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 12 -75.955369543464 -122.33935314 37.582521542 0.299D-05 0.377D-11\n Norm of current diis error: 0.28699E-04\n max. resid. norm for Fia-block= 1.386D-05 for orbital 4a \n max. resid. fock norm = 1.403D-05 for orbital 4a \n Delta Eig. = 0.0000713983 eV \n\n ENERGY & DENSITY CONVERGED !\n\n \n current damping : 0.500\n ITERATION ENERGY 1e-ENERGY 2e-ENERGY RMS[dD(SAO)] TOL\n 13 -75.955369543698 -122.33935200 37.582520398 0.116D-05 0.380D-11\n max. resid. norm for Fia-block= 3.062D-06 for orbital 4a \n max. resid. fock norm = 3.077D-06 for orbital 4a \n\n convergence criteria satisfied after 13 iterations\n\n\n *************************************************************************\n QCEngine Turbomole \n *************************************************************************\n\n\n ------------------------------------------ \n | total energy = -75.95536954370 |\n ------------------------------------------ \n : kinetic energy = 75.63206605915 :\n : potential energy = -151.58743560285 :\n : virial theorem = 1.99574350719 :\n : wavefunction norm = 1.00000000000 :\n .......................................... \n\n\n : there is no data group $energy \n\n\n : $end is missing \n\n\n orbitals $scfmo will be written to file mos\n\n irrep 1a 2a 3a 4a 5a \n eigenvalues H -20.55250 -1.29794 -0.68127 -0.56035 -0.49510\n eV -559.2664 -35.3190 -18.5384 -15.2480 -13.4725\n occupation 2.0000 2.0000 2.0000 2.0000 2.0000 \n\n irrep 6a 7a 8a 9a 10a \n eigenvalues H 0.16932 0.24840 0.76485 0.82701 1.18642\n eV 4.6076 6.7593 20.8129 22.5044 32.2845\n \n \n \n \n ==============================================================================\n electrostatic moments\n ==============================================================================\n\n reference point for electrostatic moments: 0.00000 0.00000 0.00000\n\n \n nuc elec -> total\n ------------------------------------------------------------------------------\n charge \n ------------------------------------------------------------------------------\n 10.000000 -10.000000 -0.000000\n \n ------------------------------------------------------------------------------\n dipole moment \n ------------------------------------------------------------------------------\n x 0.000000 0.000000 0.000000\n y 0.000000 0.000000 0.000000\n z 1.019078 -0.162829 0.856249\n \n | dipole moment | = 0.8562 a.u. = 2.1764 debye \n \n ------------------------------------------------------------------------------\n quadrupole moment\n ------------------------------------------------------------------------------\n xx 0.000000 -5.305745 -5.305745\n yy 4.465192 -7.479074 -3.013882\n zz 2.245407 -6.491368 -4.245961\n xy 0.000000 0.000000 0.000000\n xz 0.000000 0.000000 0.000000\n yz 0.000000 -0.000000 -0.000000\n \n 1/3 trace= -4.188529\n anisotropy= 1.986680\n \n ==============================================================================\n \n\n\n ------------------------------------------------------------------------\n total cpu-time : 0.11 seconds\n total wall-time : 0.07 seconds\n ------------------------------------------------------------------------\n\n **** dscf : all done ****\n\n\n 2019-10-29 09:56:52.696 \n\n\n OpenMP run-time library returned nthreads = 2\n\n ricc2 (southgeorgia) : TURBOMOLE V7.3 ( 22142 ) 4 Jul 2018 at 18:12:42\n Copyright (C) 2018 TURBOMOLE GmbH, Karlsruhe\n\n\n 2019-10-29 09:56:52.765 \n\n\n\n R I C C 2 - PROGRAM\n\n the quantum chemistry groups\n at the universities in\n Karlsruhe, Bochum & Mainz\n Germany\n\n\n\n *-----------------------------------------------------------------------*\n | program will use 2 thread(s) |\n *-----------------------------------------------------------------------*\n\n\n +--------------------------------------------------+\n | Atomic coordinate, charge and isotop information |\n +--------------------------------------------------+\n\n atomic coordinates atom charge isotop\n 0.00000000 0.00000000 -0.12947694 o 8.000 0\n 0.00000000 -1.49418734 1.02744651 h 1.000 0\n 0.00000000 1.49418734 1.02744651 h 1.000 0\n \n center of nuclear mass : 0.00000000 0.00000000 -0.00001570\n center of nuclear charge: 0.00000000 0.00000000 0.10190775\n\n *************************************************************************\n QCEngine Turbomole \n *************************************************************************\n\n\n\n +--------------------------------------------------+\n | basis set information |\n +--------------------------------------------------+\n\n we will work with the 1s 3p 5d 7f 9g ... basis set\n ...i.e. with spherical basis functions...\n\n type atoms prim cont basis\n ---------------------------------------------------------------------------\n o 1 24 14 def2-SVP [3s2p1d|7s4p1d]\n h 2 7 5 def2-SVP [2s1p|4s1p]\n ---------------------------------------------------------------------------\n total: 3 38 24\n ---------------------------------------------------------------------------\n\n total number of primitive shells : 17\n total number of contracted shells : 12\n total number of cartesian basis functions : 25\n total number of SCF-basis functions : 24\n\n\n symmetry group of the molecule : c1 \n\n the group has the following generators :\n c1(z)\n\n 1 symmetry operations found\n\n there are 1 real representations : a \n\n\n =========================================================================\n\n\n restricted closed shell calculation for the wavefunction models:\n MP2 - Second Order Moeller Plesset PT\n\n\n global parameters for ricc2 program:\n\n hard restart (reuse of interm.) : disabled\n soft restart (reuse of vectors) : disabled\n threshold for vector function : 0.100000E-05\n convergence threshold energy : 0.100000E-06\n linear dependence threshold : 0.100000E-13\n global print level : 1\n maximum number of iterations : 150\n maximum number DIIS vectors : 10\n max. dim. of reduced space : 100\n core memory limit (MB) : 1000\n\n\n this is a ground state geometry optimization run at the MP2 level\n\n Scratch Directory : \n\n\n =========================================================================\n\n der. integral neglect threshold : 0.10E-07\n integral neglect threshold : 0.13E-09\n integral storage threshold THIZE : 0.10E-04\n integral storage threshold THIME : 5\n\n\n +------------------------------------------+\n | Auxiliary basis set information |\n +------------------------------------------+\n\n we will work with the 1s 3p 5d 7f 9g ... basis set\n ...i.e. with spherical basis functions...\n\n type atoms prim cont basis\n ---------------------------------------------------------------------------\n o 1 72 48 def2-SVP [6s5p4d1f|8s6p5d3f]\n h 2 23 14 def2-SVP [3s2p1d|4s3p2d]\n ---------------------------------------------------------------------------\n total: 3 118 76\n ---------------------------------------------------------------------------\n\n total number of primitive shells : 31\n total number of contracted shells : 28\n total number of cartesian basis functions : 85\n total number of SCF-basis functions : 76\n\n\n maximum number of shells which are related by symmetry : 1\n\n\n The symmetry information takes 1 MB\n\n*---------------------------------------------------------------------*\n| simplified C1 algorithm will be applied |\n*---------------------------------------------------------------------*\n MOs are in ASCII format !\n\n\n reading orbital data $scfmo from file mos\n orbital characterization : scfconv=8\n time elapsed for calculating density matrices : 0.000 sec\n\n\n\n +--------------------------------------------+\n | list of orbitals that will be kept frozen: |\n +--------------------------------------------+\n | irrep index energy occ/vir |\n +--------------------------------------------+\n | a 1 -20.5525 occupied |\n +--------------------------------------------+\n\n\n\n Molecular Orbital Statistic:\n ============================\n\n -----------------------------\n orbitals in total:\n -----------------------------\n frozen occupied : 1\n active occupied : 4\n active virtual : 19\n frozen virtual : 0\n all together : 24\n -----------------------------\n\n\n time in riccmos cpu: 0.00 sec wall: 0.00 sec ratio: 1.6\n\n total memory allocated for calculation of (Q|P)**(-1/2) : 1 MiB\n\n\n calculation of (P|Q) ...\n time in lp2sym cpu: 0.00 sec wall: 0.00 sec ratio: 0.7\n\n\n calculation of the Cholesky decomposition of (P|Q)**(-1) ...\n time in invmetri cpu: 0.00 sec wall: 0.00 sec ratio: 2.3\n\n threshold for RMS(d[D]) in SCF was : 0.10E-06\n integral neglect threshold : 0.13E-09\n derivative integral neglect threshold : 0.10E-07\n\n\n setting up bound for integral derivative estimation\n\n increment for numerical differentiation : 0.00050000\n\n =========================================================================\n\n Energy of reference wave function is -75.9553695437000\n Maximum orbital residual is 0.3076940140073E-05\n\n\n Number of symmetry-nonredundant auxiliary basis functions: 76\n\n Block lengths for integral files:\n frozen occupied (BOI): 1 MiB\n active occupied (BJI): 1 MiB\n active virtual (BAI): 1 MiB\n frozen virtual (BGI): 0 MiB\n general (BTI): 1 MiB\n\n =========================================================================\n\n\n\n A SCF based gradient calculation with 4-index ERIs will be done.\n Solution of CPHF equation will be preoptimized using RI-CPHF.\n A \"cbas\" type auxiliary basis is used for RI-SCF and/or RI-CPHF.\n\n The semi-canonical algorithm will be used for densities\n\n\n ======== CC DENSITY MODULE ========\n\n current wave-function model: MP2 \n\n calculating CC ground state density\n\n a semicanonical algorithm will be used when possible\n\n density nr. cpu/min wall/min L R\n ------------------------------------------------------\n 1 0.00 0.00 L0 R0 \n ------------------------------------------------------\n time in cc_1den cpu: 0.01 sec wall: 0.00 sec ratio: 1.7\n\n reading orbital data $scfmo from file mos\n orbital characterization : scfconv=8\n\n EMP2 : -76.159361407452\n EMP2 from traces: -76.159361407452\n Delta : 0.000000000000\n\n --------------------------------------------------------------------------\n\n Solving 4-Index ERI based CPHF equations using a RI-CPHF\n preoptimization with a \"cbas\" type auxiliary basis\n\n\n ======== LINEAR CC RESPONSE SOLVER ========\n\n threshold for convergence: 0.32E-02\n maximum number of simultaneous jacoby matrix transformations: 1\n \n summary of start vectors generation:\n -------------------------------------------\n type of solution vectors : l0o\n symmetry : a \n number of vectors requested : 1\n number of vectors generated : 1\n -------------------------------------------\n \n\n Iter #Vectors time (min) max. residual \n --------------------------------------------\n 1 1 0.00 0.76E-01 ( 1)\n 2 1 0.00 0.25E-01 ( 1)\n 3 1 0.00 0.86E-02 ( 1)\n 4 1 0.00 0.19E-02 ( 1)\n --------------------------------------------\n converged in 4 iterations\n\n Total time 0.00 minutes\n\n\n ===========================================\n\n\n\n\n ======== LINEAR CC RESPONSE SOLVER ========\n\n threshold for convergence: 0.10E-04\n maximum number of simultaneous jacoby matrix transformations: 1\n \n summary of start vectors generation:\n -------------------------------------------\n type of solution vectors : l0o\n symmetry : a \n number of vectors requested : 1\n number of vectors generated : 1\n -------------------------------------------\n \n\n Iter #Vectors time (min) max. residual \n --------------------------------------------\n 1 1 0.00 0.19E-02 ( 1)\n 2 1 0.00 0.56E-03 ( 1)\n 3 1 0.00 0.16E-03 ( 1)\n 4 1 0.00 0.46E-04 ( 1)\n 5 1 0.00 0.81E-05 ( 1)\n --------------------------------------------\n converged in 5 iterations\n\n Total time 0.00 minutes\n\n\n ===========================================\n\n\n --------------------------------------------------------------------------\n\n 1e-integral 1st. derivatives will be neglected if expon. factor < 0.100000E-11\n\n \n\n +--------------------------------------------------------------------+\n | MP2 unrelaxed natural orbital occupation numbers |\n +--------------------------------------------------------------------+\n | natural orb. | occupation numbers |\n +---------------+----------------------------------------------------+\n | a | |\n | 1 - 5 | 2.0000 1.9862 1.9737 1.9687 1.9665 |\n | 6 - 10 | 0.0252 0.0230 0.0178 0.0110 0.0054 |\n | 11 - 15 | 0.0051 0.0045 0.0040 0.0038 0.0010 |\n +---------------+----------------------------------------------------+\n natural orbitals with occ < 0.10E-02 are not shown\n\n Maximum change in occupation number:\n occupied : -1.68 % ( 5 a )\n virtual : 1.26 % ( 6 a )\n \n\n +--------------------------------------------------------------------+\n | MP2 relaxed natural orbital occupation numbers |\n +--------------------------------------------------------------------+\n | natural orb. | occupation numbers |\n +---------------+----------------------------------------------------+\n | a | |\n | 1 - 5 | 2.0000 1.9862 1.9738 1.9690 1.9667 |\n | 6 - 10 | 0.0252 0.0229 0.0178 0.0110 0.0054 |\n | 11 - 14 | 0.0051 0.0045 0.0040 0.0038 |\n +---------------+----------------------------------------------------+\n natural orbitals with occ < 0.10E-02 are not shown\n\n Maximum change in occupation number:\n occupied : -1.66 % ( 5 a )\n virtual : 1.26 % ( 6 a )\n\n\n **************************************************************\n * *\n *<<<<<<<<<< GROUND STATE FIRST-ORDER PROPERTIES >>>>>>>>>>>*\n * *\n **************************************************************\n\n\n ------------------------------------------------\n Method : MP2 \n Total Energy : -76.1593614075\n ------------------------------------------------\n\n\n ------------------------------------------------\n moments of inertia \n 2.396 4.501 6.897 in a.u.\n\n rotational constants \n 25.12153 13.37539 8.72824 in cm**(-1)\n ------------------------------------------------\n\n\n NOTE: unrelaxed properties printed below refer to the MP1 wavefunction and\n are only correct through first order in the fluctuation potential!\n\n +===========================================================================+\n | OPERATOR | EXPECTAT. VALUE | ELECTRONIC CONT. | NUCLEAR CONTRIB. |\n +==================+==================+==================+==================+\n | | | | |\n | xdiplen (unrel) | 0.00000000 | 0.00000000 | 0.00000000 | \n | xdiplen (relax) | 0.00000000 | 0.00000000 | 0.00000000 | \n | | | | |\n | ydiplen (unrel) | -0.00000000 | -0.00000000 | 0.00000000 | \n | ydiplen (relax) | -0.00000000 | -0.00000000 | 0.00000000 | \n | | | | |\n | zdiplen (unrel) | 0.84597301 | -0.17310449 | 1.01907750 | \n | zdiplen (relax) | 0.81508212 | -0.20399538 | 1.01907750 | \n | | | | |\n | xxqudlen (unrel) | -5.33021521 | -5.33021521 | 0.00000000 | \n | xxqudlen (relax) | -5.34598293 | -5.34598293 | 0.00000000 | \n | | | | |\n | xyqudlen (unrel) | 0.00000000 | 0.00000000 | 0.00000000 | \n | xyqudlen (relax) | 0.00000000 | 0.00000000 | 0.00000000 | \n | | | | |\n | xzqudlen (unrel) | 0.00000000 | 0.00000000 | 0.00000000 | \n | xzqudlen (relax) | 0.00000000 | 0.00000000 | 0.00000000 | \n | | | | |\n | yyqudlen (unrel) | -3.06374549 | -7.52893711 | 4.46519161 | \n | yyqudlen (relax) | -3.13716994 | -7.60236156 | 4.46519161 | \n | | | | |\n | yzqudlen (unrel) | -0.00000000 | -0.00000000 | 0.00000000 | \n | yzqudlen (relax) | -0.00000000 | -0.00000000 | 0.00000000 | \n | | | | |\n | zzqudlen (unrel) | -4.28312315 | -6.52853004 | 2.24540689 | \n | zzqudlen (relax) | -4.32737024 | -6.57277712 | 2.24540689 | \n | | | | |\n +===========================================================================+\n\n\n Analysis of relaxed properties:\n ===============================\n\n\n dipole moment:\n --------------\n\n x 0.00000000\n y -0.00000000\n z 0.81508212\n\n | dipole moment | = 0.81508212 a.u. = 2.07173208 debye\n\n\n traceless quadrupole tensor:\n ----------------------------\n\n x y z\n\n x -1.61371284 0.00000000 0.00000000\n y 1.69950664 -0.00000000\n z -0.08579380\n\n\n principal axes of tensor: \n\n x' = ( 1.0000000, 0.0000000, 0.0000000 )\n y' = ( 0.0000000, 1.0000000, -0.0000000 )\n z' = ( 0.0000000, 0.0000000, 1.0000000 )\n\n < Q(x'x') > = -1.61371284 a.u.\n < Q(y'y') > = 1.69950664 a.u.\n < Q(z'z') > = -0.08579380 a.u.\n\n\n second moment of electron density:\n ----------------------------------\n\n x y z\n\n x 5.34598293 -0.00000000 -0.00000000\n y 7.60236156 0.00000000\n z 6.57277712\n\n\n principal axes of tensor: \n\n x' = ( 1.0000000, 0.0000000, 0.0000000 )\n y' = ( 0.0000000, 1.0000000, 0.0000000 )\n z' = ( 0.0000000, 0.0000000, 1.0000000 )\n\n < x'x'> = 5.34598293 a.u.\n < y'y'> = 7.60236156 a.u.\n < z'z'> = 6.57277712 a.u.\n\n Isotropic second moment: alpha = 6.50704053 a.u.\n\n Anisotropy of second moment: beta = 1.95656748 a.u.\n \n\n --------------------------------------------------------------------------\n\n\n Analysis of unrelaxed properties:\n =================================\n\n\n dipole moment:\n --------------\n\n x 0.00000000\n y -0.00000000\n z 0.84597301\n\n | dipole moment | = 0.84597301 a.u. = 2.15024890 debye\n\n\n traceless quadrupole tensor:\n ----------------------------\n\n x y z\n\n x -1.65678089 0.00000000 0.00000000\n y 1.74292369 -0.00000000\n z -0.08614280\n\n\n principal axes of tensor: \n\n x' = ( 1.0000000, 0.0000000, 0.0000000 )\n y' = ( 0.0000000, 1.0000000, -0.0000000 )\n z' = ( 0.0000000, 0.0000000, 1.0000000 )\n\n < Q(x'x') > = -1.65678089 a.u.\n < Q(y'y') > = 1.74292369 a.u.\n < Q(z'z') > = -0.08614280 a.u.\n\n\n second moment of electron density:\n ----------------------------------\n\n x y z\n\n x 5.33021521 -0.00000000 -0.00000000\n y 7.52893711 0.00000000\n z 6.52853004\n\n\n principal axes of tensor: \n\n x' = ( 1.0000000, 0.0000000, 0.0000000 )\n y' = ( 0.0000000, 1.0000000, 0.0000000 )\n z' = ( 0.0000000, 0.0000000, 1.0000000 )\n\n < x'x'> = 5.33021521 a.u.\n < y'y'> = 7.52893711 a.u.\n < z'z'> = 6.52853004 a.u.\n\n Isotropic second moment: alpha = 6.46256078 a.u.\n\n Anisotropy of second moment: beta = 1.90671848 a.u.\n \n\n ------------------------------\n total gradient of MP2 energy \n ------------------------------\n\n ------------------------------------------------\n cartesian gradient of the energy (hartree/bohr)\n ------------------------------------------------\n\n ATOM 1 o 2 h 3 h \ndE/dx 0.7763893D-13 -0.3870495D-13 -0.3893398D-13\ndE/dy 0.0000000D+00 -0.3122867D-01 0.3122867D-01\ndE/dz -0.3503421D-01 0.1751710D-01 0.1751710D-01\n \n resulting FORCE (fx,fy,fz) = (0.252D-28,0.755D-14,-.148D-10)\n resulting MOMENT (mx,my,mz) = (0.684D-14,-.898D-13,0.342D-15)\n\n\n **********************************************************************\n |maximum component of gradient| : 0.35034210E-01 (atom 1 o ) \n gradient norm : 0.61575592E-01\n **********************************************************************\n\n *** orbital-relaxed dipole moment written to ***\n\n\n : data group $grad is missing \n\n *** cartesian gradients written onto ***\n \n ==============================================================================\n \n Exporting ground state density\n\n\n ------------------------------------------------------------------------\n total cpu-time : 0.16 seconds\n total wall-time : 0.09 seconds\n ------------------------------------------------------------------------\n\n **** ricc2 : all done ****\n\n\n 2019-10-29 09:56:52.850 \n\n", + "stderr": null, + "success": true, + "error": null +} \ No newline at end of file diff --git a/iodata/test/data/xtb_water_no_basis.json b/iodata/test/data/xtb_water_no_basis.json new file mode 100644 index 000000000..e29064f31 --- /dev/null +++ b/iodata/test/data/xtb_water_no_basis.json @@ -0,0 +1,64 @@ +{ + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2, + "symbols": [ + "O", + "H", + "H" + ], + "geometry": [ + 0.0, + 0.0, + -0.12947694, + 0.0, + -1.49418734, + 1.02744651, + 0.0, + 1.49418734, + 1.02744651 + ], + "provenance": { + "creator": "QCElemental", + "routine": "unknown" + }, + "connectivity": [ + [ + 0, + 1, + 1.0 + ], + [ + 0, + 2, + 1.0 + ] + ] + }, + "driver": "energy", + "model": { + "method": "XTB", + "basis": "" + }, + "keywords": {}, + "id": null, + "schema_name": "qcschema_output", + "schema_version": 1, + "provenance": { + "creator": "QCElemental", + "version": "v0.2.6", + "routine": "qcelemental.models.results" + }, + "success": true, + "return_result": -5.7659905206521511, + "properties": { + "scf_total_energy": -5.7659905206521511, + "scf_iterations": 7, + "calcinfo_nbasis": 8, + "calcinfo_natom": 3 + }, + "extras": { + "scf_converged": true + }, + "error": null +} \ No newline at end of file diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 0ac030973..15528c008 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -25,7 +25,7 @@ import pytest from ..api import dump_one, load_one -from ..utils import FileFormatWarning +from ..utils import FileFormatError, FileFormatWarning try: @@ -305,3 +305,70 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): if "provenance" in mol2: del mol2["provenance"] assert mol1 == mol2 + + +# output_files: (filename, lot, obasis_name, run_type, nwarn) +OUTPUT_FILES = [ + ("H2O_CCSDprTpr_STO3G_output.json", "CCSD(T)", "sto-3g", None, 0), + ("LiCl_STO4G_Gaussian_output.json", "HF", "STO-4G", "Freq", 0), + ("xtb_water_no_basis.json", "XTB", None, None, 3), +] + + +@pytest.mark.parametrize("filename, lot, obasis_name, run_type, nwarn", OUTPUT_FILES) +def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): + with path("iodata.test.data", filename) as qcschema_output: + if nwarn == 0: + mol = load_one(str(qcschema_output)) + else: + with pytest.warns(FileFormatWarning) as record: + mol = load_one(str(qcschema_output)) + assert len(record) == nwarn + + assert mol.lot == lot + assert mol.obasis_name == obasis_name + assert mol.run_type == run_type + + +# Not a single valid example of qcschema_molecule is easily found for anything but water +# Some of these files have been manually validated, as reflected in the provenance +# bad_mol_files: (filename, error) +BAD_OUTPUT_FILES = [ + ("turbomole_water_energy_hf_output.json", FileFormatError), + ("turbomole_water_gradient_rimp2_output.json", FileFormatError), +] + + +@pytest.mark.parametrize("filename, error", BAD_OUTPUT_FILES) +def test_bad_qcschema_files(filename, error): + # FIXME: these will move + with path('iodata.test.data', filename) as qcschema_input: + with pytest.raises(error): + load_one(str(qcschema_input)) + + +INOUT_OUTPUT_FILES = [ + "H2O_CCSDprTpr_STO3G_output.json", + "LiCl_STO4G_Gaussian_output.json", +] + + +@pytest.mark.parametrize("filename", INOUT_OUTPUT_FILES) +def test_inout_qcschema_output(tmpdir, filename): + """Test that loading and dumping qcschema_molecule files retains all data.""" + with path("iodata.test.data", filename) as qcschema_input: + mol = load_one(str(qcschema_input)) + mol1 = json.loads(qcschema_input.read_bytes()) + + fn_tmp = os.path.join(tmpdir, 'test_input_mol.json') + dump_one(mol, fn_tmp) + + with open(fn_tmp, "r") as mol2_in: + mol2 = json.load(mol2_in) + + # IOData prints the most recent version, and it's not worth updating all test files each time + if "provenance" in mol1: + del mol1["provenance"] + if "provenance" in mol2: + del mol2["provenance"] + assert mol1 == mol2 \ No newline at end of file From 302c133e82451004900a6cca0455f1a81755fbed Mon Sep 17 00:00:00 2001 From: William Adams Date: Tue, 6 Apr 2021 18:54:55 -0400 Subject: [PATCH 024/144] Fix linting issues --- iodata/test/test_json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 15528c008..d91ea4816 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -371,4 +371,4 @@ def test_inout_qcschema_output(tmpdir, filename): del mol1["provenance"] if "provenance" in mol2: del mol2["provenance"] - assert mol1 == mol2 \ No newline at end of file + assert mol1 == mol2 From 9a666529a0960401d7cc26db1cef0377a94014a4 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Wed, 14 Apr 2021 11:55:19 +0200 Subject: [PATCH 025/144] Change bond type 1 to unknown in pdb --- iodata/formats/pdb.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 5b6b4a1c6..2df235824 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -31,7 +31,7 @@ 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 ..periodic import sym2num, num2sym, bond2num from ..utils import angstrom, LineIterator @@ -142,7 +142,7 @@ def load_one(lit: LineIterator) -> dict: molecule_found = True if line.startswith("CONECT"): for iatom0, iatom1 in _parse_pdb_conect_line(line): - bonds.append([iatom0, iatom1, 1]) + bonds.append([iatom0, iatom1, bond2num["un"]]) if line.startswith("END") and molecule_found: end_reached = True break From 2a2717bf9070255f68725fe6f2f87bbf30ca81f1 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Wed, 14 Apr 2021 12:07:34 +0200 Subject: [PATCH 026/144] Fix few issues in CONECT dumping --- iodata/formats/pdb.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 2df235824..41859c3a4 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -209,22 +209,22 @@ def dump_one(f: TextIO, data: IOData): 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) - # Prepare for CONECT lines. - connections = [[] for iatom in range(data.natom)] if data.bonds is not None: + # Prepare for CONECT lines. + connections = [[] for iatom in range(data.natom)] for iatom0, iatom1 in data.bonds[:, :2]: connections[iatom0].append(iatom1) connections[iatom1].append(iatom0) - # Write CONECT lines. - for iatom0, iatoms1 in enumerate(connections): - if len(iatoms1) > 0: - # Write connection in groups of max 4 - for ichunk in range(len(iatoms1) // 4 + 1): - print("CONECT{:5d}{}".format( - iatom0 + 1, "".join( + # Write CONECT lines. + for iatom0, iatoms1 in enumerate(connections): + if len(iatoms1) > 0: + # Write connection in groups of max 4 + 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]) - ), file=f) + conect_line = f"CONECT{iatom0 + 1:5d}{other_atoms_str}" + print(conect_line, file=f) print("END", file=f) From 4a868cd68805ef080eb7faf8894eb5f57899fb9d Mon Sep 17 00:00:00 2001 From: tovrstra Date: Wed, 14 Apr 2021 12:14:31 +0200 Subject: [PATCH 027/144] Disable caching on venv tests --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f5d9e79e..80d6d4dd6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,10 +45,6 @@ jobs: with: python-version: ${{ matrix.python-version }} architecture: x64 - - uses: actions/cache@v2 - with: - path: ~/.local/venvs - key: ${{ runner.os }}-Python-${{ matrix.python-version }}-venv - name: Install Pip and Roberto run: | python -m pip install --upgrade pip From 54b412728c3330ca082549961deeb1b81e0d94e6 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Wed, 14 Apr 2021 12:55:42 +0200 Subject: [PATCH 028/144] Support multiline TITLE and improve handling of COMPND --- iodata/formats/pdb.py | 54 ++++++++++++++++++++++--- iodata/test/data/water_single_model.pdb | 1 + iodata/test/test_pdb.py | 21 ++++++++++ 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 41859c3a4..1370e06a9 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -106,9 +106,10 @@ def _parse_pdb_conect_line(line): @document_load_one("PDB", ['atcoords', 'atnums', 'atffparams', 'extra'], ['title', 'bonds']) -def load_one(lit: LineIterator) -> dict: +def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, too-many-statements """Do not edit this docstring. It will be overwritten.""" - title = "PDB file from IOData" + title_lines = [] + compnd_lines = [] atnums = [] attypes = [] restypes = [] @@ -126,8 +127,10 @@ def load_one(lit: LineIterator) -> dict: except StopIteration: break # If the PDB file has a title, replace the default. - if line.startswith("TITLE") or line.startswith("COMPND"): - title = line[10:].rstrip() + if line.startswith("TITLE"): + title_lines.append(line[10:].strip()) + 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) @@ -151,15 +154,33 @@ def load_one(lit: LineIterator) -> dict: if not end_reached: lit.warn("The END is not found, but the parsed data is returned!") + # Data related to force fields atffparams = { "attypes": np.array(attypes), "restypes": np.array(restypes), "resnums": np.array(resnums), } - extra = {"occupancies": np.array(occupancies), "bfactors": np.array(bfactors)} + # Extra data + extra = { + "occupancies": np.array(occupancies), + "bfactors": np.array(bfactors), + } + 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)): extra["chainids"] = np.array(chainids) + # Set a useful title + if len(title_lines) == 0: + # Some files use COMPND instead of TITLE, in which case COMPND will be + # used as title. + if "compound" in extra: + title = extra["compound"] + del extra["compound"] + else: + title = "PDB file loaded by IOData" + else: + title = "\n".join(title_lines) result = { 'atcoords': np.array(atcoords), 'atnums': np.array(atnums), @@ -185,10 +206,31 @@ def load_many(lit: LineIterator) -> Iterator[dict]: return +def _dump_multiline_str(f: TextIO, key: str, value: str): + r"""Write a multiline string in PDB format. + + Parameters + ---------- + f + A file object to write to. + key + The key used to prefix the multiline string, e.g. `"TITLE"`. + value + A (multiline) string, with multiple lines separated by `\n`. + + """ + prefix = key.ljust(10) + for iline, line in enumerate(value.split("\n")): + print(prefix + line, file=f) + prefix = key + str(iline + 2).rjust(10 - len(key)) + " " + + @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.""" - print(str("TITLE " + data.title) or "TITLE Created with IOData", file=f) + _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) diff --git a/iodata/test/data/water_single_model.pdb b/iodata/test/data/water_single_model.pdb index 17753ffbb..bd707dd41 100644 --- a/iodata/test/data/water_single_model.pdb +++ b/iodata/test/data/water_single_model.pdb @@ -1,4 +1,5 @@ MODEL +TITLE water HETATM 1 H HOH 0 0.784 -0.492 0.000 1.00 0.00 H HETATM 2 O HOH 1 0.000 0.062 0.000 1.00 0.00 O HETATM 3 H HOH 0 -0.784 -0.492 0.000 1.00 0.00 H diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index ca2f43fd1..c1b33eb3e 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -50,6 +50,7 @@ def test_load_water_no_end(): def check_water(mol): """Test some things on a water file.""" + assert mol.title == "water" assert_equal(mol.atnums, [1, 8, 1]) # check bond length assert_allclose(np.linalg.norm( @@ -182,6 +183,26 @@ def test_load_2bcw(): # test pdb with multiple chains with path("iodata.test.data", "2bcw.pdb") as fn_pdb: mol = load_one(fn_pdb) + 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"] == """\ +MOL_ID: 1; +MOLECULE: 50S RIBOSOMAL PROTEIN L11; +CHAIN: A; +FRAGMENT: N-TERMINAL DOMAIN; +MOL_ID: 2; +MOLECULE: 50S RIBOSOMAL PROTEIN L7/L12; +CHAIN: B; +FRAGMENT: C-TERMINAL DOMAIN; +SYNONYM: L8; +MOL_ID: 3; +MOLECULE: ELONGATION FACTOR G; +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() From 80b698a887e2815add6880430da844ac881a6701 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 12:42:56 -0400 Subject: [PATCH 029/144] Make passthrough var and func names consistent --- iodata/formats/json.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 06408ab57..a932aa7d5 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -386,14 +386,14 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "id", "extras", } - passthrough_keys = _find_passthrough_keys(mol, molecule_keys) - if passthrough_keys: - topology_dict["extra"]["unparsed"] = passthrough_keys + passthrough_dict = _find_passthrough_dict(mol, molecule_keys) + if passthrough_dict: + topology_dict["extra"]["unparsed"] = passthrough_dict return topology_dict -def _find_passthrough_keys(result: dict, keys: set) -> dict: +def _find_passthrough_dict(result: dict, keys: set) -> dict: """Find all keys not specified for a given schema. Parameters @@ -586,9 +586,9 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: "protocols", "provenance", } - passthrough_keys = _find_passthrough_keys(result, input_keys) - if passthrough_keys: - input_dict["extra"]["unparsed"] = passthrough_keys + passthrough_dict = _find_passthrough_dict(result, input_keys) + if passthrough_dict: + input_dict["extra"]["unparsed"] = passthrough_dict return input_dict @@ -848,9 +848,9 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: "success", "return_result" } - passthrough_keys = _find_passthrough_keys(result, output_keys) - if passthrough_keys: - output_dict["extra"]["unparsed"] = passthrough_keys + passthrough_dict = _find_passthrough_dict(result, output_keys) + if passthrough_dict: + output_dict["extra"]["unparsed"] = passthrough_dict return output_dict From d1e87d7a513dc9cd3a4924f50fec66abc13dc8d4 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 12:46:14 -0400 Subject: [PATCH 030/144] Add prefix to unused basis func args --- iodata/formats/json.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index a932aa7d5..fec2da233 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -421,15 +421,14 @@ def _find_passthrough_dict(result: dict, keys: set) -> dict: return passthrough_dict -# pylint: disable=unused-argument -def _load_qcschema_basis(result: dict, lit: LineIterator) -> dict: +def _load_qcschema_basis(_result: dict, _lit: LineIterator) -> dict: """Load qcschema_basis properties. Parameters ---------- - result + _result The JSON dict loaded from file. - lit + _lit The line iterator holding the file data. Returns @@ -442,14 +441,14 @@ def _load_qcschema_basis(result: dict, lit: LineIterator) -> dict: raise NotImplementedError("qcschema_basis is not yet implemented in IOData.") -def _parse_basis_keys(basis: dict, lit: LineIterator) -> dict: +def _parse_basis_keys(_basis: dict, _lit: LineIterator) -> dict: """Parse basis keys for a QCSchema input, output, or basis file. Parameters ---------- - basis + _basis The basis dictionary from a QCSchema basis file or QCSchema input or output 'method' key. - lit + _lit The line iterator holding the file data. Returns @@ -725,7 +724,6 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: return protocols_dict -# pylint: disable=unused-argument def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: """Load qcschema_output properties. From 8840b18d7715a201885b2f022443be9d32f30447 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 13:44:17 -0400 Subject: [PATCH 031/144] Store run_type only if it's an expected value --- iodata/formats/json.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index fec2da233..59379d41c 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -552,10 +552,13 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: # Load keywords & store # Currently, only the IOData run_type attribute is specifically parsed from keywords, but this # is a good space for passing additional IOData-specific keywords, given that the official spec - # treats this as program-specific territory. + # treats this as program-specific territory. If run_type is not one of the values expected by + # IOData, it will be stored only in the extra_dict. if "keywords" in result: keywords_dict = result["keywords"] - if "run_type" in keywords_dict: + if "run_type" in keywords_dict and keywords_dict["run_type"].lower() in { + "energy", "energy_force", "opt", "scan", "freq" + }: input_dict["run_type"] = keywords_dict["run_type"] extra_dict["keywords"] = keywords_dict # Check for extras From 01c20c46be9a4db8dde46e1361ee4aa9002beb6b Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 13:45:47 -0400 Subject: [PATCH 032/144] Fix typo --- iodata/formats/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 59379d41c..9f7eb9754 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -764,7 +764,7 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: def _parse_output_keys(result: dict, lit: LineIterator) -> dict: - """Parse input keys for QCSchema input or output files. + """Parse output keys for QCSchema input or output files. Parameters ---------- From 490998fff30684101ab99b5760927a0182923ea1 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 13:48:04 -0400 Subject: [PATCH 033/144] Document _parse_provenance return value --- iodata/formats/json.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 9f7eb9754..f64f62c18 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -873,6 +873,10 @@ def _parse_provenance( append Append IOData provenance entry to provenance list? + Returns + ------- + base_provenance + The provenance data for a QCSchema file. """ if isinstance(provenance, dict): if "creator" not in provenance: From 6c3c31db4dfbfc66553ab4c237c97db6beff8def Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 13:49:52 -0400 Subject: [PATCH 034/144] Fix elif after raise --- iodata/formats/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index f64f62c18..1c35b1951 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -1145,7 +1145,7 @@ def _dump_qcschema_output(data: IOData) -> dict: output_dict["return_result"] = data.energy if "return_result" not in output_dict and "return_result" not in data.extra["output"]: raise FileFormatError("qcschema_output requires `return_result` field in extra['output'].") - elif "return_result" in data.extra["output"]: + if "return_result" in data.extra["output"]: output_dict["return_result"] = data.extra["output"]["return_result"] if "keywords" in data.extra["input"]: output_dict["keywords"] = data.extra["input"]["keywords"] From 556fd4d04c9e64695a9f16eea3fd34ad445dbaf1 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 14:03:43 -0400 Subject: [PATCH 035/144] Move version check to separate function --- iodata/formats/json.py | 74 ++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 1c35b1951..4414a9611 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -244,18 +244,7 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: # Save schema name & version extra_dict["schema_name"] = "qcschema_molecule" - try: - version = mol["schema_version"] - except KeyError: - version = -1 - if float(version) < 0 or float(version) > 2: - warn( - "{}: Unknown `qcschema_molecule` version {}, " - "loading may produce invalid results".format(lit.filename, version), - FileFormatWarning, - 2, - ) - extra_dict["schema_version"] = version + extra_dict["schema_version"] = _version_check(mol, 2, "qcschema_molecule", lit) # Geometry is in a flattened list, convert to N x 3 topology_dict["atcoords"] = np.array(mol["geometry"]).reshape(-1, 3) @@ -393,6 +382,39 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: return topology_dict +def _version_check(result: dict, max_version: float, schema_name: str, lit: LineIterator) -> str: + """Check whether the QCSchema version is a known version. + + Parameters + ---------- + result + The JSON dict loaded from file. + max_version + The highest (most recent) known version for the QCSchema type. + schema_name + The `schema_name` key of a QCSchema file. + lit + The line iterator holding the file data. + + Returns + ------- + version + The version of the QCSchema file, -1 if unknown version. + """ + try: + version = result["schema_version"] + except KeyError: + version = -1 + if float(version) < 0 or float(version) > max_version: + warn( + f"{lit.filename}: Unknown {schema_name} version {version}, " + "loading may produce invalid results", + FileFormatWarning, + 2, + ) + return version + + def _find_passthrough_dict(result: dict, keys: set) -> dict: """Find all keys not specified for a given schema. @@ -527,19 +549,7 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: # Save schema name & version extra_dict["schema_name"] = "qcschema_input" - try: - version = result["schema_version"] - except KeyError: - version = -1 - if float(version) < 1.0: - warn( - "{}: Unknown `qcschema_input` version {}, loading may produce invalid results".format( - lit.filename, version - ), - FileFormatWarning, - 2, - ) - extra_dict["schema_version"] = version + extra_dict["schema_version"] = _version_check(result, 1, "qcschema_input", lit) # Load driver extra_dict["driver"] = _parse_driver(result["driver"], lit) @@ -800,19 +810,7 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: extra_dict = dict() extra_dict["schema_name"] = "qcschema_output" - try: - version = result["schema_version"] - except KeyError: - version = -1 - if float(version) < 1.0: - warn( - "{}: Unknown `qcschema_input` version {}, loading may produce invalid results".format( - lit.filename, version - ), - FileFormatWarning, - 2, - ) - extra_dict["schema_version"] = version + extra_dict["schema_version"] = _version_check(result, 2, "qcschema_output", lit) extra_dict["return_result"] = result["return_result"] extra_dict["success"] = result["success"] From 3a97178c80407df32a91dfae1b66679ea0acb390 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 22 Apr 2021 14:16:06 -0400 Subject: [PATCH 036/144] Disable too-many-lines check --- .pylintrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pylintrc b/.pylintrc index a37e77164..715d3b73a 100644 --- a/.pylintrc +++ b/.pylintrc @@ -50,7 +50,7 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,too-many-arguments,too-many-locals,too-few-public-methods,fixme,invalid-name,duplicate-code,unsubscriptable-object,no-member +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,too-many-arguments,too-many-locals,too-few-public-methods,fixme,invalid-name,duplicate-code,unsubscriptable-object,no-member,too-many-lines # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option From f0391d80e09172fae43463d6f1fbd15d3c950030 Mon Sep 17 00:00:00 2001 From: William Adams Date: Fri, 23 Apr 2021 12:49:49 -0400 Subject: [PATCH 037/144] Document QCSchema usage --- iodata/formats/json.py | 59 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 4414a9611..8cc78f4c2 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -25,6 +25,65 @@ - Output: specifying QC program output for a specific Molecule - Basis: specifying a basis set for a specific Molecule +General Usage +------------- +The QCSchema format is intended to be a catch-all file format for storing and sharing QC calculation +data. Due to the wide number of possibilities of the data contained in a single file, not every +field in a QCSchema file directly corresponds to an IOData attribute. For example, `qcschema_output` +files allow for many fields capturing different energy contributions, especially for coupled-cluster +calculations. To accommodate this fact, IOData does not always assume the intent of the user; +instead, IOData ensures that every field in the file is stored in a structured manner. When a +QCSchema field does not correspond to an IOData attribute, that data is instead stored in the +`extra` dict, in a dictionary corresponding to the subschema where that data was found. In cases +where multiple subschema contain the relevant field (e.g. the Output subschema contains the entirety +of the Input subschema), the data will be found in the smallest subschema (for the example above, in +`IOData.extra["input"], not IOData.extra["output"]). + +Dumping an IOData instance to a QCSchema file involves adding relevant required (and optional, if +needed) fields to the necessary dictionaries in the `extra` dict. One exception is the `provenance` +field: if the only desired provenance data is the creation of the file by IOData, that data will be +added automatically. + +The following sections will describe the requirements of each subschema and the behaviour to expect +from IOData when loading in or dumping out a QCSchema file. + +Molecule +-------- +The `qcschema_molecule` subschema describes a molecular system, and contains the data necessary to +specify a molecular system and support I/O and manipulation processes. + +The required fields for a `qcschema_molecule` file are: + +====================== ============ ==================================================================================== +Field IOData attr. Description +====================== ============ ==================================================================================== +schema_name N/A The name of the QCSchema subschema. Fixed as `'qcschema_molecule'`. +schema_version N/A The version of the subschema specification. 2.0 is the current version. +symbols `atnums` An array of the atomic symbols for the system. +geometry `atcoords` An ordered array of XYZ atomic coordinates, corresponding to the order of `symbols`. +molecular_charge `charge` The net electrostatic charge of the molecule. Some writers assume a default of 0. +molecular_multiplicity `spinpol` The total multiplicity of this molecule. Some writers assume a default of 1. +provenance N/A Information about the file was generated, provided, and manipulated. +====================== ============ ==================================================================================== + +The following is an example of a minimal `qcschema_molecule` file: + +.. code-block :: JSON + + { + "schema_name": "qcschema_molecule", + "schema_version": 2, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0, + "molecular_multiplicity": 1, + "provenance": { + "creator": "HORTON3", + "routine": "Manual validation" + } + } + + The QCSchema subschema are in various levels of maturity, and are subject to change at any time without warning, as they are also used as the internal data representation for the QCElemental program. IOData currently supports the Molecule subschema for both ``load_one`` and ``dump_one``. From 7e5e5739d324a1c5fd529d6813d3c281ac7234e2 Mon Sep 17 00:00:00 2001 From: tovrstra Date: Fri, 23 Apr 2021 19:38:17 +0200 Subject: [PATCH 038/144] Wrap lines in table --- iodata/formats/json.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 8cc78f4c2..b11a83646 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -54,17 +54,23 @@ The required fields for a `qcschema_molecule` file are: -====================== ============ ==================================================================================== +====================== ============ ================================================= Field IOData attr. Description -====================== ============ ==================================================================================== -schema_name N/A The name of the QCSchema subschema. Fixed as `'qcschema_molecule'`. -schema_version N/A The version of the subschema specification. 2.0 is the current version. +====================== ============ ================================================= +schema_name N/A The name of the QCSchema subschema. Fixed as + `'qcschema_molecule'`. +schema_version N/A The version of the subschema specification. + 2.0 is the current version. symbols `atnums` An array of the atomic symbols for the system. -geometry `atcoords` An ordered array of XYZ atomic coordinates, corresponding to the order of `symbols`. -molecular_charge `charge` The net electrostatic charge of the molecule. Some writers assume a default of 0. -molecular_multiplicity `spinpol` The total multiplicity of this molecule. Some writers assume a default of 1. -provenance N/A Information about the file was generated, provided, and manipulated. -====================== ============ ==================================================================================== +geometry `atcoords` An ordered array of XYZ atomic coordinates, + corresponding to the order of `symbols`. +molecular_charge `charge` The net electrostatic charge of the molecule. + Some writers assume a default of 0. +molecular_multiplicity `spinpol` The total multiplicity of this molecule. + Some writers assume a default of 1. +provenance N/A Information about the file was generated, + provided, and manipulated. +====================== ============ ================================================= The following is an example of a minimal `qcschema_molecule` file: From 5fc98ab979602b136be358b1fba8bac60254a95e Mon Sep 17 00:00:00 2001 From: tovrstra Date: Thu, 29 Apr 2021 09:06:45 +0200 Subject: [PATCH 039/144] Add exception for new pylint check --- iodata/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/utils.py b/iodata/utils.py index 10156c8da..c8da5c269 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -72,7 +72,7 @@ def __init__(self, filename: str): """ self.filename = filename - self.f = open(filename) + self.f = open(filename) # pylint: disable=consider-using-with self.lineno = 0 self.stack = [] From f6aa1e58b232b883831c623d39a30ea8bcbe5e79 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 29 Apr 2021 14:32:53 -0400 Subject: [PATCH 040/144] Enhance provenance functions and tests --- iodata/formats/json.py | 60 +++++++++++-------- iodata/test/data/LiCl_string_STO4G_input.json | 2 +- iodata/test/test_json.py | 41 +++++++++++-- 3 files changed, 73 insertions(+), 30 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index b11a83646..d1cba6568 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -959,7 +959,7 @@ def _parse_provenance( raise FileFormatError("{}: Invalid `{}` provenance type".format(lit.filename, source)) if append: base_provenance.append( - {"creator": "IOData", "version": __version__, "routine": "iodata.formats.json"} + {"creator": "IOData", "version": __version__, "routine": "iodata.formats.json.load_one"} ) return base_provenance @@ -1068,14 +1068,7 @@ def _dump_qcschema_molecule(data: IOData) -> dict: molecule_dict["fix_com"] = data.extra["molecule"]["fix_com"] if "fix_orientation" in data.extra["molecule"]: molecule_dict["fix_orientation"] = data.extra["molecule"]["fix_orientation"] - if "provenance" in data.extra["molecule"]: - molecule_dict["provenance"] = data.extra["molecule"]["provenance"] - else: - molecule_dict["provenance"] = { - "creator": "IOData", - "version": __version__, - "routine": "iodata.formats.json", - } + molecule_dict["provenance"] = _dump_provenance(data, "molecule") if "id" in data.extra["molecule"]: molecule_dict["id"] = data.extra["molecule"]["id"] if "extras" in data.extra["molecule"]: @@ -1087,6 +1080,37 @@ def _dump_qcschema_molecule(data: IOData) -> dict: return molecule_dict +def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: + """Generate the provenance information for dumping an IOData instance to QCSchema. + + Parameters + ---------- + data + The IOData instance to dump to file. + source + The `extra` dict location for the dump file, to find provenance data. + + Returns + ------- + provenance + The provenance information for the IOData instance. + """ + new_provenance = { + "creator": "IOData", + "version": __version__, + "routine": "iodata.formats.json.dump_one", + } + if "provenance" in data.extra[source]: + provenance = data.extra[source]["provenance"] + if isinstance(provenance, dict): + return [provenance, new_provenance] + if isinstance(provenance, list): + provenance.append(new_provenance) + return provenance + raise FileFormatError("QCSchema provenance must be either a dict or list of dicts.") + return new_provenance + + def _dump_qcschema_input(data: IOData) -> dict: """Dump relevant attributes from IOData to qcschema_input. @@ -1138,14 +1162,7 @@ def _dump_qcschema_input(data: IOData) -> dict: # Remove 'keep_' from protocols keys (added in IOData for readability) for keep in data.extra["input"]["protocols"]: input_dict["protocols"][keep[5:]] = data.extra["input"]["protocols"][keep] - if "provenance" in data.extra["input"]: - input_dict["provenance"] = data.extra["input"]["provenance"] - else: - input_dict["provenance"] = { - "creator": "IOData", - "version": __version__, - "routine": "iodata.formats.json", - } + input_dict["provenance"] = _dump_provenance(data, "input") if "unparsed" in data.extra["input"]: for k in data.extra["input"]["unparsed"]: input_dict[k] = data.extra["input"]["unparsed"][k] @@ -1229,14 +1246,7 @@ def _dump_qcschema_output(data: IOData) -> dict: output_dict["stderr"] = data.extra["output"]["stdout"] if "wavefunction" in data.extra["output"]: output_dict["wavefunction"] = data.extra["output"]["wavefunction"] - if "provenance" in data.extra["input"]: - output_dict["provenance"] = data.extra["input"]["provenance"] - else: - output_dict["provenance"] = { - "creator": "IOData", - "version": __version__, - "routine": "iodata.formats.json", - } + output_dict["provenance"] = _dump_provenance(data, "input") if "unparsed" in data.extra["input"]: for k in data.extra["input"]["unparsed"]: output_dict[k] = data.extra["input"]["unparsed"][k] diff --git a/iodata/test/data/LiCl_string_STO4G_input.json b/iodata/test/data/LiCl_string_STO4G_input.json index 9d3fda82f..3f99c7de0 100644 --- a/iodata/test/data/LiCl_string_STO4G_input.json +++ b/iodata/test/data/LiCl_string_STO4G_input.json @@ -6,7 +6,7 @@ "schema_version": 2.0, "symbols": ["Li", "Cl"], "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], - "molecular_charge": 0, + "molecular_charge": 0.0, "molecular_multiplicity": 1, "real": [true, true], "provenance": [ diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index d91ea4816..f4083d7ca 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -144,6 +144,19 @@ def test_passthrough_qcschema_molecule(filename, unparsed_dict): assert len(record) == 1 +def _check_provenance(mol1, mol2): + """Test the provenance information, if available, to avoid updating version on test files.""" + if "provenance" not in mol1: + return isinstance(mol2["provenance"], dict) + if isinstance(mol1["provenance"], dict): + return mol1["provenance"] in mol2["provenance"] + if isinstance(mol1["provenance"], list): + for entry in mol1["provenance"]: + assert entry in mol2["provenance"] + return True + return False + + INOUT_MOL_FILES = [ ("LiCl_molecule.json", 0), ("Hydroxyl_radical_molecule.json", 0), @@ -171,8 +184,12 @@ def test_inout_qcschema_molecule(tmpdir, filename, nwarn): with open(fn_tmp, "r") as mol2_in: mol2 = json.load(mol2_in) - # print(mol1) - # print(mol2) + # Check that prior provenance info is kept + assert _check_provenance(mol1, mol2) + if "provenance" in mol1: + del mol1["provenance"] + if "provenance" in mol2: + del mol2["provenance"] assert mol1 == mol2 @@ -211,6 +228,12 @@ def test_inout_molssi_qcschema_molecule(tmpdir, filename): for key in keys: if isinstance(mol1[key], dict) and not bool(mol1[key]): del mol1[key] + # Check that prior provenance info is kept + assert _check_provenance(mol1, mol2) + if "provenance" in mol1: + del mol1["provenance"] + if "provenance" in mol2: + del mol2["provenance"] assert mol1 == mol2 @@ -299,11 +322,16 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): with open(fn_tmp, "r") as mol2_in: mol2 = json.load(mol2_in) - # IOData prints the most recent version, and it's not worth updating all test files each time + # Check that prior provenance info is kept + assert _check_provenance(mol1, mol2) if "provenance" in mol1: del mol1["provenance"] + if "provenance" in mol1["molecule"]: + del mol1["molecule"]["provenance"] if "provenance" in mol2: del mol2["provenance"] + if "provenance" in mol2["molecule"]: + del mol2["molecule"]["provenance"] assert mol1 == mol2 @@ -366,9 +394,14 @@ def test_inout_qcschema_output(tmpdir, filename): with open(fn_tmp, "r") as mol2_in: mol2 = json.load(mol2_in) - # IOData prints the most recent version, and it's not worth updating all test files each time + # Check that prior provenance info is kept + assert _check_provenance(mol1, mol2) if "provenance" in mol1: del mol1["provenance"] + if "provenance" in mol1["molecule"]: + del mol1["molecule"]["provenance"] if "provenance" in mol2: del mol2["provenance"] + if "provenance" in mol2["molecule"]: + del mol2["molecule"]["provenance"] assert mol1 == mol2 From f097d1c22d0eef62a0563135cbeed58c1be401a5 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 29 Apr 2021 14:33:43 -0400 Subject: [PATCH 041/144] Document QCSchema usage and add section headers --- iodata/formats/json.py | 530 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 493 insertions(+), 37 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index d1cba6568..b67cf0a77 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -20,6 +20,7 @@ """QCSchema JSON file format. QCSchema defines four different subschema: + - Molecule: specifying a molecular system - Input: specifying QC program input for a specific Molecule - Output: specifying QC program output for a specific Molecule @@ -29,50 +30,58 @@ ------------- The QCSchema format is intended to be a catch-all file format for storing and sharing QC calculation data. Due to the wide number of possibilities of the data contained in a single file, not every -field in a QCSchema file directly corresponds to an IOData attribute. For example, `qcschema_output` -files allow for many fields capturing different energy contributions, especially for coupled-cluster -calculations. To accommodate this fact, IOData does not always assume the intent of the user; -instead, IOData ensures that every field in the file is stored in a structured manner. When a -QCSchema field does not correspond to an IOData attribute, that data is instead stored in the -`extra` dict, in a dictionary corresponding to the subschema where that data was found. In cases +field in a QCSchema file directly corresponds to an IOData attribute. For example, +``qcschema_output`` files allow for many fields capturing different energy contributions, especially +for coupled-cluster calculations. To accommodate this fact, IOData does not always assume the intent +of the user; instead, IOData ensures that every field in the file is stored in a structured manner. +When a QCSchema field does not correspond to an IOData attribute, that data is instead stored in the +``extra`` dict, in a dictionary corresponding to the subschema where that data was found. In cases where multiple subschema contain the relevant field (e.g. the Output subschema contains the entirety of the Input subschema), the data will be found in the smallest subschema (for the example above, in -`IOData.extra["input"], not IOData.extra["output"]). +``IOData.extra["input"]``, not ``IOData.extra["output"]``). Dumping an IOData instance to a QCSchema file involves adding relevant required (and optional, if -needed) fields to the necessary dictionaries in the `extra` dict. One exception is the `provenance` -field: if the only desired provenance data is the creation of the file by IOData, that data will be -added automatically. +needed) fields to the necessary dictionaries in the ``extra`` dict. One exception is the +``provenance`` field: if the only desired provenance data is the creation of the file by IOData, +that data will be added automatically. The following sections will describe the requirements of each subschema and the behaviour to expect from IOData when loading in or dumping out a QCSchema file. -Molecule --------- -The `qcschema_molecule` subschema describes a molecular system, and contains the data necessary to +Schema Definitions +------------------ + +.. _json_schema_provenance: + +Provenance Information +^^^^^^^^^^^^^^^^^^^^^^ +The provenance field contains information about how the associated QCSchema object and its +attributes were generated, provided, and manipulated. A provenance entry expects these fields: + +======= =========== +Field Description +======= =========== +creator **Required**. The program that generated, provided, or manipulated this file. +version The version of the creator. +routine The routine of the creator. +======= =========== + +In QCElemental, only a single provenance entry is permitted. When generating a QCSchema file for use +with QCElemental, the easiest way to ensure compliance is to leave the provenance field blank, to +allow the ``dump_one`` function to generate the correct provenance information. However, allowing +only one entry for provenance information limits the ability to properly trace a file through +several operations during complex workflows. With this in mind, IOData supports an enhanced +provenance field, in the form of a list of provenance entries, with new entries appended to the end +of the list. + +.. _json_schema_molecule: + +Molecule Schema +^^^^^^^^^^^^^^^ +The ``qcschema_molecule`` subschema describes a molecular system, and contains the data necessary to specify a molecular system and support I/O and manipulation processes. -The required fields for a `qcschema_molecule` file are: - -====================== ============ ================================================= -Field IOData attr. Description -====================== ============ ================================================= -schema_name N/A The name of the QCSchema subschema. Fixed as - `'qcschema_molecule'`. -schema_version N/A The version of the subschema specification. - 2.0 is the current version. -symbols `atnums` An array of the atomic symbols for the system. -geometry `atcoords` An ordered array of XYZ atomic coordinates, - corresponding to the order of `symbols`. -molecular_charge `charge` The net electrostatic charge of the molecule. - Some writers assume a default of 0. -molecular_multiplicity `spinpol` The total multiplicity of this molecule. - Some writers assume a default of 1. -provenance N/A Information about the file was generated, - provided, and manipulated. -====================== ============ ================================================= - -The following is an example of a minimal `qcschema_molecule` file: +The following is an example of a minimal ``qcschema_molecule`` file: .. code-block :: JSON @@ -90,9 +99,456 @@ } -The QCSchema subschema are in various levels of maturity, and are subject to change at any time -without warning, as they are also used as the internal data representation for the QCElemental -program. IOData currently supports the Molecule subschema for both ``load_one`` and ``dump_one``. +The required fields and corresponding types for a ``qcschema_molecule`` file are: + +====================== ============ ============ ================================================= +Field Type IOData attr. Description +====================== ============ ============ ================================================= +schema_name str N/A The name of the QCSchema subschema. Fixed as + ``qcschema_molecule``. +schema_version str N/A The version of the subschema specification. + 2.0 is the current version. +symbols list(N_at) ``atnums`` An array of the atomic symbols for the system. +geometry list(3*N_at) ``atcoords`` An ordered array of XYZ atomic coordinates, + corresponding to the order of ``symbols``. The + first three elements correspond to atom one, + the second three to atom two, etc. +molecular_charge float ``charge`` The net electrostatic charge of the molecule. + Some writers assume a default of 0. +molecular_multiplicity int ``spinpol`` The total multiplicity of this molecule. + Some writers assume a default of 1. +provenance dict or list N/A Information about the file was generated, + provided, and manipulated. See Provenance section + above for more details. +====================== ============ ============ ================================================= + +Note: N_at corresponds to the number of atoms in the molecule, as defined by the length of +``symbols``. + +The optional fields and corresponding types for a ``qcschema_molecule`` file are: + +======================= ============ ============== ================================================== +Field Type IOData attr. Description +======================= ============ ============== ================================================== +atom_labels list(N_at) N/A Additional per-atom labels. Typically used for + model conversions, not user assignment. The + indices of this array correspond to the + ``symbols`` ordering. +atomic_numbers list(N_at) ``atnums`` An array of atomic numbers for each atom. + Typically inferred from ``symbols``. +comment str N/A Additional comments for this molecule. These + comments are intended for user information, not + any computational tasks. +connectivity list ``bonds`` The connectivity information between each atom + in the ``symbols`` array. Each entry in this + array is a 3-item array, + ``[index_a, index_b, bond_order]``, + where the indices correspond to the atom indices + in ``symbols``. +extras dict N/A Extra information to associate with this + molecule. +fix_symmetry str ``g_rot`` Maximal point group symmetry with which the + molecule should be treated. +fragments list(N_fr) N/A An array that designates which sets of atoms are + fragments within the molecule. This is a nested + array, with the indices of the base array + corresponding to the values in + ``fragment_charges`` and + ``fragment_multiplicities`` and the values in + the nested arrays corresponding to the indices + of ``symbols``. +fragment_charges list(N_fr) N/A The total charge of each fragment in + ``fragments``. The indices of this array + correspond to the ``fragments`` ordering. +fragment_multiplicities list(N_fr) N/A The multiplicity of each fragment in + ``fragments``. The indices of this array + correspond to the ``fragments`` ordering. +id str N/A A unique identifier for this molecule. +identifiers dict N/A Additional identifiers by which this molecule + can be referenced, such as INCHI, SMILES, etc. +real list(N_at) ``atcorenums`` An array indicating whether each atom is real + (true) or a ghost/virtual atom (false). The + indices of this array correspond to the + ``symbols`` ordering. +mass_numbers list(N_at) ``atmasses`` An array of atomic mass numbers for each atom. The + indices of this array correspond to the + ``symbols`` ordering. +masses list(N_at) ``atmasses`` An array of atomic masses [u] for each atom. + Typically inferred from ``symbols``. The indices + of this array correspond to the ``symbols`` + ordering. +name str ``title`` An arbitrary, common, or human-readable name to + assign to this molecule. +======================= ============ ============== ================================================== + +Note: N_at corresponds to the number of atoms in the molecule, as defined by the length of +``symbols``; N_fr corresponds to the number of fragments in the molecule, as defined by the length +of ``fragments``. Fragment data is stored in a sub-dictionary, ``fragments``. + +The following are additional optional keywords used in QCElemental's QCSchema implementation. These +keywords mostly correspond to specific QCElemental functionality, and may not necessarily produce +similar results in other QCSchema parsers. + +======================= ============ ================================================== +Field Type Description +======================= ============ ================================================== +fix_com bool An indicator to prevent pre-processing the + molecule by translating the COM to (0,0,0) in + Euclidean coordinate space. +fix_orientation bool An indicator to prevent pre-processing the + molecule by orienting via the inertia tensor. +validated bool An indicator that the input molecule data has been + previously checked for schema and physics (e.g. + non-overlapping atoms, feasible multiplicity) + compliance. Generally should only be true when set + by a trusted validator. +======================= ============ ================================================== + +.. _json_schema_input: + +Input Schema +^^^^^^^^^^^^ +The ``qcschema_input`` subschema describes all data necessary to generate and parse a QC program +input file for a given molecule. + +The following is an example of a minimal ``qcschema_input`` file: + +.. code-block :: JSON + + { + "schema_name": "qcschema_input", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "provenance": { + "creator": "HORTON3", + "routine": "Manual validation" + } + }, + "driver": "energy", + "model": { + "method": "B3LYP", + "basis": "Def2TZVP" + } + } + +The required fields and corresponding types for a ``qcschema_input`` file are: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +schema_name str N/A The QCSchema specification to which this model + conforms. Fixed as ``qcschema_input``. +schema_version float N/A The version number of ``schema_name`` to which + this model conforms, currently 2. +molecule dict N/A QCSchema Molecule instance. +driver str N/A The type of calculation being performed. One of + ``energy``, ``gradient``, ``hessian``, or + ``properties``. +model dict N/A The quantum chemistry model specification for a + given operation to compute against. +======================= ============ ============ ================================================== + +The optional fields and corresponding types for a `qcschema_input` file are: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +extras dict N/A Extra information associated with the input. +id str N/A An identifier for the input object. +keywords dict N/A QC program-specific keywords to be used for a + computation. See details below for IOData-specific + usages. +protocols dict N/A Protocols regarding the manipulation of the output + that results from this input. See Protocols + section below. +provenance dict or list N/A Information about the file was generated, + provided, and manipulated. See Provenance section + above for more information. +======================= ============ ============ ================================================== + +IOData currently supports the following keywords for ``qcschema_input`` files: + +======================= ============ ============ ================================================== +Keyword Type IOData attr. Description +======================= ============ ============ ================================================== +run_type str ``run_type`` The type of calculation that lead to the results + stored in IOData, which must be one of the + following: ``energy``, ``energy_force``, ``opt``, + ``scan``, ``freq`` or None. +======================= ============ ============ ================================================== + +.. _json_schema_model: + +Model Subschema +^^^^^^^^^^^^^^^ +The ``model`` dict contains the following fields: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +method str ``lot`` The level of theory used for the computation (e.g. + B3LYP, PBE, CCSD(T), etc.) +basis str or dict N/A The quantum chemistry basis set to evaluate (e.g. + 6-31G, cc-pVDZ, etc.) Can be 'none' for methods + without basis sets. Must be either a string + specifying the basis set name (the same as its + name in the Basis Set Exchange, when possible) or + a qcschema_basis instance. +======================= ============ ============ ================================================== + +.. _json_schema_protocols: + +Protocols Subschema +^^^^^^^^^^^^^^^^^^^ +The ``protocols`` dict contains the following fields: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +wavefunction str N/A Specification of the wavefunction properties to + keep from the resulting output. One of ``all``, + ``orbitals_and_eigenvalues``, ``return_results``, + or ``none``. +keep_stdout bool N/A An indicator to keep the output file from the + resulting output. +======================= ============ ============ ================================================== + +.. _json_schema_output: + +Output Schema +^^^^^^^^^^^^^ +The ``qcschema_output`` subschema describes all data necessary to generate and parse a QC program's +output file for a given molecule. + +The following is an example of a minimal ``qcschema_output`` file: + +.. code-block :: JSON + + { + "schema_name": "qcschema_output", + "schema_version": 2.0, + "molecule": { + "schema_name": "qcschema_molecule", + "schema_version": 2.0, + "symbols": ["Li", "Cl"], + "geometry": [0.000000, 0.000000, -1.631761, 0.000000, 0.000000, 0.287958], + "molecular_charge": 0.0, + "molecular_multiplicity": 1, + "provenance": { + "creator": "HORTON3", + "routine": "Manual validation" + } + }, + "driver": "energy", + "model": { + "method": "HF", + "basis": "STO-4G" + }, + "properties": {}, + "return_result": -464.626219879, + "success": true + } + +The required fields and corresponding types for a ``qcschema_output`` file are: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +schema_name str N/A The QCSchema specification to which this model + conforms. Fixed as ``qcschema_output``. +schema_version float N/A The version number of ``schema_name`` to which + this model conforms, currently 2. +molecule dict N/A QCSchema Molecule instance. +driver str N/A The type of calculation being performed. One of + ``energy``, ``gradient``, ``hessian``, or + ``properties``. +model dict N/A The quantum chemistry model specification for a + given operation to compute against. +properties dict N/A Named properties of quantum chemistry + computations. See Properties section below. +return_result varies N/A The result requested by the ``driver``. The type + depends on the ``driver``. +success bool N/A An indicator for the success of the QC program's + execution. +======================= ============ ============ ================================================== + +The optional fields and corresponding types for a ``qcschema_output`` file are: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +error dict N/A A complete description of an error-terminated + computation. See Error section below. +extras dict N/A Extra information associated with the input. Also + specified for ``qcschema_input``. +id str N/A An identifier for the input object. Also specified + for ``qcschema_input``. +keywords dict N/A QC program-specific keywords to be used for a + computation. See details below for IOData-specific + usages. Also specified for ``qcschema_input``. +protocols dict N/A Protocols regarding the manipulation of the output + that results from this input. See Protocols + section above. Also specified for + ``qcschema_input``. +provenance dict or list N/A Information about the file was generated, + provided, and manipulated. See Provenance section + above for more information. Also specified for + ``qcschema_input``. +stderr str N/A The standard error (stderr) of the associated + computation. +stdout str N/A The standard output (stdout) of the associated + computation. +wavefunction dict N/A The wavefunction properties of a QC computation. + All matrices appear in column-major order. See + Wavefunction section below. +======================= ============ ============ ================================================== + +.. _json_schema_properties: + +Properties Subschema +^^^^^^^^^^^^^^^^^^^^ +The ``properties`` dict contains named properties of quantum chemistry computations. Due to the +variability possible for the contents of an output file, IOData does not guess at which properties +are desired by the user, and stores all properties in the ``extra["output]["properties"]`` dict for +easy retrieval. The current QCSchema standard provides names for the following properties: + +======================================== =========================================================== +Field Description +======================================== =========================================================== +calcinfo_nbasis The number of basis functions for the computation. +calcinfo_nmo The number of molecular orbitals for the computation. +calcinfo_nalpha The number of alpha electrons in the computation. +calcinfo_nbeta The number of beta electrons in the computation. +calcinfo_natom The number of atoms in the computation. +nuclear_repulsion_energy The nuclear repulsion energy term. +return_energy The energy of the requested method, identical to + ``return_value`` for energy computations. +scf_one_electron_energy The one-electron (core Hamiltonian) energy contribution to + the total SCF energy. +scf_two_electron_energy The two-electron energy contribution to the total SCF + energy. +scf_vv10_energy The VV10 functional energy contribution to the total SCF + energy. +scf_xc_energy The functional (XC) energy contribution to the total SCF + energy. +scf_dispersion_correction_energy The dispersion correction appended to an underlying + functional when a DFT-D method is requested. +scf_dipole_moment The X, Y, and Z dipole components. +scf_total_energy The total electronic energy of the SCF stage of the + calculation. +scf_iterations The number of SCF iterations taken before convergence. +mp2_same_spin_correlation_energy The portion of MP2 doubles correlation energy from + same-spin (i.e. triplet) correlations. +mp2_opposite_spin_correlation_energy The portion of MP2 doubles correlation energy from + opposite-spin (i.e. singlet) correlations. +mp2_singles_energy The singles portion of the MP2 correlation energy. Zero + except in ROHF. +mp2_doubles_energy The doubles portion of the MP2 correlation energy including + same-spin and opposite-spin correlations. +mp2_total_correlation_energy The MP2 correlation energy. +mp2_correlation_energy The MP2 correlation energy. +mp2_total_energy The total MP2 energy (MP2 correlation energy + HF energy). +mp2_dipole_moment The MP2 X, Y, and Z dipole components. +ccsd_same_spin_correlation_energy The portion of CCSD doubles correlation energy from + same-spin (i.e. triplet) correlations. +ccsd_opposite_spin_correlation_energy The portion of CCSD doubles correlation energy from + opposite-spin (i.e. singlet) correlations +ccsd_singles_energy The singles portion of the CCSD correlation energy. Zero + except in ROHF. +ccsd_doubles_energy The doubles portion of the CCSD correlation energy + including same-spin and opposite-spin correlations. +ccsd_correlation_energy The CCSD correlation energy. +ccsd_total_energy The total CCSD energy (CCSD correlation energy + HF + energy). +ccsd_dipole_moment The CCSD X, Y, and Z dipole components. +ccsd_iterations The number of CCSD iterations taken before convergence. +ccsd_prt_pr_correlation_energy The CCSD(T) correlation energy. +ccsd_prt_pr_total_energy The total CCSD(T) energy (CCSD(T) correlation energy + HF + energy). +ccsd_prt_pr_dipole_moment The CCSD(T) X, Y, and Z dipole components. +ccsd_prt_pr_iterations The number of CCSD(T) iterations taken before convergence. +ccsdt_correlation_energy The CCSDT correlation energy. +ccsdt_total_energy The total CCSDT energy (CCSDT correlation energy + HF + energy). +ccsdt_dipole_moment The CCSDT X, Y, and Z dipole components. +ccsdt_iterations The number of CCSDT iterations taken before convergence. +ccsdtq_correlation_energy The CCSDTQ correlation energy. +ccsdtq_total_energy The total CCSDTQ energy (CCSDTQ correlation energy + HF + energy). +ccsdtq_dipole_moment The CCSDTQ X, Y, and Z dipole components. +ccsdtq_iterations The number of CCSDTQ iterations taken before convergence. +======================================== =========================================================== + +.. _json_schema_error: + +Error Subschema +^^^^^^^^^^^^^^^ +The ``error`` dict contains the following fields: + +======================= ============ ============ ================================================== +Field Type IOData attr. Description +======================= ============ ============ ================================================== +error_type str N/A The type of error raised during the computation. +error_message str N/A Additional information related to the error, such + as the backtrace. +extras dict N/A Additional data associated with the error. +======================= ============ ============ ================================================== + +.. _json_schema_wavefunction: + +Wavefunction subschema +^^^^^^^^^^^^^^^^^^^^^^ +The wavefunction subschema contains the wavefunction properties of a QC computation. All matrices +appear in column-major order. The current QCSchema standard provides names for the following +wavefunction properties: + +.. CCA_convention_source: +https://github.com/evaleev/libint/wiki/using-modern-CPlusPlus-API#solid-harmonic-gaussians-ordering-and-normalization + +======================================== =========================================================== +Field Description +======================================== =========================================================== +basis A ``qcschema_basis`` instance for the one-electron AO basis + set. AO basis functions are ordered according to the CCA + standard as implemented in + :ref:`libint `. +restricted An indicator for a restricted calculation (alpha == beta). + When true, all beta quantites are omitted, since quantity_b + == quantity_a +h_core_a Alpha-spin core (one-electron) Hamiltonian. +h_core_b Beta-spin core (one-electron) Hamiltonian. +h_effective_a Alpha-spin effective core (one-electron) Hamiltonian. +h_effective_b Beta-spin effective core (one-electron) Hamiltonian. +scf_orbitals_a Alpha-spin SCF orbitals. +scf_orbitals_b Beta-spin SCF orbitals. +scf_density_a Alpha-spin SCF density matrix. +scf_density_b Beta-spin SCF density matrix. +scf_fock_a Alpha-spin SCF Fock matrix. +scf_fock_b Beta-spin SCF Fock matrix. +scf_eigenvalues_a Alpha-spin SCF eigenvalues. +scf_eigenvalues_b Beta-spin SCF eigenvalues. +scf_occupations_a Alpha-spin SCF orbital occupations. +scf_occupations_b Beta-spin SCF orbital occupations. +orbitals_a Keyword for the primary return alpha-spin orbitals. +orbitals_b Keyword for the primary return beta-spin orbitals. +density_a Keyword for the primary return alpha-spin density. +density_b Keyword for the primary return beta-spin density. +fock_a Keyword for the primary return alpha-spin Fock matrix. +fock_b Keyword for the primary return beta-spin Fock matrix. +eigenvalues_a Keyword for the primary return alpha-spin eigenvalues. +eigenvalues_b Keyword for the primary return beta-spin eigenvalues. +occupations_a Keyword for the primary return alpha-spin orbital + occupations. +occupations_b Keyword for the primary return beta-spin orbital + occupations. +======================================== =========================================================== + """ From dc8ad23ade6fa87ffb1f64cac5df2f1a2b18c06e Mon Sep 17 00:00:00 2001 From: William Adams Date: Wed, 5 May 2021 16:55:29 -0400 Subject: [PATCH 042/144] Add internal links --- iodata/formats/json.py | 60 +++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index b67cf0a77..09ed649f3 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -21,9 +21,9 @@ QCSchema defines four different subschema: -- Molecule: specifying a molecular system -- Input: specifying QC program input for a specific Molecule -- Output: specifying QC program output for a specific Molecule +- :ref:`Molecule `: specifying a molecular system +- :ref:`Input `: specifying QC program input for a specific Molecule +- :ref:`Output `: specifying QC program output for a specific Molecule - Basis: specifying a basis set for a specific Molecule General Usage @@ -118,7 +118,8 @@ molecular_multiplicity int ``spinpol`` The total multiplicity of this molecule. Some writers assume a default of 1. provenance dict or list N/A Information about the file was generated, - provided, and manipulated. See Provenance section + provided, and manipulated. See + :ref:`Provenance section ` above for more details. ====================== ============ ============ ================================================= @@ -127,9 +128,9 @@ The optional fields and corresponding types for a ``qcschema_molecule`` file are: -======================= ============ ============== ================================================== +======================= ============ ============== ================================================ Field Type IOData attr. Description -======================= ============ ============== ================================================== +======================= ============ ============== ================================================ atom_labels list(N_at) N/A Additional per-atom labels. Typically used for model conversions, not user assignment. The indices of this array correspond to the @@ -170,8 +171,8 @@ (true) or a ghost/virtual atom (false). The indices of this array correspond to the ``symbols`` ordering. -mass_numbers list(N_at) ``atmasses`` An array of atomic mass numbers for each atom. The - indices of this array correspond to the +mass_numbers list(N_at) ``atmasses`` An array of atomic mass numbers for each atom. + The indices of this array correspond to the ``symbols`` ordering. masses list(N_at) ``atmasses`` An array of atomic masses [u] for each atom. Typically inferred from ``symbols``. The indices @@ -179,7 +180,7 @@ ordering. name str ``title`` An arbitrary, common, or human-readable name to assign to this molecule. -======================= ============ ============== ================================================== +======================= ============ ============== ================================================ Note: N_at corresponds to the number of atoms in the molecule, as defined by the length of ``symbols``; N_fr corresponds to the number of fragments in the molecule, as defined by the length @@ -246,12 +247,14 @@ conforms. Fixed as ``qcschema_input``. schema_version float N/A The version number of ``schema_name`` to which this model conforms, currently 2. -molecule dict N/A QCSchema Molecule instance. +molecule dict N/A :ref:`QCSchema Molecule ` + instance. driver str N/A The type of calculation being performed. One of ``energy``, ``gradient``, ``hessian``, or ``properties``. model dict N/A The quantum chemistry model specification for a - given operation to compute against. + given operation to compute against. See + :ref:`Model section ` below. ======================= ============ ============ ================================================== The optional fields and corresponding types for a `qcschema_input` file are: @@ -265,10 +268,12 @@ computation. See details below for IOData-specific usages. protocols dict N/A Protocols regarding the manipulation of the output - that results from this input. See Protocols - section below. + that results from this input. See + :ref:`Protocols section ` + below. provenance dict or list N/A Information about the file was generated, - provided, and manipulated. See Provenance section + provided, and manipulated. See + :ref:`Provenance section ` above for more information. ======================= ============ ============ ================================================== @@ -371,7 +376,9 @@ model dict N/A The quantum chemistry model specification for a given operation to compute against. properties dict N/A Named properties of quantum chemistry - computations. See Properties section below. + computations. See + :ref:`Properties section ` + below. return_result varies N/A The result requested by the ``driver``. The type depends on the ``driver``. success bool N/A An indicator for the success of the QC program's @@ -384,29 +391,34 @@ Field Type IOData attr. Description ======================= ============ ============ ================================================== error dict N/A A complete description of an error-terminated - computation. See Error section below. + computation. See + :ref:`Error section ` below. extras dict N/A Extra information associated with the input. Also - specified for ``qcschema_input``. + specified for + :ref:`qcschema_input `. id str N/A An identifier for the input object. Also specified - for ``qcschema_input``. + for :ref:`qcschema_input `. keywords dict N/A QC program-specific keywords to be used for a computation. See details below for IOData-specific - usages. Also specified for ``qcschema_input``. + usages. Also specified for + :ref:`qcschema_input `. protocols dict N/A Protocols regarding the manipulation of the output - that results from this input. See Protocols - section above. Also specified for - ``qcschema_input``. + that results from this input. See + :ref:`Protocols section ` + above. Also specified for + :ref:`qcschema_input `. provenance dict or list N/A Information about the file was generated, provided, and manipulated. See Provenance section above for more information. Also specified for - ``qcschema_input``. + :ref:`qcschema_input `. stderr str N/A The standard error (stderr) of the associated computation. stdout str N/A The standard output (stdout) of the associated computation. wavefunction dict N/A The wavefunction properties of a QC computation. All matrices appear in column-major order. See - Wavefunction section below. + :ref:`Wavefunction ` + section below. ======================= ============ ============ ================================================== .. _json_schema_properties: From 10a9aad6cdc86993c1823db77ff431bd51899ba2 Mon Sep 17 00:00:00 2001 From: William Adams Date: Thu, 6 May 2021 12:15:36 -0400 Subject: [PATCH 043/144] Add links to function docstrings --- iodata/formats/json.py | 108 ++++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 09ed649f3..f4332031b 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -587,7 +587,7 @@ @document_load_one( "QCSchema", ["atnums", "atcorenums", "atcoords", "charge", "nelec", "spinpol"], - ["atmasses", "bonds", "g_rot", "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 @@ -599,11 +599,11 @@ def load_one(lit: LineIterator) -> dict: def _parse_json(json_in: dict, lit: LineIterator) -> dict: """Parse data from QCSchema JSON input file. - QCSchema supports four different schema types: `qcschema_molecule`, specifying one or more - molecules in a single system; `qcschema_basis`, specifying a basis set for a molecular system, - `qcschema_input`, specifying input to a QC program for a specific system; and `qcschema_output`, - specifying results of a QC program calculation for a specific system along with the input - information. + QCSchema supports four different schema types: :ref:`qcschema_molecule `, + specifying one or more molecules in a single system; `qcschema_basis`, specifying a basis set + for a molecular system, :ref:`qcschema_input `, specifying input to a QC + program for a specific system; and :ref:`qcschema_output `, specifying + results of a QC program calculation for a specific system along with the input information. Parameters ---------- @@ -692,7 +692,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: def _load_qcschema_molecule(result: dict, lit: LineIterator) -> dict: - """Load qcschema_molecule properties. + """Load :ref:`qcschema_molecule ` properties. Parameters ---------- @@ -704,10 +704,10 @@ def _load_qcschema_molecule(result: dict, lit: LineIterator) -> dict: Returns ------- molecule_dict - Output dictionary containing ``atcoords``, ``atnums``, ``charge``, + Output dictionary containing ``atcoords``, ``atnums``, ``charge``, ``extra``, ``nelec`` & ``spinpol`` keys and corresponding values. - It may contain ``atmasses``, ``bonds``, ``g_rot``, ``title`` & ``extra`` - keys and corresponding values as well. + It may contain ``atmasses``, ``bonds``, ``g_rot`` & ``title`` keys and corresponding values + as well. """ # All Topology properties are found in the "molecule" key @@ -725,9 +725,9 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: """Load topology properties from old QCSchema Molecule specifications. The qcschema_molecule v2 specification requires a topology for every file, specified in the - `molecule` key, containing at least the keys `schema_name`, `schema_version`, `symbols`, - `geometry`, `molecular_charge`, `molecular_multiplicity`, and `provenance`. This schema is - currently used in QCElemental (and thus the QCArchive ecosystem). + ``molecule`` key, containing at least the keys ``schema_name``, ``schema_version``, ``symbols``, + ``geometry``, ``molecular_charge``, ``molecular_multiplicity``, and ``provenance``. This schema + is currently used in QCElemental (and thus the QCArchive ecosystem). qcschema_molecule v1 only exists as the specification on the QCSchema website, and seems never to have been implemented in QCArchive. It is possible to accept v1 input, since all required @@ -744,10 +744,10 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: Returns ------- topology_dict - Output dictionary containing ``atcoords``, ``atnums``, ``charge``, + Output dictionary containing ``atcoords``, ``atnums``, ``charge``, ``extra``, ``nelec`` & ``spinpol`` keys and corresponding values. - It may contain ``atmasses``, ``bonds``, ``g_rot``, ``title`` & ``extra`` - keys and corresponding values as well. + It may contain ``atmasses``, ``bonds``, ``g_rot`` & ``title`` keys and corresponding values + as well. """ # Make sure required topology properties are present @@ -925,7 +925,7 @@ def _version_check(result: dict, max_version: float, schema_name: str, lit: Line max_version The highest (most recent) known version for the QCSchema type. schema_name - The `schema_name` key of a QCSchema file. + The ``schema_name`` key of a QCSchema file. lit The line iterator holding the file data. @@ -990,6 +990,13 @@ def _load_qcschema_basis(_result: dict, _lit: LineIterator) -> dict: ------- basis_dict ... + + + Raises + ------ + NotImplementedError + QCSchema Basis schema is not yet implemented in IOData. + """ # basis_dict = dict() # return basis_dict @@ -1010,6 +1017,12 @@ def _parse_basis_keys(_basis: dict, _lit: LineIterator) -> dict: ------- basis_dict Dictionary containing ... + + Raises + ------ + NotImplementedError + QCSchema Basis schema is not yet implemented in IOData. + """ raise NotImplementedError("qcschema_basis is not yet implemented in IOData.") @@ -1027,7 +1040,10 @@ def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: Returns ------- input_dict - ... + Output dictionary containing ``atcoords``, ``atnums``, ``charge``, ``extra``, ``lot``, + ``nelec``, ``obasis_name`` & ``spinpol`` keys and corresponding values. + It may contain ``atmasses``, ``bonds``, ``g_rot``, ``obasis``, ``run_type`` & ``title`` + keys and corresponding values as well. """ extra_dict = dict() input_dict = _parse_input_keys(result, lit) @@ -1057,8 +1073,9 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: Returns ------- input_dict - Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. - It may contain ``obasis`` and ``extra`` keys and corresponding values as well. + Output dictionary containing ``extra``, ``lot`` and ``obasis_name`` keys and corresponding + values. + It may contain ``obasis`` & ``run_type`` keys and corresponding values as well. """ # QCEngineRecords input files don't actually specify a name or version @@ -1144,7 +1161,7 @@ def _parse_driver(driver: str, lit: LineIterator) -> str: Parameters ---------- driver - The `driver` key from the QCSchema input. + The ``driver`` key from the QCSchema input. lit The line iterator holding the file data. @@ -1160,8 +1177,8 @@ def _parse_driver(driver: str, lit: LineIterator) -> str: Notes ----- - This keyword is similar to, but not really interchangeable with, the `run_type` IOData - attribute. In order to specify the `run_type`, add it to the `keywords` dictionary. + This keyword is similar to, but not really interchangeable with, the ``run_type`` IOData + attribute. In order to specify the ``run_type``, add it to the ``keywords`` dictionary. """ if driver not in ["energy", "gradient", "hessian", "properties"]: @@ -1175,7 +1192,7 @@ def _parse_driver(driver: str, lit: LineIterator) -> str: def _parse_model(model: dict, lit: LineIterator) -> dict: - """Load model properties from QCSchema. + """Load :ref:`model ` properties from QCSchema. Parameters ---------- @@ -1188,7 +1205,7 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: ------- model_dict Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. - It may contain ``obasis`` key and corresponding values as well. + It may contain ``obasis`` and ``extra`` keys and corresponding values as well. """ model_dict = dict() @@ -1224,7 +1241,7 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: - """Load protocols properties from QCSchema. + """Load :ref:`protocols ` properties from QCSchema. Parameters ---------- @@ -1271,7 +1288,7 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: - """Load qcschema_output properties. + """Load :ref:`qcschema_output ` properties. Parameters ---------- @@ -1283,8 +1300,10 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: Returns ------- output_dict - Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. - It may contain ``energy``, ``obasis`` and ``extra`` keys and corresponding values as well. + Output dictionary containing ``atcoords``, ``atnums``, ``charge``, ``extra``, ``lot``, + ``nelec``, ``obasis_name`` & ``spinpol`` keys and corresponding values. + It may contain ``atmasses``, ``bonds``, ``energy``, ``g_rot``, ``obasis``, ``run_type`` & + ``title`` keys and corresponding values as well. """ extra_dict = dict() @@ -1307,7 +1326,7 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: def _parse_output_keys(result: dict, lit: LineIterator) -> dict: - """Parse output keys for QCSchema input or output files. + """Parse output keys for QCSchema output files. Parameters ---------- @@ -1318,9 +1337,9 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: Returns ------- - input_dict - Output dictionary containing ``lot`` and ``obasis_name`` keys and corresponding values. - It may contain ``energy`` keys and corresponding values as well. + output_dict + Output dictionary containing ``extra`` key and corresponding values. + It may contain ``energy`` key and corresponding values as well. """ should_be_required_keys = {"schema_name", "schema_version"} @@ -1390,7 +1409,7 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: def _parse_provenance( provenance: Union[List[dict], dict], lit: LineIterator, source: str, append=True ) -> Union[List[dict], dict]: - """Load provenance properties from QCSchema. + """Load :ref:`provenance ` properties from QCSchema. Parameters ---------- @@ -1399,7 +1418,7 @@ def _parse_provenance( lit The line iterator holding the file data. source - The schema type {`qcschema_molecule`, `qcschema_input`, `qcschema_output`} associated + The schema type {``qcschema_molecule``, ``qcschema_input``, ``qcschema_output``} associated with this provenance data. append Append IOData provenance entry to provenance list? @@ -1460,7 +1479,7 @@ def dump_one(f: TextIO, data: IOData): def _dump_qcschema_molecule(data: IOData) -> dict: - """Dump relevant attributes from IOData to qcschema_molecule. + """Dump relevant attributes from IOData to :ref:`qcschema_molecule `. Parameters ---------- @@ -1549,7 +1568,8 @@ def _dump_qcschema_molecule(data: IOData) -> dict: def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: - """Generate the provenance information for dumping an IOData instance to QCSchema. + """Generate the :ref:`provenance ` information for dumping an IOData + instance to QCSchema. Parameters ---------- @@ -1580,10 +1600,10 @@ def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: def _dump_qcschema_input(data: IOData) -> dict: - """Dump relevant attributes from IOData to qcschema_input. + """Dump relevant attributes from IOData to :ref:`qcschema_input `. - Using this function requires keywords to be stored in two locations in the `extra` dict: - a `molecule` dict for the QCSchema Molecule extra keys, and an `input` dict for the QCSchema + Using this function requires keywords to be stored in two locations in the ``extra`` dict: + a ``molecule`` dict for the QCSchema Molecule extra keys, and an ``input`` dict for the QCSchema Input extra keys. Parameters @@ -1639,11 +1659,11 @@ def _dump_qcschema_input(data: IOData) -> dict: def _dump_qcschema_output(data: IOData) -> dict: - """Dump relevant attributes from IOData to qcschema_output. + """Dump relevant attributes from IOData to :ref:`qcschema_output `. - Using this function requires keywords to be stored in three locations in the `extra` dict: - a `molecule` dict for the QCSchema Molecule extra keys, an `input` dict for the QCSchema - Input extra keys, and an `output` dict for the QCSchema Output extra keys. + Using this function requires keywords to be stored in three locations in the ``extra`` dict: + a ``molecule`` dict for the QCSchema Molecule extra keys, an ``input`` dict for the QCSchema + Input extra keys, and an ``output`` dict for the QCSchema Output extra keys. Parameters ---------- From 8697647589ec78e35d27f322957a6a56d0fa3282 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 17 May 2021 16:17:03 +0200 Subject: [PATCH 044/144] Make doc style linter happy --- iodata/formats/json.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index f4332031b..691324f3e 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -1568,8 +1568,9 @@ def _dump_qcschema_molecule(data: IOData) -> dict: def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: - """Generate the :ref:`provenance ` information for dumping an IOData - instance to QCSchema. + """Generate the :ref:`provenance ` information. + + This is used when dumping an IOData instance to QCSchema. Parameters ---------- @@ -1582,6 +1583,7 @@ def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: ------- provenance The provenance information for the IOData instance. + """ new_provenance = { "creator": "IOData", From bd3478ebead888dfe82814ba734dfd61bb095b4f Mon Sep 17 00:00:00 2001 From: Fanwang Meng Date: Fri, 21 May 2021 23:07:40 -0400 Subject: [PATCH 045/144] Fix missing `M END` tag for SDF format --- iodata/formats/sdf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index 6243bcb27..8e4d3b73f 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -120,6 +120,7 @@ def dump_one(f: TextIO, data: IOData): print('{:3d}{:3d}{:3d} 0 0 0 0'.format( iatom + 1, jatom + 1, bondtype ), file=f) + print('M END', file=f) print('$$$$', file=f) From 82bc4bd7fdec6cda81ca3624c961b80d4470689c Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 25 Jun 2021 10:25:50 +0200 Subject: [PATCH 046/144] Drop cached conda install --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 80d6d4dd6..d63316c72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,7 @@ jobs: !~/miniconda3/envs/*/locks !~/miniconda3/envs/*/pkgs !~/miniconda3/envs/*/var - key: ${{ runner.os }}-conda-3 + key: ${{ runner.os }}-conda-4 - name: Install Roberto run: | python -m pip install roberto>=2.0.0 From e80e463bba8c0257f3dcd48248a048545acabde2 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 5 Jul 2021 09:56:18 +0200 Subject: [PATCH 047/144] Fix new pylint issues --- iodata/test/test_cube.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 4e1a605ff..f8cd5f787 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -104,7 +104,8 @@ def test_load_dump_h2o_5points(tmpdir): fn_cube2 = tmpdir.join('iodata_h2o_5points.cube') dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare - content1 = open(fn_cube1, "r").read().split("\n", 2)[-1] + with open(fn_cube1, "r") as f: + content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 @@ -117,7 +118,8 @@ def test_load_dump_ch4_6points(tmpdir): fn_cube2 = tmpdir.join('iodata_ch4_6points.cube') dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare - content1 = open(fn_cube1, "r").read().split("\n", 2)[-1] + with open(fn_cube1, "r") as f: + content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 @@ -130,6 +132,7 @@ def test_load_dump_nh3_7points(tmpdir): fn_cube2 = tmpdir.join('iodata_nh3_7points.cube') dump_one(mol1, fn_cube2) # read the contents as string (skip the first 2 lines) & compare - content1 = open(fn_cube1, "r").read().split("\n", 2)[-1] + with open(fn_cube1, "r") as f: + content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 From 900131df7a2a0f9c26253a9c2a537271dba19d16 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 6 Jul 2021 13:30:47 +0200 Subject: [PATCH 048/144] Use same package name qc-iodata on conda and PyPI. --- doc/install.rst | 6 +++--- tools/conda.recipe/meta.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/install.rst b/doc/install.rst index 4ae24cece..79fbbdd54 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -63,14 +63,14 @@ To install IOData using the conda package management system, install source activate horton3 # Install the stable release. - conda install -c theochem iodata + conda install -c theochem qc-iodata # Unstable releases # (Only do this if you understand the implications.) # Install the testing release. (beta) - conda install -c theochem/label/test iodata + conda install -c theochem/label/test qc-iodata # Install the development release. (alpha) - conda install -c theochem/label/dev iodata + conda install -c theochem/label/dev qc-iodata Installation with Pip diff --git a/tools/conda.recipe/meta.yaml b/tools/conda.recipe/meta.yaml index 21c7eb629..ec4db6ea3 100644 --- a/tools/conda.recipe/meta.yaml +++ b/tools/conda.recipe/meta.yaml @@ -1,6 +1,6 @@ package: version: "{{ PROJECT_VERSION }}" - name: 'iodata' + name: 'qc-iodata' source: path: ../../ From dc77ccba38a101ddcd2898e0b96b880f09714ded Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 5 Jul 2021 14:31:15 -0400 Subject: [PATCH 049/144] adding cfour framwork --- iodata/formats/molden.py | 15 ++++ iodata/test/data/h_donly_sph.molden | 111 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 iodata/test/data/h_donly_sph.molden diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 889edf361..47eb93ce9 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -548,6 +548,18 @@ def _fix_mo_coeffs_psi4(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return np.concatenate(correction) return None +def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: + """Return correction values for the MO coefficients for CFOUR. + + CFOUR 2.1 uses a different normalizationion conventions for Cartesian + AO basis functions. The coefficients need to be divided by the returned + correction factor. + """ + correction = [] + corrected = False + + for shell in obasis.shells: + def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): """Detect errors in the data loaded from a molden or mkl file and correct. @@ -626,6 +638,9 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): result['mo'].coeffsa[:] = coeffsa_psi4 result['mo'].coeffsb[:] = coeffsb_psi4 return + # --- CFOUR 2.1 + cour_coeff_correction = _fix_mo_coeffs_cfour(obasis) + 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 ' diff --git a/iodata/test/data/h_donly_sph.molden b/iodata/test/data/h_donly_sph.molden new file mode 100644 index 000000000..f587f7fde --- /dev/null +++ b/iodata/test/data/h_donly_sph.molden @@ -0,0 +1,111 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +d 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 -0.2886751346 + 2 -0.2886751346 + 3 0.5773502692 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.5000000000 + 2 -0.5000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 -0.2886751346 + 2 -0.2886751346 + 3 0.5773502692 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.5000000000 + 2 -0.5000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 From 3e59b2e1ec96337217aa8fd066a3dda123642a4c Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Mon, 5 Jul 2021 14:58:17 -0400 Subject: [PATCH 050/144] Add test --- iodata/formats/molden.py | 2 + iodata/test/data/h_donly_sph.molden | 1 - iodata/test/test_molden.py | 8 ++++ iodata/test/test_molden_cfour_TOMERGE.py | 50 ++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 iodata/test/test_molden_cfour_TOMERGE.py diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 47eb93ce9..ba20b9c44 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -559,6 +559,8 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: corrected = False for shell in obasis.shells: + pass + return obasis def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): diff --git a/iodata/test/data/h_donly_sph.molden b/iodata/test/data/h_donly_sph.molden index f587f7fde..ab982c6c1 100644 --- a/iodata/test/data/h_donly_sph.molden +++ b/iodata/test/data/h_donly_sph.molden @@ -1,7 +1,6 @@ [Molden Format] [ATOMS] AU H 1 1 0.0000000000 0.0000000000 0.0000000000 -[Molden Format] [GTO] 1 0 d 1 1.00 diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index cce5955e0..f1df12668 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -39,6 +39,14 @@ except ImportError: from importlib.resources import path +def test_load_molden_h_cfour(): + # The file tested here is created with CFOUR 2.1. + with path('iodata.test.data', 'h_donly_sph.molden') as fn_molden: + mol = load_one(str(fn_molden)) + + # Check normalization + olp = compute_overlap(mol.obasis, mol.atcoords) + check_orthonormal(mol.mo.coeffs, olp) def test_load_molden_li2_orca(): with path('iodata.test.data', 'li2.molden.input') as fn_molden: diff --git a/iodata/test/test_molden_cfour_TOMERGE.py b/iodata/test/test_molden_cfour_TOMERGE.py new file mode 100644 index 000000000..fa9c6baf0 --- /dev/null +++ b/iodata/test/test_molden_cfour_TOMERGE.py @@ -0,0 +1,50 @@ +# IODATA is an input and output module for quantum chemistry. +# Copyright (C) 2011-2019 The IODATA Development Team +# +# This file is part of IODATA. +# +# IODATA is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# IODATA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see +# -- +# pylint: disable=unsubscriptable-object +"""Test iodata.formats.molden module.""" + +import os + +import attr +import numpy as np +from numpy.testing import assert_allclose, assert_equal +import pytest + +from .common import compute_mulliken_charges, compare_mols, check_orthonormal +from ..api import load_one, dump_one +from ..basis import convert_conventions +from ..formats.molden import _load_low +from ..overlap import compute_overlap, OVERLAP_CONVENTIONS +from ..utils import LineIterator, angstrom, FileFormatWarning + + +try: + from importlib_resources import path +except ImportError: + from importlib.resources import path + +def test_load_molden_h_cfour(): + # The file tested here is created with CFOUR 2.1. + with path('iodata.test.data', 'h_donly_sph.molden') as fn_molden: + mol = load_one(str(fn_molden)) + + # Check normalization + olp = compute_overlap(mol.obasis, mol.atcoords) + check_orthonormal(mol.mo.coeffs, olp) + From 1291e86ea0498e25a90e97414d2ea5761169b05d Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 5 Jul 2021 15:25:38 -0400 Subject: [PATCH 051/144] adding cart molden, reorganize --- iodata/test/data/cfour/h_donly_cart.molden | 131 +++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 iodata/test/data/cfour/h_donly_cart.molden diff --git a/iodata/test/data/cfour/h_donly_cart.molden b/iodata/test/data/cfour/h_donly_cart.molden new file mode 100644 index 000000000..3fb15c265 --- /dev/null +++ b/iodata/test/data/cfour/h_donly_cart.molden @@ -0,0 +1,131 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +d 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= -5.180222245094024E-002 + Spin= Alpha + Occup= 0.0 + 1 0.2581988897 + 2 0.2581988897 + 3 0.2581988897 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.5483667316 + 2 -0.4306136881 + 3 -0.1177530435 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.1806301774 + 2 0.3845844315 + 3 -0.5652146089 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= -5.180222245094024E-002 + Spin= Beta + Occup= 0.0 + 1 0.2581988897 + 2 0.2581988897 + 3 0.2581988897 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.5483667316 + 2 -0.4306136881 + 3 -0.1177530435 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.1806301774 + 2 0.3845844315 + 3 -0.5652146089 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 From 724f3c2debf3f45a87924470b2c78cb866e1f284 Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 5 Jul 2021 15:26:33 -0400 Subject: [PATCH 052/144] reorganize --- iodata/test/data/cfour/h_donly_sph.molden | 110 ++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 iodata/test/data/cfour/h_donly_sph.molden diff --git a/iodata/test/data/cfour/h_donly_sph.molden b/iodata/test/data/cfour/h_donly_sph.molden new file mode 100644 index 000000000..ab982c6c1 --- /dev/null +++ b/iodata/test/data/cfour/h_donly_sph.molden @@ -0,0 +1,110 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[GTO] + 1 0 +d 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 -0.2886751346 + 2 -0.2886751346 + 3 0.5773502692 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.5000000000 + 2 -0.5000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 -0.2886751346 + 2 -0.2886751346 + 3 0.5773502692 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.5000000000 + 2 -0.5000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.0000000000 + 5 0.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.0000000000 + 6 0.0000000000 + Sym= A + Ene= 1.14819777754906 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.0000000000 From e65b4c982c4e1a0be4b1fb07d4821e686165c50d Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 5 Jul 2021 16:51:37 -0400 Subject: [PATCH 053/144] progress --- iodata/formats/molden.py | 73 +++++++++++++++++----- iodata/test/data/cfour/__init__.py | 18 ++++++ iodata/test/data/cfour/h_donly_cart.molden | 38 +++++------ iodata/test/test_molden_cfour_TOMERGE.py | 9 ++- 4 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 iodata/test/data/cfour/__init__.py diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index ba20b9c44..d8798a2a5 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -348,7 +348,8 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, # every attempt because also the primitive normalization may differ in # different cases. olp = compute_overlap(obasis, atcoords) - + print('checking normalization') + print(olp) # Convenient code for debugging files coming from crappy QC codes. # np.set_printoptions(linewidth=5000, precision=2, suppress=True, threshold=100000) # coeffs = orb_alpha._coeffs @@ -377,6 +378,52 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, # final judgement return error_max <= threshold +def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: + """Return correction values for the MO coefficients for CFOUR. + + CFOUR 2.1 uses a different normalizationion conventions for Spherical + AO basis functions. The coefficients need to be divided by the returned + correction factor. + """ + cfour_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'], + # # 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'], + } + fixed_shells = [] + corrected = False + print('cfour parsing attempt') + # TODO: check whether sph or cartesian functions are used + # this can be checked by whether the coefficient matrix is square or + # rectangular. + for shell in obasis.shells: + fixed_shell = copy.deepcopy(shell) + fixed_shells.append(fixed_shell) + angmom = shell.angmoms[0] + kind = shell.kinds[0] + if kind == "c": + for iprim, exponent in enumerate(shell.exponents): + if angmom == 2: + correction = gob_cart_normalization(exponent, np.array([2, 0, 0])) + elif angmom == 3: + correction = gob_cart_normalization(exponent, np.array([1, 1, 1])) + elif angmom == 4: + correction = gob_cart_normalization(exponent, np.array([2, 1, 1])) + print(correction) + if correction != 1.0: + fixed_shell.coeffs[iprim, 0] /= correction + if kind == 'p': + print('Correcting spherical basis functions from cfour not yet supported') + return MolecularBasis(fixed_shells, cfour_conventions, obasis.primitive_normalization) def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: """Return a new MolecularBasis correcting for errors from ORCA. @@ -384,6 +431,7 @@ def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: Orca has different normalization conventions for the primitives and also different sign conventions for some of the pure functions. """ + print('orca parsing attempt') orca_conventions = { (0, 'c'): ['1'], (1, 'c'): ['x', 'y', 'z'], @@ -548,19 +596,6 @@ def _fix_mo_coeffs_psi4(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return np.concatenate(correction) return None -def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: - """Return correction values for the MO coefficients for CFOUR. - - CFOUR 2.1 uses a different normalizationion conventions for Cartesian - AO basis functions. The coefficients need to be divided by the returned - correction factor. - """ - correction = [] - corrected = False - - for shell in obasis.shells: - pass - return obasis def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): @@ -616,6 +651,14 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): result['obasis'] = turbom_obasis return + # --- CFOUR 2.1 + cfour_obasis = _fix_obasis_cfour(obasis) + if cfour_obasis is not None and \ + _is_normalized_properly(cfour_obasis, atcoords, coeffsa, coeffsb): + lit.warn('Corrected for CFOUR errors in Molden/MKL file.') + result['obasis'] = cfour_obasis + return + # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb): @@ -640,8 +683,6 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): result['mo'].coeffsa[:] = coeffsa_psi4 result['mo'].coeffsb[:] = coeffsb_psi4 return - # --- CFOUR 2.1 - cour_coeff_correction = _fix_mo_coeffs_cfour(obasis) lit.error('Could not correct the data read from {}. The molden or mkl file ' diff --git a/iodata/test/data/cfour/__init__.py b/iodata/test/data/cfour/__init__.py new file mode 100644 index 000000000..15ca168f5 --- /dev/null +++ b/iodata/test/data/cfour/__init__.py @@ -0,0 +1,18 @@ +# IODATA is an input and output module for quantum chemistry. +# Copyright (C) 2011-2019 The IODATA Development Team +# +# This file is part of IODATA. +# +# IODATA is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 3 +# of the License, or (at your option) any later version. +# +# IODATA is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see +# -- diff --git a/iodata/test/data/cfour/h_donly_cart.molden b/iodata/test/data/cfour/h_donly_cart.molden index 3fb15c265..75f315ee3 100644 --- a/iodata/test/data/cfour/h_donly_cart.molden +++ b/iodata/test/data/cfour/h_donly_cart.molden @@ -1,13 +1,13 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[Molden Format] -[GTO] +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] 1 0 -d 1 1.00 - 0.500000000000000 1.00000000000000 - - +d 1 1.00sd + 0.500000000000000 1.00000000000000 + + [MO] Sym= A Ene= -5.180222245094024E-002 @@ -20,7 +20,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -30,7 +30,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -40,7 +40,7 @@ d 1 1.00 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -50,7 +50,7 @@ d 1 1.00 5 0.0000000000 6 1.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.5483667316 @@ -60,7 +60,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.1806301774 @@ -80,7 +80,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -90,7 +90,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -100,7 +100,7 @@ d 1 1.00 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -110,7 +110,7 @@ d 1 1.00 5 0.0000000000 6 1.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.5483667316 @@ -120,7 +120,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.1806301774 diff --git a/iodata/test/test_molden_cfour_TOMERGE.py b/iodata/test/test_molden_cfour_TOMERGE.py index fa9c6baf0..e637a30a2 100644 --- a/iodata/test/test_molden_cfour_TOMERGE.py +++ b/iodata/test/test_molden_cfour_TOMERGE.py @@ -41,10 +41,17 @@ def test_load_molden_h_cfour(): # The file tested here is created with CFOUR 2.1. - with path('iodata.test.data', 'h_donly_sph.molden') as fn_molden: + with path('iodata.test.data.cfour', 'h_donly_cart.molden') as fn_molden: mol = load_one(str(fn_molden)) # Check normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) + # # The file tested here is created with CFOUR 2.1. + # with path('iodata.test.data.cfour', 'h_donly_sph.molden') as fn_molden: + # mol = load_one(str(fn_molden)) + # + # # Check normalization + # olp = compute_overlap(mol.obasis, mol.atcoords) + # check_orthonormal(mol.mo.coeffs, olp) From 423c5fa4591ecd064e86adc8f6a453ab8a0b1542 Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:41:02 -0400 Subject: [PATCH 054/144] Add all test data --- iodata/test/data/cfour/h_donly_cart.molden | 38 +- iodata/test/data/cfour/h_donly_sph.molden | 35 +- iodata/test/data/cfour/h_fonly_cart.molden | 291 ++++++ iodata/test/data/cfour/h_fonly_sph.molden | 207 ++++ iodata/test/data/cfour/h_gonly_cart.molden | 581 +++++++++++ iodata/test/data/cfour/h_gonly_sph.molden | 353 +++++++ iodata/test/data/cfour/h_honly_cart.molden | 1061 ++++++++++++++++++++ iodata/test/data/cfour/h_honly_sph.molden | 561 +++++++++++ iodata/test/data/cfour/h_ponly_cart.molden | 53 + iodata/test/data/cfour/h_ponly_sph.molden | 53 + iodata/test/data/cfour/h_sonly_cart.molden | 21 + iodata/test/data/cfour/h_sonly_sph.molden | 21 + 12 files changed, 3239 insertions(+), 36 deletions(-) create mode 100644 iodata/test/data/cfour/h_fonly_cart.molden create mode 100644 iodata/test/data/cfour/h_fonly_sph.molden create mode 100644 iodata/test/data/cfour/h_gonly_cart.molden create mode 100644 iodata/test/data/cfour/h_gonly_sph.molden create mode 100644 iodata/test/data/cfour/h_honly_cart.molden create mode 100644 iodata/test/data/cfour/h_honly_sph.molden create mode 100644 iodata/test/data/cfour/h_ponly_cart.molden create mode 100644 iodata/test/data/cfour/h_ponly_sph.molden create mode 100644 iodata/test/data/cfour/h_sonly_cart.molden create mode 100644 iodata/test/data/cfour/h_sonly_sph.molden diff --git a/iodata/test/data/cfour/h_donly_cart.molden b/iodata/test/data/cfour/h_donly_cart.molden index 75f315ee3..3fb15c265 100644 --- a/iodata/test/data/cfour/h_donly_cart.molden +++ b/iodata/test/data/cfour/h_donly_cart.molden @@ -1,13 +1,13 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[Molden Format] -[GTO] +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] 1 0 -d 1 1.00sd - 0.500000000000000 1.00000000000000 - - +d 1 1.00 + 0.500000000000000 1.00000000000000 + + [MO] Sym= A Ene= -5.180222245094024E-002 @@ -20,7 +20,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -30,7 +30,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -40,7 +40,7 @@ d 1 1.00sd 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -50,7 +50,7 @@ d 1 1.00sd 5 0.0000000000 6 1.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.5483667316 @@ -60,7 +60,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.1806301774 @@ -80,7 +80,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -90,7 +90,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -100,7 +100,7 @@ d 1 1.00sd 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -110,7 +110,7 @@ d 1 1.00sd 5 0.0000000000 6 1.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.5483667316 @@ -120,7 +120,7 @@ d 1 1.00sd 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.1806301774 diff --git a/iodata/test/data/cfour/h_donly_sph.molden b/iodata/test/data/cfour/h_donly_sph.molden index ab982c6c1..ed162f897 100644 --- a/iodata/test/data/cfour/h_donly_sph.molden +++ b/iodata/test/data/cfour/h_donly_sph.molden @@ -1,15 +1,16 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[GTO] +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] 1 0 d 1 1.00 - 0.500000000000000 1.00000000000000 - - + 0.500000000000000 1.00000000000000 + + [MO] Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 -0.2886751346 @@ -19,7 +20,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.5000000000 @@ -29,7 +30,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -39,7 +40,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -49,7 +50,7 @@ d 1 1.00 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Alpha Occup= 0.0 1 0.0000000000 @@ -59,7 +60,7 @@ d 1 1.00 5 0.0000000000 6 1.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 -0.2886751346 @@ -69,7 +70,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.5000000000 @@ -79,7 +80,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -89,7 +90,7 @@ d 1 1.00 5 0.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 @@ -99,7 +100,7 @@ d 1 1.00 5 1.0000000000 6 0.0000000000 Sym= A - Ene= 1.14819777754906 + Ene= 1.14819777754906 Spin= Beta Occup= 0.0 1 0.0000000000 diff --git a/iodata/test/data/cfour/h_fonly_cart.molden b/iodata/test/data/cfour/h_fonly_cart.molden new file mode 100644 index 000000000..5da3f1eda --- /dev/null +++ b/iodata/test/data/cfour/h_fonly_cart.molden @@ -0,0 +1,291 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +f 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 0.305598095042051 + Spin= Alpha + Occup= 0.0 + 1 0.1690308509 + 2 0.0000000000 + 3 0.0000000000 + 4 0.1690308509 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1690308509 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 0.305598095042051 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.1690308509 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1690308509 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1690308509 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 0.305598095042052 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.1690308509 + 4 0.0000000000 + 5 0.0000000000 + 6 0.1690308509 + 7 0.0000000000 + 8 0.0000000000 + 9 0.1690308509 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361347 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 -0.1127525231 + 3 0.0000000000 + 4 0.0000000000 + 5 0.6189351397 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.2806775704 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 -0.2322790029 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1300741823 + 6 0.0000000000 + 7 0.0000000000 + 8 0.5667628265 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.2352502570 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.5589456093 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1468051619 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.1064142059 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2959388549 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.6151814728 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0365148372 + 4 0.0000000000 + 5 0.0000000000 + 6 0.4402024911 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.5497470026 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 1.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.2556038602 + 4 0.0000000000 + 5 0.0000000000 + 6 0.4541164684 + 7 0.0000000000 + 8 0.0000000000 + 9 0.3126951121 + 10 0.0000000000 + Sym= A + Ene= 0.305598095042051 + Spin= Beta + Occup= 0.0 + 1 0.1690308509 + 2 0.0000000000 + 3 0.0000000000 + 4 0.1690308509 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1690308509 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 0.305598095042051 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.1690308509 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1690308509 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1690308509 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 0.305598095042052 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.1690308509 + 4 0.0000000000 + 5 0.0000000000 + 6 0.1690308509 + 7 0.0000000000 + 8 0.0000000000 + 9 0.1690308509 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361347 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 -0.1127525231 + 3 0.0000000000 + 4 0.0000000000 + 5 0.6189351397 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.2806775704 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 -0.2322790029 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1300741823 + 6 0.0000000000 + 7 0.0000000000 + 8 0.5667628265 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.2352502570 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.5589456093 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1468051619 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.1064142059 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2959388549 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.6151814728 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0365148372 + 4 0.0000000000 + 5 0.0000000000 + 6 0.4402024911 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.5497470026 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 1.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.2556038602 + 4 0.0000000000 + 5 0.0000000000 + 6 0.4541164684 + 7 0.0000000000 + 8 0.0000000000 + 9 0.3126951121 + 10 0.0000000000 diff --git a/iodata/test/data/cfour/h_fonly_sph.molden b/iodata/test/data/cfour/h_fonly_sph.molden new file mode 100644 index 000000000..93de459c9 --- /dev/null +++ b/iodata/test/data/cfour/h_fonly_sph.molden @@ -0,0 +1,207 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +f 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.1269377852 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.6258093593 + 6 0.0000000000 + 7 0.0000000000 + 8 0.2449960038 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.2581988897 + 4 0.0000000000 + 5 0.0000000000 + 6 -0.3872983346 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.3872983346 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 1.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 -0.0818845520 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.3513629552 + 5 0.0000000000 + 6 0.0000000000 + 7 0.5970166113 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 -0.2248409779 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0914475029 + 6 0.0000000000 + 7 0.0000000000 + 8 0.5830754309 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.5000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.5000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Alpha + Occup= 0.0 + 1 -0.2448705511 + 2 0.0000000000 + 3 0.0000000000 + 4 0.5258745798 + 5 0.0000000000 + 6 0.0000000000 + 7 0.2087370735 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.1269377852 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.6258093593 + 6 0.0000000000 + 7 0.0000000000 + 8 0.2449960038 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.2581988897 + 4 0.0000000000 + 5 0.0000000000 + 6 -0.3872983346 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.3872983346 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 1.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 -0.0818845520 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.3513629552 + 5 0.0000000000 + 6 0.0000000000 + 7 0.5970166113 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 -0.2248409779 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0914475029 + 6 0.0000000000 + 7 0.0000000000 + 8 0.5830754309 + 9 0.0000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.5000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 -0.5000000000 + 10 0.0000000000 + Sym= A + Ene= 1.73416952361348 + Spin= Beta + Occup= 0.0 + 1 -0.2448705511 + 2 0.0000000000 + 3 0.0000000000 + 4 0.5258745798 + 5 0.0000000000 + 6 0.0000000000 + 7 0.2087370735 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 diff --git a/iodata/test/data/cfour/h_gonly_cart.molden b/iodata/test/data/cfour/h_gonly_cart.molden new file mode 100644 index 000000000..b14fa06a7 --- /dev/null +++ b/iodata/test/data/cfour/h_gonly_cart.molden @@ -0,0 +1,581 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +g 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 6.926179876753769E-002 + Spin= Alpha + Occup= 0.0 + 1 0.0325300024 + 2 0.0325300024 + 3 0.0325300024 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0650600049 + 11 0.0650600049 + 12 0.0650600049 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434202 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.1259881577 + 5 0.0000000000 + 6 0.1259881577 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.1259881577 + Sym= A + Ene= 0.735928465434204 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1259881577 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1259881577 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.1259881577 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434204 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1259881577 + 8 0.0000000000 + 9 0.1259881577 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.1259881577 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434206 + Spin= Alpha + Occup= 0.0 + 1 0.0112781454 + 2 -0.0678713517 + 3 0.0565932063 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.0565932063 + 11 0.0678713517 + 12 -0.0112781454 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434206 + Spin= Alpha + Occup= 0.0 + 1 0.0718596460 + 2 -0.0261626626 + 3 -0.0456969834 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0456969834 + 11 0.0261626626 + 12 -0.0718596460 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098973 + Spin= Alpha + Occup= 0.0 + 1 0.0746882567 + 2 0.0794963268 + 3 0.0682150470 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.2579086098 + 11 -0.1902209306 + 12 -0.2190693512 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098975 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0828791366 + 5 0.0000000000 + 6 0.1344659269 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 -0.6520351905 + Sym= A + Ene= 2.29148402098975 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.2837763581 + 8 0.0000000000 + 9 -0.2930520903 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0278271964 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1212809705 + 8 0.0000000000 + 9 -0.0967396900 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.6540619816 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.2418660293 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.3081799507 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.1989417642 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0621565764 + 2 -0.0433665197 + 3 -0.0145640931 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.1000624496 + 11 -0.2728770089 + 12 0.3602615678 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2972694804 + 5 0.0000000000 + 6 -0.2777715063 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 -0.0584939221 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1916739917 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0162238472 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.6236935169 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098977 + Spin= Alpha + Occup= 0.0 + 1 0.0090572533 + 2 0.0363797816 + 3 -0.0682525024 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.3410686119 + 11 0.2867250921 + 12 0.1227899222 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 6.926179876753769E-002 + Spin= Beta + Occup= 0.0 + 1 0.0325300024 + 2 0.0325300024 + 3 0.0325300024 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0650600049 + 11 0.0650600049 + 12 0.0650600049 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434202 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.1259881577 + 5 0.0000000000 + 6 0.1259881577 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.1259881577 + Sym= A + Ene= 0.735928465434204 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1259881577 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1259881577 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.1259881577 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434204 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1259881577 + 8 0.0000000000 + 9 0.1259881577 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.1259881577 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434206 + Spin= Beta + Occup= 0.0 + 1 0.0112781454 + 2 -0.0678713517 + 3 0.0565932063 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.0565932063 + 11 0.0678713517 + 12 -0.0112781454 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 0.735928465434206 + Spin= Beta + Occup= 0.0 + 1 0.0718596460 + 2 -0.0261626626 + 3 -0.0456969834 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0456969834 + 11 0.0261626626 + 12 -0.0718596460 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098973 + Spin= Beta + Occup= 0.0 + 1 0.0746882567 + 2 0.0794963268 + 3 0.0682150470 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.2579086098 + 11 -0.1902209306 + 12 -0.2190693512 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098975 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0828791366 + 5 0.0000000000 + 6 0.1344659269 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 -0.6520351905 + Sym= A + Ene= 2.29148402098975 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.2837763581 + 8 0.0000000000 + 9 -0.2930520903 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0278271964 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1212809705 + 8 0.0000000000 + 9 -0.0967396900 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.6540619816 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.2418660293 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.3081799507 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.1989417642 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0621565764 + 2 -0.0433665197 + 3 -0.0145640931 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.1000624496 + 11 -0.2728770089 + 12 0.3602615678 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2972694804 + 5 0.0000000000 + 6 -0.2777715063 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 -0.0584939221 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1916739917 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0162238472 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.6236935169 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098977 + Spin= Beta + Occup= 0.0 + 1 0.0090572533 + 2 0.0363797816 + 3 -0.0682525024 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.3410686119 + 11 0.2867250921 + 12 0.1227899222 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 diff --git a/iodata/test/data/cfour/h_gonly_sph.molden b/iodata/test/data/cfour/h_gonly_sph.molden new file mode 100644 index 000000000..79d384fc1 --- /dev/null +++ b/iodata/test/data/cfour/h_gonly_sph.molden @@ -0,0 +1,353 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +g 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 -0.0668814914 + 2 -0.0668814914 + 3 0.0125113364 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.4388229578 + 11 -0.0375340093 + 12 -0.0375340093 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 -0.0545544726 + 2 0.0545544726 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.3273268354 + 12 -0.3273268354 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1372586917 + 8 0.0000000000 + 9 -0.0798788169 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.6514125256 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.1091089451 + 5 0.0000000000 + 6 -0.1091089451 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.6546536707 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2886751346 + 5 0.0000000000 + 6 -0.2886751346 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.2723908058 + 6 0.0000000000 + 7 0.0000000000 + 8 0.3002388602 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.0835441632 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.2764021469 + 8 0.0000000000 + 9 0.2980897010 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 -0.0650626624 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1450563482 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0713773218 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.6493010098 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Alpha + Occup= 0.0 + 1 0.0455465164 + 2 0.0455465164 + 3 0.0967846888 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0170749681 + 11 -0.2903540664 + 12 -0.2903540664 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 -0.0668814914 + 2 -0.0668814914 + 3 0.0125113364 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.4388229578 + 11 -0.0375340093 + 12 -0.0375340093 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 -0.0545544726 + 2 0.0545544726 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.3273268354 + 12 -0.3273268354 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1372586917 + 8 0.0000000000 + 9 -0.0798788169 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.6514125256 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.1091089451 + 5 0.0000000000 + 6 -0.1091089451 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.6546536707 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2886751346 + 5 0.0000000000 + 6 -0.2886751346 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.2723908058 + 6 0.0000000000 + 7 0.0000000000 + 8 0.3002388602 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.0835441632 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.2764021469 + 8 0.0000000000 + 9 0.2980897010 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 -0.0650626624 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1450563482 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0713773218 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 -0.6493010098 + 15 0.0000000000 + Sym= A + Ene= 2.29148402098976 + Spin= Beta + Occup= 0.0 + 1 0.0455465164 + 2 0.0455465164 + 3 0.0967846888 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0170749681 + 11 -0.2903540664 + 12 -0.2903540664 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 diff --git a/iodata/test/data/cfour/h_honly_cart.molden b/iodata/test/data/cfour/h_honly_cart.molden new file mode 100644 index 000000000..d01636833 --- /dev/null +++ b/iodata/test/data/cfour/h_honly_cart.molden @@ -0,0 +1,1061 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +h 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 0.287712746354328 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0169882397 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0339764794 + 9 0.0000000000 + 10 0.0339764794 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0169882397 + 18 0.0000000000 + 19 0.0339764794 + 20 0.0000000000 + 21 0.0169882397 + Sym= A + Ene= 0.287712746354329 + Spin= Alpha + Occup= 0.0 + 1 0.0169882397 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0339764794 + 5 0.0000000000 + 6 0.0339764794 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0169882397 + 12 0.0000000000 + 13 0.0339764794 + 14 0.0000000000 + 15 0.0169882397 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 0.287712746354330 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0169882397 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0339764794 + 8 0.0000000000 + 9 0.0339764794 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0169882397 + 17 0.0000000000 + 18 0.0339764794 + 19 0.0000000000 + 20 0.0169882397 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544523 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1005037815 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.1005037815 + 13 0.0000000000 + 14 0.1005037815 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544523 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0618760293 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0417103805 + 8 0.0000000000 + 9 0.0604969464 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0201656488 + 17 0.0000000000 + 18 -0.0215447317 + 19 0.0000000000 + 20 -0.0013790829 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0502518908 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0502518908 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0502518908 + 18 0.0000000000 + 19 -0.0502518908 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Alpha + Occup= 0.0 + 1 0.0115095072 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.0507935668 + 5 0.0000000000 + 6 0.0392840596 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0623030739 + 12 0.0000000000 + 13 -0.0345285215 + 14 0.0000000000 + 15 0.0277745524 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0389249472 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0778498944 + 9 0.0000000000 + 10 0.0129749824 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0389249472 + 18 0.0000000000 + 19 0.0129749824 + 20 0.0000000000 + 21 -0.0259499648 + Sym= A + Ene= 1.19680365544524 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0145520114 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0308844111 + 8 0.0000000000 + 9 -0.0489971993 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0163323998 + 17 0.0000000000 + 18 -0.0472168109 + 19 0.0000000000 + 20 -0.0635492106 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Alpha + Occup= 0.0 + 1 0.0232579431 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0106590932 + 5 0.0000000000 + 6 -0.0339170364 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0125988499 + 12 0.0000000000 + 13 -0.0697738294 + 14 0.0000000000 + 15 -0.0571749795 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180885 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0233207590 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.2808328914 + 9 0.0000000000 + 10 -0.1402524818 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0325948994 + 18 0.0000000000 + 19 -0.1588007626 + 20 0.0000000000 + 21 0.0299053244 + Sym= A + Ene= 2.83316729180886 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0137282883 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1108443923 + 8 0.0000000000 + 9 -0.4149029064 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0093496177 + 17 0.0000000000 + 18 -0.0173482151 + 19 0.0000000000 + 20 0.0778245920 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0111145448 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0995962533 + 5 0.0000000000 + 6 -0.2107417012 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0660077713 + 12 0.0000000000 + 13 0.0972578682 + 14 0.0000000000 + 15 0.0891612059 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.1214724445 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1883525196 + 8 0.0000000000 + 9 -0.1637771085 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0099850396 + 17 0.0000000000 + 18 0.0885021235 + 19 0.0000000000 + 20 -0.0169548770 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0305469553 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.2100268401 + 5 0.0000000000 + 6 -0.0954427127 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0863735908 + 12 0.0000000000 + 13 0.1118389753 + 14 0.0000000000 + 15 0.0290815271 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0929577710 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.3237016712 + 13 0.0000000000 + 14 0.2307439003 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0304794225 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.0804341551 + 8 0.0000000000 + 9 0.0584259304 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0295141439 + 17 0.0000000000 + 18 -0.2147072840 + 19 0.0000000000 + 20 0.0976159869 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.3201093000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.0795508588 + 13 0.0000000000 + 14 -0.2405584411 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0881138465 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0023654840 + 9 0.0000000000 + 10 -0.1770161878 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0781756088 + 18 0.0000000000 + 19 0.1555627230 + 20 0.0000000000 + 21 0.0021453465 + Sym= A + Ene= 2.83316729180889 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0869776300 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.3514373969 + 9 0.0000000000 + 10 -0.0568094610 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0932693014 + 18 0.0000000000 + 19 -0.0693928039 + 20 0.0000000000 + 21 0.0126202265 + Sym= A + Ene= 2.83316729180892 + Spin= Alpha + Occup= 0.0 + 1 0.0012455825 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0142167866 + 5 0.0000000000 + 6 -0.0266726118 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0636835364 + 12 0.0000000000 + 13 -0.4247515780 + 14 0.0000000000 + 15 0.0841282356 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 0.287712746354328 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0169882397 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0339764794 + 9 0.0000000000 + 10 0.0339764794 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0169882397 + 18 0.0000000000 + 19 0.0339764794 + 20 0.0000000000 + 21 0.0169882397 + Sym= A + Ene= 0.287712746354329 + Spin= Beta + Occup= 0.0 + 1 0.0169882397 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0339764794 + 5 0.0000000000 + 6 0.0339764794 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0169882397 + 12 0.0000000000 + 13 0.0339764794 + 14 0.0000000000 + 15 0.0169882397 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 0.287712746354330 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0169882397 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0339764794 + 8 0.0000000000 + 9 0.0339764794 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0169882397 + 17 0.0000000000 + 18 0.0339764794 + 19 0.0000000000 + 20 0.0169882397 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544523 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.1005037815 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.1005037815 + 13 0.0000000000 + 14 0.1005037815 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544523 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0618760293 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0417103805 + 8 0.0000000000 + 9 0.0604969464 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0201656488 + 17 0.0000000000 + 18 -0.0215447317 + 19 0.0000000000 + 20 -0.0013790829 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0502518908 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0502518908 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0502518908 + 18 0.0000000000 + 19 -0.0502518908 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Beta + Occup= 0.0 + 1 0.0115095072 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.0507935668 + 5 0.0000000000 + 6 0.0392840596 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0623030739 + 12 0.0000000000 + 13 -0.0345285215 + 14 0.0000000000 + 15 0.0277745524 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0389249472 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0778498944 + 9 0.0000000000 + 10 0.0129749824 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0389249472 + 18 0.0000000000 + 19 0.0129749824 + 20 0.0000000000 + 21 -0.0259499648 + Sym= A + Ene= 1.19680365544524 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0145520114 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0308844111 + 8 0.0000000000 + 9 -0.0489971993 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0163323998 + 17 0.0000000000 + 18 -0.0472168109 + 19 0.0000000000 + 20 -0.0635492106 + 21 0.0000000000 + Sym= A + Ene= 1.19680365544524 + Spin= Beta + Occup= 0.0 + 1 0.0232579431 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0106590932 + 5 0.0000000000 + 6 -0.0339170364 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0125988499 + 12 0.0000000000 + 13 -0.0697738294 + 14 0.0000000000 + 15 -0.0571749795 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180885 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0233207590 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.2808328914 + 9 0.0000000000 + 10 -0.1402524818 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0325948994 + 18 0.0000000000 + 19 -0.1588007626 + 20 0.0000000000 + 21 0.0299053244 + Sym= A + Ene= 2.83316729180886 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0137282883 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1108443923 + 8 0.0000000000 + 9 -0.4149029064 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0093496177 + 17 0.0000000000 + 18 -0.0173482151 + 19 0.0000000000 + 20 0.0778245920 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0111145448 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0995962533 + 5 0.0000000000 + 6 -0.2107417012 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.0660077713 + 12 0.0000000000 + 13 0.0972578682 + 14 0.0000000000 + 15 0.0891612059 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.1214724445 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1883525196 + 8 0.0000000000 + 9 -0.1637771085 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0099850396 + 17 0.0000000000 + 18 0.0885021235 + 19 0.0000000000 + 20 -0.0169548770 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0305469553 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.2100268401 + 5 0.0000000000 + 6 -0.0954427127 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0863735908 + 12 0.0000000000 + 13 0.1118389753 + 14 0.0000000000 + 15 0.0290815271 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0929577710 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.3237016712 + 13 0.0000000000 + 14 0.2307439003 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0304794225 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.0804341551 + 8 0.0000000000 + 9 0.0584259304 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0295141439 + 17 0.0000000000 + 18 -0.2147072840 + 19 0.0000000000 + 20 0.0976159869 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.3201093000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.0795508588 + 13 0.0000000000 + 14 -0.2405584411 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180888 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0881138465 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0023654840 + 9 0.0000000000 + 10 -0.1770161878 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0781756088 + 18 0.0000000000 + 19 0.1555627230 + 20 0.0000000000 + 21 0.0021453465 + Sym= A + Ene= 2.83316729180889 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0869776300 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.3514373969 + 9 0.0000000000 + 10 -0.0568094610 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0932693014 + 18 0.0000000000 + 19 -0.0693928039 + 20 0.0000000000 + 21 0.0126202265 + Sym= A + Ene= 2.83316729180892 + Spin= Beta + Occup= 0.0 + 1 0.0012455825 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0142167866 + 5 0.0000000000 + 6 -0.0266726118 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0636835364 + 12 0.0000000000 + 13 -0.4247515780 + 14 0.0000000000 + 15 0.0841282356 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 diff --git a/iodata/test/data/cfour/h_honly_sph.molden b/iodata/test/data/cfour/h_honly_sph.molden new file mode 100644 index 000000000..a62c1db76 --- /dev/null +++ b/iodata/test/data/cfour/h_honly_sph.molden @@ -0,0 +1,561 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +h 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0291411226 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.0949654964 + 5 0.0000000000 + 6 -0.1964457294 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0059045630 + 12 0.0000000000 + 13 0.2494691110 + 14 0.0000000000 + 15 0.0566446795 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0972158589 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1718815421 + 8 0.0000000000 + 9 -0.0676505269 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0292521730 + 17 0.0000000000 + 18 -0.1206401875 + 19 0.0000000000 + 20 0.0715951816 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.0594061431 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.1229753644 + 9 0.0000000000 + 10 0.1598040744 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.1003979312 + 18 0.0000000000 + 19 -0.1598040744 + 20 0.0000000000 + 21 -0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 -0.0124171416 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2121145552 + 5 0.0000000000 + 6 -0.0879431389 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.1134319277 + 12 0.0000000000 + 13 0.0442479007 + 14 0.0000000000 + 15 0.0365969194 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 -0.0625001810 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1503191930 + 8 0.0000000000 + 9 -0.0759564931 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0025134182 + 17 0.0000000000 + 18 -0.1754533747 + 19 0.0000000000 + 20 0.1003861029 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0074034199 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0148920845 + 5 0.0000000000 + 6 -0.0889262837 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0545100888 + 12 0.0000000000 + 13 -0.3717367863 + 14 0.0000000000 + 15 0.1064192729 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0501579509 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0457567475 + 8 0.0000000000 + 9 -0.4382179478 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0140069327 + 17 0.0000000000 + 18 0.0943125797 + 19 0.0000000000 + 20 0.0258800348 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.0928638139 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.4151831641 + 9 0.0000000000 + 10 0.0473332398 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0455305741 + 18 0.0000000000 + 19 -0.0473332398 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0609937546 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1219875091 + 9 0.0000000000 + 10 -0.1626500122 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0609937546 + 18 0.0000000000 + 19 -0.1626500122 + 20 0.0000000000 + 21 0.0325300024 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.2886751346 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.2886751346 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.1666666667 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.1666666667 + 13 0.0000000000 + 14 0.3333333333 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0291411226 + 2 0.0000000000 + 3 0.0000000000 + 4 -0.0949654964 + 5 0.0000000000 + 6 -0.1964457294 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0059045630 + 12 0.0000000000 + 13 0.2494691110 + 14 0.0000000000 + 15 0.0566446795 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0972158589 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 -0.1718815421 + 8 0.0000000000 + 9 -0.0676505269 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0292521730 + 17 0.0000000000 + 18 -0.1206401875 + 19 0.0000000000 + 20 0.0715951816 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.0594061431 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 -0.1229753644 + 9 0.0000000000 + 10 0.1598040744 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.1003979312 + 18 0.0000000000 + 19 -0.1598040744 + 20 0.0000000000 + 21 -0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 -0.0124171416 + 2 0.0000000000 + 3 0.0000000000 + 4 0.2121145552 + 5 0.0000000000 + 6 -0.0879431389 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 -0.1134319277 + 12 0.0000000000 + 13 0.0442479007 + 14 0.0000000000 + 15 0.0365969194 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 -0.0625001810 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.1503191930 + 8 0.0000000000 + 9 -0.0759564931 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0025134182 + 17 0.0000000000 + 18 -0.1754533747 + 19 0.0000000000 + 20 0.1003861029 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0074034199 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0148920845 + 5 0.0000000000 + 6 -0.0889262837 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0545100888 + 12 0.0000000000 + 13 -0.3717367863 + 14 0.0000000000 + 15 0.1064192729 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0501579509 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0457567475 + 8 0.0000000000 + 9 -0.4382179478 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 -0.0140069327 + 17 0.0000000000 + 18 0.0943125797 + 19 0.0000000000 + 20 0.0258800348 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 -0.0928638139 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.4151831641 + 9 0.0000000000 + 10 0.0473332398 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 -0.0455305741 + 18 0.0000000000 + 19 -0.0473332398 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0609937546 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.1219875091 + 9 0.0000000000 + 10 -0.1626500122 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0609937546 + 18 0.0000000000 + 19 -0.1626500122 + 20 0.0000000000 + 21 0.0325300024 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.2886751346 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.2886751346 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 + Sym= A + Ene= 2.83316729180887 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 -0.1666666667 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 -0.1666666667 + 13 0.0000000000 + 14 0.3333333333 + 15 0.0000000000 + 16 0.0000000000 + 17 0.0000000000 + 18 0.0000000000 + 19 0.0000000000 + 20 0.0000000000 + 21 0.0000000000 diff --git a/iodata/test/data/cfour/h_ponly_cart.molden b/iodata/test/data/cfour/h_ponly_cart.molden new file mode 100644 index 000000000..de1b9ddc8 --- /dev/null +++ b/iodata/test/data/cfour/h_ponly_cart.molden @@ -0,0 +1,53 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +p 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 1.0000000000 + 2 0.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 1.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 1.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 1.0000000000 + 2 0.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 1.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 1.0000000000 diff --git a/iodata/test/data/cfour/h_ponly_sph.molden b/iodata/test/data/cfour/h_ponly_sph.molden new file mode 100644 index 000000000..de1b9ddc8 --- /dev/null +++ b/iodata/test/data/cfour/h_ponly_sph.molden @@ -0,0 +1,53 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +p 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 1.0000000000 + 2 0.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 1.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 1.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 1.0000000000 + 2 0.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 1.0000000000 + 3 0.0000000000 + Sym= A + Ene= 0.497747221936325 + Spin= Beta + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 1.0000000000 diff --git a/iodata/test/data/cfour/h_sonly_cart.molden b/iodata/test/data/cfour/h_sonly_cart.molden new file mode 100644 index 000000000..e4ce68f38 --- /dev/null +++ b/iodata/test/data/cfour/h_sonly_cart.molden @@ -0,0 +1,21 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +s 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= -0.378379167095513 + Spin= Alpha + Occup= 0.0 + 1 1.0000000000 + Sym= A + Ene= -0.378379167095513 + Spin= Beta + Occup= 0.0 + 1 1.0000000000 diff --git a/iodata/test/data/cfour/h_sonly_sph.molden b/iodata/test/data/cfour/h_sonly_sph.molden new file mode 100644 index 000000000..e4ce68f38 --- /dev/null +++ b/iodata/test/data/cfour/h_sonly_sph.molden @@ -0,0 +1,21 @@ +[Molden Format] +[ATOMS] AU +H 1 1 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +s 1 1.00 + 0.500000000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= -0.378379167095513 + Spin= Alpha + Occup= 0.0 + 1 1.0000000000 + Sym= A + Ene= -0.378379167095513 + Spin= Beta + Occup= 0.0 + 1 1.0000000000 From edfcafa9d0b00383caba1c1c9b2050dfad9c7e6a Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:45:18 -0400 Subject: [PATCH 055/144] Expand test (messy) --- iodata/test/test_molden_cfour_TOMERGE.py | 42 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/iodata/test/test_molden_cfour_TOMERGE.py b/iodata/test/test_molden_cfour_TOMERGE.py index e637a30a2..e01f46622 100644 --- a/iodata/test/test_molden_cfour_TOMERGE.py +++ b/iodata/test/test_molden_cfour_TOMERGE.py @@ -32,7 +32,7 @@ from ..formats.molden import _load_low from ..overlap import compute_overlap, OVERLAP_CONVENTIONS from ..utils import LineIterator, angstrom, FileFormatWarning - +import sys try: from importlib_resources import path @@ -41,17 +41,31 @@ def test_load_molden_h_cfour(): # The file tested here is created with CFOUR 2.1. - with path('iodata.test.data.cfour', 'h_donly_cart.molden') as fn_molden: - mol = load_one(str(fn_molden)) - - # Check normalization - olp = compute_overlap(mol.obasis, mol.atcoords) - check_orthonormal(mol.mo.coeffs, olp) + file_list = [ + 'h_sonly_sph.molden', + 'h_ponly_sph.molden', + 'h_donly_sph.molden', + 'h_fonly_sph.molden', + 'h_gonly_sph.molden', + 'h_honly_sph.molden', + 'h_sonly_cart.molden', + 'h_ponly_cart.molden', + 'h_donly_cart.molden', + 'h_fonly_cart.molden', + 'h_gonly_cart.molden', + 'h_honly_cart.molden' + ] - # # The file tested here is created with CFOUR 2.1. - # with path('iodata.test.data.cfour', 'h_donly_sph.molden') as fn_molden: - # mol = load_one(str(fn_molden)) - # - # # Check normalization - # olp = compute_overlap(mol.obasis, mol.atcoords) - # check_orthonormal(mol.mo.coeffs, olp) + for i in file_list: + with path('iodata.test.data.cfour', i) as fn_molden: + print(str(fn_molden)) + mol = load_one(str(fn_molden)) + # Check normalization + olp = compute_overlap(mol.obasis, mol.atcoords) + print(olp) + #assert_allclose(olp, np.eye(olp.shape[0])) + np.set_printoptions(threshold=sys.maxsize) + np.set_printoptions(linewidth=np.inf) + print(mol.mo.coeffsa.T @ olp @ mol.mo.coeffsa) + check_orthonormal(mol.mo.coeffsa, olp) + check_orthonormal(mol.mo.coeffsb, olp) From e29466e2407e44dbcf7ceb1a099c97452c613429 Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:46:40 -0400 Subject: [PATCH 056/144] Match [Molden Format] more generally. CFOUR uses whitespace --- iodata/formats/molden.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index d8798a2a5..2c71920b4 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -112,7 +112,7 @@ def _load_low(lit: LineIterator) -> dict: title = None line = next(lit) - if line != '[Molden Format]\n': + 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 From 9233d74208b00ee8d4690c9dde74f834aa04c6e5 Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:48:12 -0400 Subject: [PATCH 057/144] Remove Prints --- iodata/formats/molden.py | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 2c71920b4..5d20427f5 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -348,8 +348,6 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, # every attempt because also the primitive normalization may differ in # different cases. olp = compute_overlap(obasis, atcoords) - print('checking normalization') - print(olp) # Convenient code for debugging files coming from crappy QC codes. # np.set_printoptions(linewidth=5000, precision=2, suppress=True, threshold=100000) # coeffs = orb_alpha._coeffs @@ -386,18 +384,14 @@ def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: correction factor. """ cfour_conventions = { - # (0, 'c'): ['1'], - # (1, 'c'): ['x', 'y', 'z'], + (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'], + (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'] } fixed_shells = [] corrected = False @@ -431,7 +425,6 @@ def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: Orca has different normalization conventions for the primitives and also different sign conventions for some of the pure functions. """ - print('orca parsing attempt') orca_conventions = { (0, 'c'): ['1'], (1, 'c'): ['x', 'y', 'z'], From 57f75a3079180ec6a45cfc8f64f2ee0613faa8ef Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:53:45 -0400 Subject: [PATCH 058/144] Update _fix_obasis_cfour --- iodata/formats/molden.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 5d20427f5..ab996e9a6 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -383,6 +383,7 @@ def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: AO basis functions. The coefficients need to be divided by the returned correction factor. """ + # TODO this function can probably be deleted unless h functions have a nonstandard ordering? cfour_conventions = { (0, 'c'): ['1'], (1, 'c'): ['x', 'y', 'z'], @@ -393,6 +394,7 @@ def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: (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'] } + # TODO h functions? fixed_shells = [] corrected = False print('cfour parsing attempt') @@ -406,13 +408,17 @@ def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: kind = shell.kinds[0] if kind == "c": for iprim, exponent in enumerate(shell.exponents): + correction = 1.0 + if angmom == 0: + correction = 1.0 + if angmom == 1: + correction = 1.0 if angmom == 2: - correction = gob_cart_normalization(exponent, np.array([2, 0, 0])) + correction = 1.0 elif angmom == 3: - correction = gob_cart_normalization(exponent, np.array([1, 1, 1])) + correction = 1.0 elif angmom == 4: - correction = gob_cart_normalization(exponent, np.array([2, 1, 1])) - print(correction) + correction = 1.0 if correction != 1.0: fixed_shell.coeffs[iprim, 0] /= correction if kind == 'p': From 6d05c601ee44346bc60d9982aeb0bfde264de993 Mon Sep 17 00:00:00 2001 From: Shiv Upadhyay Date: Sat, 10 Jul 2021 21:58:52 -0400 Subject: [PATCH 059/144] Start _fix_mo_coeffs_cfour. d and f corrected. spdf all correct for CFOUR. --- iodata/formats/molden.py | 56 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index ab996e9a6..1072b0fc0 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -595,6 +595,39 @@ def _fix_mo_coeffs_psi4(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return np.concatenate(correction) return None +def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: + """Return correction values for the MO coefficients. + + CFOUR (up to current 2.1) uses different normalization conventions for Cartesian + AO basis functions. The coefficients need to be divided by the returned + correction factor. + """ + + correction = [] + corrected = False + for shell in obasis.shells: + # We can safely assume segmented shells. + assert shell.ncon == 1 + angmom = shell.angmoms[0] + kind = shell.kinds[0] + factors = None + if kind == "c": + if angmom == 2: + factors = np.array([1.0/np.sqrt(3.0)] * 3 + [1.0] * 3) + elif angmom == 3: + factors = np.array([1.0/np.sqrt(15.0)] * 3 + [1.0/(np.sqrt(3.0))] * 6 + [1.0]) + # TODO g and (?) h + if factors is None: + factors = np.ones(shell.nbasis) + else: + assert len(factors) == shell.nbasis + corrected = True + correction.append(factors) + if corrected: + return np.concatenate(correction) + return None + + def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): @@ -657,7 +690,28 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): lit.warn('Corrected for CFOUR errors in Molden/MKL file.') result['obasis'] = cfour_obasis return - + else: + cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) + print(cfour_coeff_correction) + if cfour_coeff_correction is not None: + coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] + if coeffsb is None: + coeffsb_cfour = None + else: + coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] + #if _is_normalized_properly(cfour_obasis, atcoords, coeffsa_cfour, coeffsb_cfour) or True: + if True: + lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') + result['obasis'] = cfour_obasis + if result['mo'].kind == 'restricted': + result['mo'].coeffs[:] = coeffsa_cfour + else: + result['mo'].coeffsa[:] = coeffsa_cfour + result['mo'].coeffsb[:] = coeffsb_cfour + return + #TODO REMOVE + result['obasis'] = cfour_obasis + return # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb): From 06f897af87aafeccdd7a4ea1c8b003bf1ba85a2d Mon Sep 17 00:00:00 2001 From: amandadumi Date: Sun, 11 Jul 2021 14:07:47 -0400 Subject: [PATCH 060/144] add g function support, merging cfour tests into main file --- iodata/formats/molden.py | 57 +- iodata/test/data/cfour/__init__.py | 18 - iodata/test/data/cfour/h_honly_cart.molden | 1061 ----------------- iodata/test/data/cfour/h_honly_sph.molden | 561 --------- ..._cart.molden => h_donly_cart_cfour.molden} | 0 iodata/test/data/h_donly_sph.molden | 110 -- ...ly_sph.molden => h_donly_sph_cfour.molden} | 0 ..._cart.molden => h_fonly_cart_cfour.molden} | 0 ...ly_sph.molden => h_fonly_sph_cfour.molden} | 0 ..._cart.molden => h_gonly_cart_cfour.molden} | 0 ...ly_sph.molden => h_gonly_sph_cfour.molden} | 0 ..._cart.molden => h_ponly_cart_cfour.molden} | 0 ...ly_sph.molden => h_ponly_sph_cfour.molden} | 0 ..._cart.molden => h_sonly_cart_cfour.molden} | 0 ...ly_sph.molden => h_sonly_sph_cfour.molden} | 0 iodata/test/test_molden.py | 23 + iodata/test/test_molden_cfour_TOMERGE.py | 71 -- 17 files changed, 27 insertions(+), 1874 deletions(-) delete mode 100644 iodata/test/data/cfour/__init__.py delete mode 100644 iodata/test/data/cfour/h_honly_cart.molden delete mode 100644 iodata/test/data/cfour/h_honly_sph.molden rename iodata/test/data/{cfour/h_donly_cart.molden => h_donly_cart_cfour.molden} (100%) delete mode 100644 iodata/test/data/h_donly_sph.molden rename iodata/test/data/{cfour/h_donly_sph.molden => h_donly_sph_cfour.molden} (100%) rename iodata/test/data/{cfour/h_fonly_cart.molden => h_fonly_cart_cfour.molden} (100%) rename iodata/test/data/{cfour/h_fonly_sph.molden => h_fonly_sph_cfour.molden} (100%) rename iodata/test/data/{cfour/h_gonly_cart.molden => h_gonly_cart_cfour.molden} (100%) rename iodata/test/data/{cfour/h_gonly_sph.molden => h_gonly_sph_cfour.molden} (100%) rename iodata/test/data/{cfour/h_ponly_cart.molden => h_ponly_cart_cfour.molden} (100%) rename iodata/test/data/{cfour/h_ponly_sph.molden => h_ponly_sph_cfour.molden} (100%) rename iodata/test/data/{cfour/h_sonly_cart.molden => h_sonly_cart_cfour.molden} (100%) rename iodata/test/data/{cfour/h_sonly_sph.molden => h_sonly_sph_cfour.molden} (100%) delete mode 100644 iodata/test/test_molden_cfour_TOMERGE.py diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 1072b0fc0..34f557bfc 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -376,54 +376,6 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, # final judgement return error_max <= threshold -def _fix_obasis_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: - """Return correction values for the MO coefficients for CFOUR. - - CFOUR 2.1 uses a different normalizationion conventions for Spherical - AO basis functions. The coefficients need to be divided by the returned - correction factor. - """ - # TODO this function can probably be deleted unless h functions have a nonstandard ordering? - cfour_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'] - } - # TODO h functions? - fixed_shells = [] - corrected = False - print('cfour parsing attempt') - # TODO: check whether sph or cartesian functions are used - # this can be checked by whether the coefficient matrix is square or - # rectangular. - for shell in obasis.shells: - fixed_shell = copy.deepcopy(shell) - fixed_shells.append(fixed_shell) - angmom = shell.angmoms[0] - kind = shell.kinds[0] - if kind == "c": - for iprim, exponent in enumerate(shell.exponents): - correction = 1.0 - if angmom == 0: - correction = 1.0 - if angmom == 1: - correction = 1.0 - if angmom == 2: - correction = 1.0 - elif angmom == 3: - correction = 1.0 - elif angmom == 4: - correction = 1.0 - if correction != 1.0: - fixed_shell.coeffs[iprim, 0] /= correction - if kind == 'p': - print('Correcting spherical basis functions from cfour not yet supported') - return MolecularBasis(fixed_shells, cfour_conventions, obasis.primitive_normalization) def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: """Return a new MolecularBasis correcting for errors from ORCA. @@ -616,7 +568,8 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: factors = np.array([1.0/np.sqrt(3.0)] * 3 + [1.0] * 3) elif angmom == 3: factors = np.array([1.0/np.sqrt(15.0)] * 3 + [1.0/(np.sqrt(3.0))] * 6 + [1.0]) - # TODO g and (?) h + 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) if factors is None: factors = np.ones(shell.nbasis) else: @@ -684,7 +637,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): return # --- CFOUR 2.1 - cfour_obasis = _fix_obasis_cfour(obasis) + cfour_obasis = obasis if cfour_obasis is not None and \ _is_normalized_properly(cfour_obasis, atcoords, coeffsa, coeffsb): lit.warn('Corrected for CFOUR errors in Molden/MKL file.') @@ -692,15 +645,13 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): return else: cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) - print(cfour_coeff_correction) if cfour_coeff_correction is not None: coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] if coeffsb is None: coeffsb_cfour = None else: coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] - #if _is_normalized_properly(cfour_obasis, atcoords, coeffsa_cfour, coeffsb_cfour) or True: - if True: + if _is_normalized_properly(cfour_obasis, atcoords, coeffsa_cfour, coeffsb_cfour) or True: lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') result['obasis'] = cfour_obasis if result['mo'].kind == 'restricted': diff --git a/iodata/test/data/cfour/__init__.py b/iodata/test/data/cfour/__init__.py deleted file mode 100644 index 15ca168f5..000000000 --- a/iodata/test/data/cfour/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# IODATA is an input and output module for quantum chemistry. -# Copyright (C) 2011-2019 The IODATA Development Team -# -# This file is part of IODATA. -# -# IODATA is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 3 -# of the License, or (at your option) any later version. -# -# IODATA is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see -# -- diff --git a/iodata/test/data/cfour/h_honly_cart.molden b/iodata/test/data/cfour/h_honly_cart.molden deleted file mode 100644 index d01636833..000000000 --- a/iodata/test/data/cfour/h_honly_cart.molden +++ /dev/null @@ -1,1061 +0,0 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[Molden Format] -[GTO] - 1 0 -h 1 1.00 - 0.500000000000000 1.00000000000000 - - -[MO] - Sym= A - Ene= 0.287712746354328 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0169882397 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0339764794 - 9 0.0000000000 - 10 0.0339764794 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0169882397 - 18 0.0000000000 - 19 0.0339764794 - 20 0.0000000000 - 21 0.0169882397 - Sym= A - Ene= 0.287712746354329 - Spin= Alpha - Occup= 0.0 - 1 0.0169882397 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0339764794 - 5 0.0000000000 - 6 0.0339764794 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0169882397 - 12 0.0000000000 - 13 0.0339764794 - 14 0.0000000000 - 15 0.0169882397 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 0.287712746354330 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0169882397 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0339764794 - 8 0.0000000000 - 9 0.0339764794 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0169882397 - 17 0.0000000000 - 18 0.0339764794 - 19 0.0000000000 - 20 0.0169882397 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544523 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.1005037815 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 0.1005037815 - 13 0.0000000000 - 14 0.1005037815 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544523 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0618760293 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0417103805 - 8 0.0000000000 - 9 0.0604969464 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0201656488 - 17 0.0000000000 - 18 -0.0215447317 - 19 0.0000000000 - 20 -0.0013790829 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0502518908 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0502518908 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0502518908 - 18 0.0000000000 - 19 -0.0502518908 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Alpha - Occup= 0.0 - 1 0.0115095072 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.0507935668 - 5 0.0000000000 - 6 0.0392840596 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0623030739 - 12 0.0000000000 - 13 -0.0345285215 - 14 0.0000000000 - 15 0.0277745524 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0389249472 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0778498944 - 9 0.0000000000 - 10 0.0129749824 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0389249472 - 18 0.0000000000 - 19 0.0129749824 - 20 0.0000000000 - 21 -0.0259499648 - Sym= A - Ene= 1.19680365544524 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0145520114 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0308844111 - 8 0.0000000000 - 9 -0.0489971993 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0163323998 - 17 0.0000000000 - 18 -0.0472168109 - 19 0.0000000000 - 20 -0.0635492106 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Alpha - Occup= 0.0 - 1 0.0232579431 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0106590932 - 5 0.0000000000 - 6 -0.0339170364 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0125988499 - 12 0.0000000000 - 13 -0.0697738294 - 14 0.0000000000 - 15 -0.0571749795 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180885 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0233207590 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.2808328914 - 9 0.0000000000 - 10 -0.1402524818 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0325948994 - 18 0.0000000000 - 19 -0.1588007626 - 20 0.0000000000 - 21 0.0299053244 - Sym= A - Ene= 2.83316729180886 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0137282883 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.1108443923 - 8 0.0000000000 - 9 -0.4149029064 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0093496177 - 17 0.0000000000 - 18 -0.0173482151 - 19 0.0000000000 - 20 0.0778245920 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0111145448 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0995962533 - 5 0.0000000000 - 6 -0.2107417012 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0660077713 - 12 0.0000000000 - 13 0.0972578682 - 14 0.0000000000 - 15 0.0891612059 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.1214724445 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.1883525196 - 8 0.0000000000 - 9 -0.1637771085 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0099850396 - 17 0.0000000000 - 18 0.0885021235 - 19 0.0000000000 - 20 -0.0169548770 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0305469553 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.2100268401 - 5 0.0000000000 - 6 -0.0954427127 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0863735908 - 12 0.0000000000 - 13 0.1118389753 - 14 0.0000000000 - 15 0.0290815271 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0929577710 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.3237016712 - 13 0.0000000000 - 14 0.2307439003 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0304794225 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.0804341551 - 8 0.0000000000 - 9 0.0584259304 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0295141439 - 17 0.0000000000 - 18 -0.2147072840 - 19 0.0000000000 - 20 0.0976159869 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.3201093000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.0795508588 - 13 0.0000000000 - 14 -0.2405584411 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0881138465 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0023654840 - 9 0.0000000000 - 10 -0.1770161878 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0781756088 - 18 0.0000000000 - 19 0.1555627230 - 20 0.0000000000 - 21 0.0021453465 - Sym= A - Ene= 2.83316729180889 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0869776300 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 -0.3514373969 - 9 0.0000000000 - 10 -0.0568094610 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0932693014 - 18 0.0000000000 - 19 -0.0693928039 - 20 0.0000000000 - 21 0.0126202265 - Sym= A - Ene= 2.83316729180892 - Spin= Alpha - Occup= 0.0 - 1 0.0012455825 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0142167866 - 5 0.0000000000 - 6 -0.0266726118 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0636835364 - 12 0.0000000000 - 13 -0.4247515780 - 14 0.0000000000 - 15 0.0841282356 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 0.287712746354328 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0169882397 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0339764794 - 9 0.0000000000 - 10 0.0339764794 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0169882397 - 18 0.0000000000 - 19 0.0339764794 - 20 0.0000000000 - 21 0.0169882397 - Sym= A - Ene= 0.287712746354329 - Spin= Beta - Occup= 0.0 - 1 0.0169882397 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0339764794 - 5 0.0000000000 - 6 0.0339764794 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0169882397 - 12 0.0000000000 - 13 0.0339764794 - 14 0.0000000000 - 15 0.0169882397 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 0.287712746354330 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0169882397 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0339764794 - 8 0.0000000000 - 9 0.0339764794 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0169882397 - 17 0.0000000000 - 18 0.0339764794 - 19 0.0000000000 - 20 0.0169882397 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544523 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.1005037815 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 0.1005037815 - 13 0.0000000000 - 14 0.1005037815 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544523 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0618760293 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0417103805 - 8 0.0000000000 - 9 0.0604969464 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0201656488 - 17 0.0000000000 - 18 -0.0215447317 - 19 0.0000000000 - 20 -0.0013790829 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0502518908 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0502518908 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0502518908 - 18 0.0000000000 - 19 -0.0502518908 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Beta - Occup= 0.0 - 1 0.0115095072 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.0507935668 - 5 0.0000000000 - 6 0.0392840596 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0623030739 - 12 0.0000000000 - 13 -0.0345285215 - 14 0.0000000000 - 15 0.0277745524 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0389249472 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0778498944 - 9 0.0000000000 - 10 0.0129749824 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0389249472 - 18 0.0000000000 - 19 0.0129749824 - 20 0.0000000000 - 21 -0.0259499648 - Sym= A - Ene= 1.19680365544524 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0145520114 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0308844111 - 8 0.0000000000 - 9 -0.0489971993 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0163323998 - 17 0.0000000000 - 18 -0.0472168109 - 19 0.0000000000 - 20 -0.0635492106 - 21 0.0000000000 - Sym= A - Ene= 1.19680365544524 - Spin= Beta - Occup= 0.0 - 1 0.0232579431 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0106590932 - 5 0.0000000000 - 6 -0.0339170364 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0125988499 - 12 0.0000000000 - 13 -0.0697738294 - 14 0.0000000000 - 15 -0.0571749795 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180885 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0233207590 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.2808328914 - 9 0.0000000000 - 10 -0.1402524818 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0325948994 - 18 0.0000000000 - 19 -0.1588007626 - 20 0.0000000000 - 21 0.0299053244 - Sym= A - Ene= 2.83316729180886 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0137282883 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.1108443923 - 8 0.0000000000 - 9 -0.4149029064 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0093496177 - 17 0.0000000000 - 18 -0.0173482151 - 19 0.0000000000 - 20 0.0778245920 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0111145448 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0995962533 - 5 0.0000000000 - 6 -0.2107417012 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.0660077713 - 12 0.0000000000 - 13 0.0972578682 - 14 0.0000000000 - 15 0.0891612059 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.1214724445 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.1883525196 - 8 0.0000000000 - 9 -0.1637771085 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0099850396 - 17 0.0000000000 - 18 0.0885021235 - 19 0.0000000000 - 20 -0.0169548770 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0305469553 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.2100268401 - 5 0.0000000000 - 6 -0.0954427127 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0863735908 - 12 0.0000000000 - 13 0.1118389753 - 14 0.0000000000 - 15 0.0290815271 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0929577710 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.3237016712 - 13 0.0000000000 - 14 0.2307439003 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0304794225 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.0804341551 - 8 0.0000000000 - 9 0.0584259304 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0295141439 - 17 0.0000000000 - 18 -0.2147072840 - 19 0.0000000000 - 20 0.0976159869 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.3201093000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.0795508588 - 13 0.0000000000 - 14 -0.2405584411 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180888 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0881138465 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0023654840 - 9 0.0000000000 - 10 -0.1770161878 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0781756088 - 18 0.0000000000 - 19 0.1555627230 - 20 0.0000000000 - 21 0.0021453465 - Sym= A - Ene= 2.83316729180889 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0869776300 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 -0.3514373969 - 9 0.0000000000 - 10 -0.0568094610 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0932693014 - 18 0.0000000000 - 19 -0.0693928039 - 20 0.0000000000 - 21 0.0126202265 - Sym= A - Ene= 2.83316729180892 - Spin= Beta - Occup= 0.0 - 1 0.0012455825 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0142167866 - 5 0.0000000000 - 6 -0.0266726118 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0636835364 - 12 0.0000000000 - 13 -0.4247515780 - 14 0.0000000000 - 15 0.0841282356 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 diff --git a/iodata/test/data/cfour/h_honly_sph.molden b/iodata/test/data/cfour/h_honly_sph.molden deleted file mode 100644 index a62c1db76..000000000 --- a/iodata/test/data/cfour/h_honly_sph.molden +++ /dev/null @@ -1,561 +0,0 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[Molden Format] -[GTO] - 1 0 -h 1 1.00 - 0.500000000000000 1.00000000000000 - - -[MO] - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0291411226 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.0949654964 - 5 0.0000000000 - 6 -0.1964457294 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0059045630 - 12 0.0000000000 - 13 0.2494691110 - 14 0.0000000000 - 15 0.0566446795 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0972158589 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.1718815421 - 8 0.0000000000 - 9 -0.0676505269 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0292521730 - 17 0.0000000000 - 18 -0.1206401875 - 19 0.0000000000 - 20 0.0715951816 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 -0.0594061431 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 -0.1229753644 - 9 0.0000000000 - 10 0.1598040744 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.1003979312 - 18 0.0000000000 - 19 -0.1598040744 - 20 0.0000000000 - 21 -0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 -0.0124171416 - 2 0.0000000000 - 3 0.0000000000 - 4 0.2121145552 - 5 0.0000000000 - 6 -0.0879431389 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.1134319277 - 12 0.0000000000 - 13 0.0442479007 - 14 0.0000000000 - 15 0.0365969194 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 -0.0625001810 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.1503191930 - 8 0.0000000000 - 9 -0.0759564931 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0025134182 - 17 0.0000000000 - 18 -0.1754533747 - 19 0.0000000000 - 20 0.1003861029 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0074034199 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0148920845 - 5 0.0000000000 - 6 -0.0889262837 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0545100888 - 12 0.0000000000 - 13 -0.3717367863 - 14 0.0000000000 - 15 0.1064192729 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0501579509 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0457567475 - 8 0.0000000000 - 9 -0.4382179478 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0140069327 - 17 0.0000000000 - 18 0.0943125797 - 19 0.0000000000 - 20 0.0258800348 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 -0.0928638139 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.4151831641 - 9 0.0000000000 - 10 0.0473332398 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0455305741 - 18 0.0000000000 - 19 -0.0473332398 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0609937546 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.1219875091 - 9 0.0000000000 - 10 -0.1626500122 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0609937546 - 18 0.0000000000 - 19 -0.1626500122 - 20 0.0000000000 - 21 0.0325300024 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.2886751346 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.2886751346 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 -0.1666666667 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.1666666667 - 13 0.0000000000 - 14 0.3333333333 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0291411226 - 2 0.0000000000 - 3 0.0000000000 - 4 -0.0949654964 - 5 0.0000000000 - 6 -0.1964457294 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0059045630 - 12 0.0000000000 - 13 0.2494691110 - 14 0.0000000000 - 15 0.0566446795 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0972158589 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 -0.1718815421 - 8 0.0000000000 - 9 -0.0676505269 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0292521730 - 17 0.0000000000 - 18 -0.1206401875 - 19 0.0000000000 - 20 0.0715951816 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 -0.0594061431 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 -0.1229753644 - 9 0.0000000000 - 10 0.1598040744 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.1003979312 - 18 0.0000000000 - 19 -0.1598040744 - 20 0.0000000000 - 21 -0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 -0.0124171416 - 2 0.0000000000 - 3 0.0000000000 - 4 0.2121145552 - 5 0.0000000000 - 6 -0.0879431389 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 -0.1134319277 - 12 0.0000000000 - 13 0.0442479007 - 14 0.0000000000 - 15 0.0365969194 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 -0.0625001810 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.1503191930 - 8 0.0000000000 - 9 -0.0759564931 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0025134182 - 17 0.0000000000 - 18 -0.1754533747 - 19 0.0000000000 - 20 0.1003861029 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0074034199 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0148920845 - 5 0.0000000000 - 6 -0.0889262837 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0545100888 - 12 0.0000000000 - 13 -0.3717367863 - 14 0.0000000000 - 15 0.1064192729 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0501579509 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0457567475 - 8 0.0000000000 - 9 -0.4382179478 - 10 0.0000000000 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 -0.0140069327 - 17 0.0000000000 - 18 0.0943125797 - 19 0.0000000000 - 20 0.0258800348 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 -0.0928638139 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.4151831641 - 9 0.0000000000 - 10 0.0473332398 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 -0.0455305741 - 18 0.0000000000 - 19 -0.0473332398 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0609937546 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - 7 0.0000000000 - 8 0.1219875091 - 9 0.0000000000 - 10 -0.1626500122 - 11 0.0000000000 - 12 0.0000000000 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0609937546 - 18 0.0000000000 - 19 -0.1626500122 - 20 0.0000000000 - 21 0.0325300024 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.2886751346 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.2886751346 - 13 0.0000000000 - 14 0.0000000000 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 - Sym= A - Ene= 2.83316729180887 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 -0.1666666667 - 6 0.0000000000 - 7 0.0000000000 - 8 0.0000000000 - 9 0.0000000000 - 10 0.0000000000 - 11 0.0000000000 - 12 -0.1666666667 - 13 0.0000000000 - 14 0.3333333333 - 15 0.0000000000 - 16 0.0000000000 - 17 0.0000000000 - 18 0.0000000000 - 19 0.0000000000 - 20 0.0000000000 - 21 0.0000000000 diff --git a/iodata/test/data/cfour/h_donly_cart.molden b/iodata/test/data/h_donly_cart_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_donly_cart.molden rename to iodata/test/data/h_donly_cart_cfour.molden diff --git a/iodata/test/data/h_donly_sph.molden b/iodata/test/data/h_donly_sph.molden deleted file mode 100644 index ab982c6c1..000000000 --- a/iodata/test/data/h_donly_sph.molden +++ /dev/null @@ -1,110 +0,0 @@ -[Molden Format] -[ATOMS] AU -H 1 1 0.0000000000 0.0000000000 0.0000000000 -[GTO] - 1 0 -d 1 1.00 - 0.500000000000000 1.00000000000000 - - -[MO] - Sym= A - Ene= 1.14819777754906 - Spin= Alpha - Occup= 0.0 - 1 -0.2886751346 - 2 -0.2886751346 - 3 0.5773502692 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Alpha - Occup= 0.0 - 1 0.5000000000 - 2 -0.5000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 1.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 1.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Alpha - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 1.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Beta - Occup= 0.0 - 1 -0.2886751346 - 2 -0.2886751346 - 3 0.5773502692 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Beta - Occup= 0.0 - 1 0.5000000000 - 2 -0.5000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 1.0000000000 - 5 0.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 1.0000000000 - 6 0.0000000000 - Sym= A - Ene= 1.14819777754906 - Spin= Beta - Occup= 0.0 - 1 0.0000000000 - 2 0.0000000000 - 3 0.0000000000 - 4 0.0000000000 - 5 0.0000000000 - 6 1.0000000000 diff --git a/iodata/test/data/cfour/h_donly_sph.molden b/iodata/test/data/h_donly_sph_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_donly_sph.molden rename to iodata/test/data/h_donly_sph_cfour.molden diff --git a/iodata/test/data/cfour/h_fonly_cart.molden b/iodata/test/data/h_fonly_cart_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_fonly_cart.molden rename to iodata/test/data/h_fonly_cart_cfour.molden diff --git a/iodata/test/data/cfour/h_fonly_sph.molden b/iodata/test/data/h_fonly_sph_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_fonly_sph.molden rename to iodata/test/data/h_fonly_sph_cfour.molden diff --git a/iodata/test/data/cfour/h_gonly_cart.molden b/iodata/test/data/h_gonly_cart_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_gonly_cart.molden rename to iodata/test/data/h_gonly_cart_cfour.molden diff --git a/iodata/test/data/cfour/h_gonly_sph.molden b/iodata/test/data/h_gonly_sph_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_gonly_sph.molden rename to iodata/test/data/h_gonly_sph_cfour.molden diff --git a/iodata/test/data/cfour/h_ponly_cart.molden b/iodata/test/data/h_ponly_cart_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_ponly_cart.molden rename to iodata/test/data/h_ponly_cart_cfour.molden diff --git a/iodata/test/data/cfour/h_ponly_sph.molden b/iodata/test/data/h_ponly_sph_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_ponly_sph.molden rename to iodata/test/data/h_ponly_sph_cfour.molden diff --git a/iodata/test/data/cfour/h_sonly_cart.molden b/iodata/test/data/h_sonly_cart_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_sonly_cart.molden rename to iodata/test/data/h_sonly_cart_cfour.molden diff --git a/iodata/test/data/cfour/h_sonly_sph.molden b/iodata/test/data/h_sonly_sph_cfour.molden similarity index 100% rename from iodata/test/data/cfour/h_sonly_sph.molden rename to iodata/test/data/h_sonly_sph_cfour.molden diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index f1df12668..384437f7d 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -207,6 +207,29 @@ def test_load_molden_nh3_molden_cart(): molden_charges = np.array([0.3138, -0.4300, -0.0667, 0.1829]) assert_allclose(charges, molden_charges, atol=1.e-3) +def test_load_molden_h_cfour(): + # The file tested here is created with CFOUR 2.1. + file_list = [ + 'h_sonly_sph.molden', + 'h_ponly_sph.molden', + 'h_donly_sph.molden', + 'h_fonly_sph.molden', + 'h_gonly_sph.molden', + 'h_sonly_cart.molden', + 'h_ponly_cart.molden', + 'h_donly_cart.molden', + 'h_fonly_cart.molden', + 'h_gonly_cart.molden', + ] + + for i in file_list: + with path('iodata.test.data', i) as fn_molden: + print(str(fn_molden)) + mol = load_one(str(fn_molden)) + # Check normalization + olp = compute_overlap(mol.obasis, mol.atcoords) + check_orthonormal(mol.mo.coeffsa, olp) + check_orthonormal(mol.mo.coeffsb, olp) def test_load_molden_nh3_orca(): # The file tested here is created with ORCA. It should be read in diff --git a/iodata/test/test_molden_cfour_TOMERGE.py b/iodata/test/test_molden_cfour_TOMERGE.py deleted file mode 100644 index e01f46622..000000000 --- a/iodata/test/test_molden_cfour_TOMERGE.py +++ /dev/null @@ -1,71 +0,0 @@ -# IODATA is an input and output module for quantum chemistry. -# Copyright (C) 2011-2019 The IODATA Development Team -# -# This file is part of IODATA. -# -# IODATA is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 3 -# of the License, or (at your option) any later version. -# -# IODATA is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see -# -- -# pylint: disable=unsubscriptable-object -"""Test iodata.formats.molden module.""" - -import os - -import attr -import numpy as np -from numpy.testing import assert_allclose, assert_equal -import pytest - -from .common import compute_mulliken_charges, compare_mols, check_orthonormal -from ..api import load_one, dump_one -from ..basis import convert_conventions -from ..formats.molden import _load_low -from ..overlap import compute_overlap, OVERLAP_CONVENTIONS -from ..utils import LineIterator, angstrom, FileFormatWarning -import sys - -try: - from importlib_resources import path -except ImportError: - from importlib.resources import path - -def test_load_molden_h_cfour(): - # The file tested here is created with CFOUR 2.1. - file_list = [ - 'h_sonly_sph.molden', - 'h_ponly_sph.molden', - 'h_donly_sph.molden', - 'h_fonly_sph.molden', - 'h_gonly_sph.molden', - 'h_honly_sph.molden', - 'h_sonly_cart.molden', - 'h_ponly_cart.molden', - 'h_donly_cart.molden', - 'h_fonly_cart.molden', - 'h_gonly_cart.molden', - 'h_honly_cart.molden' - ] - - for i in file_list: - with path('iodata.test.data.cfour', i) as fn_molden: - print(str(fn_molden)) - mol = load_one(str(fn_molden)) - # Check normalization - olp = compute_overlap(mol.obasis, mol.atcoords) - print(olp) - #assert_allclose(olp, np.eye(olp.shape[0])) - np.set_printoptions(threshold=sys.maxsize) - np.set_printoptions(linewidth=np.inf) - print(mol.mo.coeffsa.T @ olp @ mol.mo.coeffsa) - check_orthonormal(mol.mo.coeffsa, olp) - check_orthonormal(mol.mo.coeffsb, olp) From 7044872f3bd2a2a5e05a2e9f983b8e7553502d1d Mon Sep 17 00:00:00 2001 From: amandadumi Date: Sun, 11 Jul 2021 16:30:30 -0400 Subject: [PATCH 061/144] fix linting --- iodata/formats/molden.py | 48 +++++++++++++++++++------------------- iodata/test/test_molden.py | 23 +++++++++--------- 2 files changed, 36 insertions(+), 35 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 34f557bfc..71019f1bf 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -547,6 +547,7 @@ def _fix_mo_coeffs_psi4(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return np.concatenate(correction) return None + def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: """Return correction values for the MO coefficients. @@ -554,7 +555,6 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: AO basis functions. The coefficients need to be divided by the returned correction factor. """ - correction = [] corrected = False for shell in obasis.shells: @@ -565,11 +565,12 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: factors = None if kind == "c": if angmom == 2: - factors = np.array([1.0/np.sqrt(3.0)] * 3 + [1.0] * 3) + factors = np.array([1.0 / np.sqrt(3.0)] * 3 + [1.0] * 3) elif angmom == 3: - factors = np.array([1.0/np.sqrt(15.0)] * 3 + [1.0/(np.sqrt(3.0))] * 6 + [1.0]) + 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: @@ -581,8 +582,6 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return None - - def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): """Detect errors in the data loaded from a molden or mkl file and correct. @@ -644,23 +643,25 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): result['obasis'] = cfour_obasis return else: - cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) - if cfour_coeff_correction is not None: - coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] - if coeffsb is None: - coeffsb_cfour = None - else: - coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] - if _is_normalized_properly(cfour_obasis, atcoords, coeffsa_cfour, coeffsb_cfour) or True: - lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') - result['obasis'] = cfour_obasis - if result['mo'].kind == 'restricted': - result['mo'].coeffs[:] = coeffsa_cfour - else: - result['mo'].coeffsa[:] = coeffsa_cfour - result['mo'].coeffsb[:] = coeffsb_cfour - return - #TODO REMOVE + cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) + if cfour_coeff_correction is not None: + coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] + if coeffsb is None: + coeffsb_cfour = None + else: + coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] + if _is_normalized_properly(cfour_obasis, + atcoords, + coeffsa_cfour, + coeffsb_cfour) or True: + lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') + result['obasis'] = cfour_obasis + if result['mo'].kind == 'restricted': + result['mo'].coeffs[:] = coeffsa_cfour + else: + result['mo'].coeffsa[:] = coeffsa_cfour + result['mo'].coeffsb[:] = coeffsb_cfour + return result['obasis'] = cfour_obasis return # --- Renormalized contractions @@ -688,7 +689,6 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): 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 ' diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 384437f7d..8bb1be2f0 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -207,20 +207,20 @@ def test_load_molden_nh3_molden_cart(): molden_charges = np.array([0.3138, -0.4300, -0.0667, 0.1829]) assert_allclose(charges, molden_charges, atol=1.e-3) + def test_load_molden_h_cfour(): # The file tested here is created with CFOUR 2.1. file_list = [ - 'h_sonly_sph.molden', - 'h_ponly_sph.molden', - 'h_donly_sph.molden', - 'h_fonly_sph.molden', - 'h_gonly_sph.molden', - 'h_sonly_cart.molden', - 'h_ponly_cart.molden', - 'h_donly_cart.molden', - 'h_fonly_cart.molden', - 'h_gonly_cart.molden', - ] + 'h_sonly_sph.molden', + 'h_ponly_sph.molden', + 'h_donly_sph.molden', + 'h_fonly_sph.molden', + 'h_gonly_sph.molden', + 'h_sonly_cart.molden', + 'h_ponly_cart.molden', + 'h_donly_cart.molden', + 'h_fonly_cart.molden', + 'h_gonly_cart.molden'] for i in file_list: with path('iodata.test.data', i) as fn_molden: @@ -231,6 +231,7 @@ def test_load_molden_h_cfour(): check_orthonormal(mol.mo.coeffsa, olp) check_orthonormal(mol.mo.coeffsb, olp) + def test_load_molden_nh3_orca(): # The file tested here is created with ORCA. It should be read in # properly by altering normalization and sign conventions. From dfa6e83794d8c7ecafb2b2d9308171f90602c428 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 12 Jul 2021 08:10:20 +0200 Subject: [PATCH 062/144] Fix a few pylint and pycodestyle issues - Two test functions had the same name, the former was just a subset of the latter and hence removed. - Pylint complained about unreachable code and few other issues. These seemed to be caused by development stubs, which I've just removed. --- iodata/formats/molden.py | 41 +++++++++++++++++++------------------- iodata/test/test_molden.py | 8 -------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 71019f1bf..7b1b43a8a 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -596,6 +596,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): The line iterator to read the data from, used for warnings. """ + # pylint: disable=too-many-return-statements obasis = result['obasis'] atcoords = result['atcoords'] if result['mo'].kind == 'restricted': @@ -642,28 +643,28 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): lit.warn('Corrected for CFOUR errors in Molden/MKL file.') result['obasis'] = cfour_obasis return - else: - cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) - if cfour_coeff_correction is not None: - coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] - if coeffsb is None: - coeffsb_cfour = None + + cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) + if cfour_coeff_correction is not None: + coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] + if coeffsb is None: + coeffsb_cfour = None + else: + coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] + if _is_normalized_properly(cfour_obasis, + atcoords, + coeffsa_cfour, + coeffsb_cfour): + lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') + result['obasis'] = cfour_obasis + if result['mo'].kind == 'restricted': + result['mo'].coeffs[:] = coeffsa_cfour else: - coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] - if _is_normalized_properly(cfour_obasis, - atcoords, - coeffsa_cfour, - coeffsb_cfour) or True: - lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') - result['obasis'] = cfour_obasis - if result['mo'].kind == 'restricted': - result['mo'].coeffs[:] = coeffsa_cfour - else: - result['mo'].coeffsa[:] = coeffsa_cfour - result['mo'].coeffsb[:] = coeffsb_cfour - return + result['mo'].coeffsa[:] = coeffsa_cfour + result['mo'].coeffsb[:] = coeffsb_cfour + return result['obasis'] = cfour_obasis - return + # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb): diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 8bb1be2f0..8c94fc0d4 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -39,14 +39,6 @@ except ImportError: from importlib.resources import path -def test_load_molden_h_cfour(): - # The file tested here is created with CFOUR 2.1. - with path('iodata.test.data', 'h_donly_sph.molden') as fn_molden: - mol = load_one(str(fn_molden)) - - # Check normalization - olp = compute_overlap(mol.obasis, mol.atcoords) - check_orthonormal(mol.mo.coeffs, olp) def test_load_molden_li2_orca(): with path('iodata.test.data', 'li2.molden.input') as fn_molden: From f782c4717325daa30b03b5f848780ac53bfd12ff Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 12 Jul 2021 13:22:26 -0400 Subject: [PATCH 063/144] chainging file names for test --- iodata/test/test_molden.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 8c94fc0d4..a01bb150c 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -203,16 +203,16 @@ def test_load_molden_nh3_molden_cart(): def test_load_molden_h_cfour(): # The file tested here is created with CFOUR 2.1. file_list = [ - 'h_sonly_sph.molden', - 'h_ponly_sph.molden', - 'h_donly_sph.molden', - 'h_fonly_sph.molden', - 'h_gonly_sph.molden', - 'h_sonly_cart.molden', - 'h_ponly_cart.molden', - 'h_donly_cart.molden', - 'h_fonly_cart.molden', - 'h_gonly_cart.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'] for i in file_list: with path('iodata.test.data', i) as fn_molden: From 96c693212d36a3ce776abfe099a0f019c7a72639 Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Mon, 9 Aug 2021 16:54:55 -0400 Subject: [PATCH 064/144] Add code to save Type 6/7 charges from fchk --- iodata/formats/fchk.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 0a6dd70a9..6afe7678b 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -85,6 +85,7 @@ def load_one(lit: LineIterator) -> dict: 'Total CC Density', 'Spin CC Density', 'Total CI Density', 'Spin CI Density', 'Mulliken Charges', 'ESP Charges', 'NPA Charges', + 'Type 6 Charges', 'Type 7 Charges', 'Polarizability', 'Dipole Moment', 'Quadrupole Moment', 'Cartesian Gradient', 'Cartesian Force Constants', 'MicOpt', ]) @@ -222,6 +223,10 @@ def load_one(lit: LineIterator) -> dict: atcharges['esp'] = fchk['ESP Charges'] if 'NPA Charges' in fchk: atcharges['npa'] = fchk['NPA 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 From 1bf1cfa47b02f8b2ebc770a2eb9942caa0f9d641 Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Tue, 10 Aug 2021 10:31:22 -0400 Subject: [PATCH 065/144] Add code to save MBS charges in fchk --- iodata/formats/fchk.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 6afe7678b..8fabcf322 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -85,7 +85,7 @@ def load_one(lit: LineIterator) -> dict: 'Total CC Density', 'Spin CC Density', 'Total CI Density', 'Spin CI Density', 'Mulliken Charges', 'ESP Charges', 'NPA Charges', - 'Type 6 Charges', 'Type 7 Charges', + 'MBS Charges', 'Type 6 Charges', 'Type 7 Charges', 'Polarizability', 'Dipole Moment', 'Quadrupole Moment', 'Cartesian Gradient', 'Cartesian Force Constants', 'MicOpt', ]) @@ -223,6 +223,8 @@ def load_one(lit: LineIterator) -> dict: 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: From f12b679dae1364d22881edbdc9060c927004cdee Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Tue, 10 Aug 2021 11:04:16 -0400 Subject: [PATCH 066/144] Add new test & fchk file to check new atcharges --- iodata/test/data/water_atcharges.fchk | 1148 +++++++++++++++++++++++++ iodata/test/test_fchk.py | 12 + 2 files changed, 1160 insertions(+) create mode 100644 iodata/test/data/water_atcharges.fchk diff --git a/iodata/test/data/water_atcharges.fchk b/iodata/test/data/water_atcharges.fchk new file mode 100644 index 000000000..fc7cdbba0 --- /dev/null +++ b/iodata/test/data/water_atcharges.fchk @@ -0,0 +1,1148 @@ +water atcharges +SP RHF 6-31G +Number of atoms I 3 +Info1-9 I N= 9 + 15 12 0 0 0 110 + 1 1 2 +Full Title C N= 2 +water atcharges +Route C N= 10 +#p hf/6-31G sp scf=(maxcycle=900,verytightlineq,xqc) integra +l=grid=ultrafine symmetry=None pop=(cm5,hlygat,mbs,npa) +Charge I 0 +Multiplicity I 1 +Number of electrons I 10 +Number of alpha electrons I 5 +Number of beta electrons I 5 +Number of basis functions I 13 +Number of independent functions I 13 +Number of point charges in /Mol/ I 0 +Number of translation vectors I 0 +Atomic numbers I N= 3 + 8 1 1 +Nuclear charges R N= 3 + 8.00000000E+00 1.00000000E+00 1.00000000E+00 +Current cartesian coordinates R N= 9 + -5.53874655E+00 -4.08958344E-01 0.00000000E+00 -6.90736427E+00 2.72294764E+00 + 0.00000000E+00 -2.14148544E+00 1.45385191E-01 0.00000000E+00 +Number of symbols in /Mol/ I 0 +Int Atom Types I N= 3 + 0 0 0 +Force Field I 0 +Atom Types C N= 3 + +MM charges R N= 3 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 +Integer atomic weights I N= 3 + 16 1 1 +Real atomic weights R N= 3 + 1.59949146E+01 1.00782504E+00 1.00782504E+00 +Atom fragment info I N= 3 + 0 0 0 +Atom residue num I N= 3 + 0 0 0 +Nuclear spins I N= 3 + 0 1 1 +Nuclear ZEff R N= 3 + -5.60000000E+00 -1.00000000E+00 -1.00000000E+00 +Nuclear ZNuc R N= 3 + 8.00000000E+00 1.00000000E+00 1.00000000E+00 +Nuclear QMom R N= 3 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 +Nuclear GFac R N= 3 + 0.00000000E+00 2.79284600E+00 2.79284600E+00 +MicOpt I N= 3 + -1 -1 -1 +Number of residues I 0 +Number of secondary structures I 0 +Number of contracted shells I 7 +Number of primitive shells I 18 +Pure/Cartesian d shells I 1 +Pure/Cartesian f shells I 0 +Highest angular momentum I 1 +Largest degree of contraction I 6 +Shell types I N= 7 + 0 -1 -1 0 0 0 + 0 +Number of primitives per shell I N= 7 + 6 3 1 3 1 3 + 1 +Shell to atom map I N= 7 + 1 1 1 2 2 3 + 3 +Primitive exponents R N= 18 + 5.48467166E+03 8.25234946E+02 1.88046958E+02 5.29645000E+01 1.68975704E+01 + 5.79963534E+00 1.55396162E+01 3.59993359E+00 1.01376175E+00 2.70005823E-01 + 1.87311370E+01 2.82539436E+00 6.40121692E-01 1.61277759E-01 1.87311370E+01 + 2.82539436E+00 6.40121692E-01 1.61277759E-01 +Contraction coefficients R N= 18 + 1.83107443E-03 1.39501722E-02 6.84450781E-02 2.32714336E-01 4.70192898E-01 + 3.58520853E-01 -1.10777550E-01 -1.48026263E-01 1.13076702E+00 1.00000000E+00 + 3.34946043E-02 2.34726953E-01 8.13757326E-01 1.00000000E+00 3.34946043E-02 + 2.34726953E-01 8.13757326E-01 1.00000000E+00 +P(S=P) Contraction coefficients R N= 18 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 7.08742682E-02 3.39752839E-01 7.27158577E-01 1.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 +Coordinates of each shell R N= 21 + -5.53874655E+00 -4.08958344E-01 0.00000000E+00 -5.53874655E+00 -4.08958344E-01 + 0.00000000E+00 -5.53874655E+00 -4.08958344E-01 0.00000000E+00 -6.90736427E+00 + 2.72294764E+00 0.00000000E+00 -6.90736427E+00 2.72294764E+00 0.00000000E+00 + -2.14148544E+00 1.45385191E-01 0.00000000E+00 -2.14148544E+00 1.45385191E-01 + 0.00000000E+00 +Num ILSW I 100 +ILSW I N= 100 + 0 1 0 0 2 0 + 0 0 0 0 0 -1 + 5 0 0 0 0 0 + 0 0 0 0 0 0 + 1 1 0 0 0 0 + 0 0 100000 0 -1 0 + 0 0 0 0 0 0 + 0 0 0 1 0 0 + 0 -2000000000 1 0 0 0 + 0 0 4 52 0 0 + 0 0 0 0 0 0 + 0 0 0 3 0 1 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 +Num RLSW I 52 +RLSW R N= 52 + 1.00000000E+00 1.00000000E+00 1.00000000E+00 1.00000000E+00 1.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 1.00000000E+00 0.00000000E+00 0.00000000E+00 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 1.00000000E+00 1.00000000E+00 + 0.00000000E+00 1.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 +MxBond I 1 +NBond I N= 3 + 0 0 0 +IBond I N= 3 + 0 0 0 +RBond R N= 3 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 +Virial Ratio R 2.011194871414563E+00 +SCF Energy R -7.562730180595092E+01 +Total Energy R -7.562730180595092E+01 +RMS Density R 4.182529560118482E-09 +Job Status I 1 +Nuclear derivative order I 0 +External E-field R N= 35 + 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 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 +IOpCl I 0 +IROHF I 0 +Alpha Orbital Energies R N= 13 + -2.06699649E+01 -1.20134704E+00 -5.08560476E-01 -4.46812170E-01 -4.34275776E-01 + -7.79428952E-03 3.03563420E-02 9.37600604E-01 1.00976386E+00 1.15720781E+00 + 1.22944126E+00 1.28872667E+00 1.38501542E+00 +Alpha MO coefficients R N= 169 + 9.96015699E-01 1.97756823E-02 4.45969104E-04 8.36409875E-04 7.14964859E-17 + -4.72958689E-03 -3.06820464E-04 -5.80447063E-04 -6.57624568E-17 7.74465004E-06 + 6.75326932E-04 4.70239835E-10 6.69012740E-04 -2.30016974E-01 5.35834473E-01 + 1.12353289E-04 7.84789802E-04 1.97893033E-16 5.39733498E-01 6.44372972E-03 + 1.25402167E-02 -2.54224632E-16 2.62664472E-02 1.95743996E-02 2.56661381E-02 + 1.94517716E-02 3.48674314E-19 1.18137700E-16 1.31886706E-15 -1.10358808E-15 + 6.77692819E-01 -2.45261670E-16 1.30213650E-15 -8.52938185E-16 4.70214877E-01 + -1.08712726E-15 -5.34678758E-16 8.77260361E-16 1.13965795E-15 -2.81100311E-03 + 8.57295122E-03 3.70624851E-01 -2.44604897E-01 -2.48981384E-15 9.51362478E-03 + 2.97092303E-01 -1.95618146E-01 -1.79348647E-15 -1.73132724E-01 -3.05519875E-01 + 1.48891876E-01 2.63523997E-01 3.14296588E-02 -9.76655672E-02 2.54816160E-01 + 3.95245878E-01 -1.28486806E-16 -1.02817252E-01 2.04696554E-01 3.16681162E-01 + 1.06224400E-16 1.35855622E-01 2.49416996E-01 1.61469046E-01 2.96418183E-01 + 3.90568833E-02 -8.22061201E-02 -2.06254042E-01 -3.51088724E-01 -5.59191838E-16 + -2.13903718E-01 -2.13747934E-01 -3.64686214E-01 -6.08424725E-17 1.68333215E-01 + 4.86869652E-01 1.79833513E-01 5.15538611E-01 1.51445643E-03 -2.98226064E-03 + 3.76576410E-01 -2.18879464E-01 1.29731030E-16 -9.61276733E-03 4.09259271E-01 + -2.38288888E-01 1.84533178E-17 1.87190804E-01 5.37675923E-01 -1.77017320E-01 + -5.03247416E-01 -2.28371456E-02 6.70684468E-02 -9.03858512E-02 -1.79940119E-01 + -6.33823120E-16 2.12189988E-01 1.46761802E-01 2.86870500E-01 3.82258185E-16 + 8.97142154E-01 -8.33471204E-01 8.78500330E-01 -8.10591338E-01 6.43951581E-04 + -6.15938965E-03 1.63664400E-01 -9.10921782E-02 -6.93876788E-16 5.96794350E-03 + -3.46878413E-01 1.89265277E-01 1.65638588E-16 9.03295258E-01 -9.33369845E-01 + -9.25147732E-01 9.48497595E-01 -5.21383600E-16 5.55509380E-15 -1.66136467E-15 + -8.43262857E-15 -9.36364692E-01 -5.91378960E-15 1.61376138E-15 9.12598190E-15 + 1.05590926E+00 -3.48135129E-15 1.67091546E-15 -1.57131405E-15 1.41357615E-15 + -3.41743883E-02 4.84988283E-01 -4.40757022E-01 -7.79755332E-01 9.25559241E-15 + -5.07868015E-01 4.69006563E-01 8.28600622E-01 -1.03133184E-14 -2.24907289E-01 + 1.08716461E-01 -2.24701625E-01 1.01803904E-01 1.65689286E-03 -2.40848889E-02 + -8.53488005E-01 4.72885165E-01 -2.79523806E-15 2.59332218E-02 9.66548313E-01 + -5.34689704E-01 3.13495109E-15 2.05772667E-01 7.19352746E-02 -1.88941414E-01 + -8.82426867E-02 9.49816608E-02 -1.44719384E+00 -1.60922315E-01 -3.19778127E-01 + -8.17682253E-16 1.56617574E+00 2.12659925E-01 4.19991531E-01 6.90367110E-16 + 3.45750724E-03 -3.09023763E-01 1.93143995E-02 -3.12169776E-01 +Orthonormal basis R N= 169 + 1.04260234E-01 2.75963065E-01 4.35651620E-02 8.66407424E-02 0.00000000E+00 + 3.31353614E-01 8.02535204E-02 1.59227493E-01 -2.88926248E-20 1.89342161E-01 + 2.67949531E-01 1.81948958E-01 2.59304183E-01 2.11072341E-03 4.88724237E-03 + 2.17902006E-01 -1.17214481E-01 0.00000000E+00 5.30268422E-03 3.45344586E-01 + -1.85592879E-01 -5.13956619E-17 -2.53743474E-01 -2.78150454E-01 2.59726568E-01 + 2.87271879E-01 1.97249788E-01 3.69942871E-01 -1.44335856E-01 -2.66534177E-01 + 1.60634975E-17 3.12978802E-01 -1.79658852E-01 -3.31527443E-01 1.19792514E-17 + -1.62436229E-01 -1.30162592E-01 -1.63722712E-01 -1.32481677E-01 -1.58763949E-17 + -6.94150601E-17 3.05375578E-17 -1.10194236E-17 5.77057836E-01 9.27694992E-17 + -5.93497381E-17 -1.47181958E-17 5.77057836E-01 1.19118789E-16 -2.21144309E-16 + 2.55899080E-16 -5.33940871E-17 2.03311961E-01 2.10097041E-01 2.84200955E-01 + 4.65765566E-01 2.45976908E-17 8.46966304E-02 1.83342339E-01 3.01095760E-01 + 6.13307668E-17 -2.86762358E-01 -2.00741317E-01 -3.15904695E-01 -2.20088082E-01 + -1.25032428E-02 -1.10307309E-02 5.37665021E-01 -3.21720825E-01 4.40231864E-17 + -3.58223852E-03 2.95658637E-01 -1.77242029E-01 1.12047659E-16 3.44881773E-01 + 2.21758159E-01 -3.17149187E-01 -2.02604572E-01 9.71800394E-01 -2.17341261E-01 + -3.78048830E-02 -7.51265123E-02 0.00000000E+00 -2.77185090E-01 3.81911994E-03 + 7.25364361E-03 -1.59958040E-17 1.31986726E-01 1.69774960E-02 1.29155019E-01 + 1.60407568E-02 -9.70776961E-17 5.23855301E-16 -1.11286050E-16 -2.60863873E-15 + 1.00152416E+00 -3.15595220E-16 2.25089621E-16 2.69807273E-15 -1.00152416E+00 + -8.75756371E-16 8.64214258E-17 3.13216814E-16 -6.25999499E-16 -2.89947907E-03 + 1.64935354E-02 -5.04732464E-01 -7.87738445E-01 -2.21803263E-15 -3.75937845E-02 + 5.21080358E-01 8.15512671E-01 2.51407697E-15 -3.27813406E-01 1.02307038E-01 + -3.92443787E-01 1.26633006E-01 1.24290648E-03 -5.05048024E-03 -7.50387590E-01 + 4.86960623E-01 1.52372994E-15 4.77554478E-03 7.84856645E-01 -5.11145807E-01 + -1.76798078E-15 4.88337070E-01 -1.68339811E-01 -4.34724015E-01 1.55509287E-01 + -1.28724269E-01 7.46168483E-01 -1.13523501E-01 -2.02667889E-01 -1.12387954E-15 + -3.95639500E-01 2.02406549E-01 3.66009079E-01 6.68752845E-16 6.81758833E-01 + -6.95554565E-01 6.78157755E-01 -6.92304252E-01 -7.95988645E-04 7.74696534E-03 + 3.05325027E-01 -1.64821252E-01 5.30803354E-16 -5.06217065E-03 -6.68561707E-01 + 3.62368449E-01 -3.97573002E-16 7.33723822E-01 -1.04230583E+00 -7.41958805E-01 + 1.04863611E+00 7.70837027E-02 -1.33301405E+00 -6.90107288E-02 -1.32369695E-01 + -5.36193491E-16 1.62930154E+00 1.73684949E-01 3.34709679E-01 -3.94116091E-17 + 3.63239995E-01 -7.03948810E-01 3.59126930E-01 -6.87626692E-01 +Total SCF Density R N= 91 + 2.09190161E+00 -2.13295657E-01 5.94243438E-01 1.47706128E-02 -4.32807877E-02 + 4.04588534E-01 2.75251812E-02 -8.05236815E-02 2.01176890E-02 4.32104351E-01 + 5.77794756E-17 3.57433111E-16 -1.23373093E-16 -3.78883674E-16 9.18535113E-01 + -2.64233726E-01 5.98475098E-01 -4.52299600E-02 -8.50911058E-02 -1.40434434E-16 + 6.03993028E-01 7.62130052E-03 -2.79962846E-02 3.24540735E-01 1.64800755E-02 + 2.35392908E-16 -2.94811293E-02 2.60412263E-01 1.40809340E-02 -5.17957369E-02 + 1.63913626E-02 3.46050873E-01 -2.58453090E-16 -5.53004084E-02 1.35757622E-02 + 2.77422022E-01 3.03917166E-18 -2.15445308E-16 -3.50998811E-17 -7.69954484E-17 + 6.37322491E-01 -5.60424930E-16 1.99157444E-16 -3.94726414E-17 4.42204062E-01 + -2.55488494E-03 -1.35610734E-03 -5.90922552E-02 1.92132213E-01 -6.35854478E-16 + -2.87715334E-03 -4.69159407E-02 1.54440402E-01 -3.85838238E-16 9.82432325E-02 + 9.73620090E-03 -3.29533324E-02 -9.93505536E-02 3.46657248E-01 7.40429575E-16 + -3.59784129E-02 -7.91737572E-02 2.77991942E-01 6.36010641E-16 1.74588689E-01 + 3.11869690E-01 -2.49453095E-03 -1.48144307E-03 1.92661670E-01 5.48408709E-02 + 4.16265003E-16 -2.66485530E-03 1.54904346E-01 4.46602225E-02 3.12184594E-16 + -6.33484018E-03 -9.42780734E-03 9.77995879E-02 9.53533019E-03 -3.25090231E-02 + 3.46406338E-01 1.05429260E-01 1.64047380E-16 -3.49484520E-02 2.78183737E-01 + 8.51270396E-02 1.79510259E-16 -9.68723357E-03 -1.23977551E-02 1.75196390E-01 + 3.15374910E-01 +Mulliken Charges R N= 3 + -3.91150532E-01 1.96895396E-01 1.94255137E-01 +ESP Charges R N= 3 + -4.47363368E-01 2.24922518E-01 2.22440849E-01 +NPA Charges R N= 3 + -4.98161654E-01 2.50757174E-01 2.47404480E-01 +MBS Charges R N= 3 + -2.90505882E-01 1.45850946E-01 1.44654936E-01 +Type 6 Charges R N= 3 + -3.37450356E-01 1.68988978E-01 1.68461239E-01 +Type 7 Charges R N= 3 + -3.77750403E-01 1.89459551E-01 1.88290713E-01 +ONIOM Charges I N= 16 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 +ONIOM Multiplicities I N= 16 + 1 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 +Atom Layers I N= 3 + 1 1 1 +Atom Modifiers I N= 3 + 0 0 0 +Int Atom Modified Types I N= 3 + 0 0 0 +Force Field I 0 +Atom Modified Types C N= 3 + +Link Atoms I N= 3 + 0 0 0 +Atom Modified MM Charges R N= 3 + 0.00000000E+00 0.00000000E+00 0.00000000E+00 +Link Distances R N= 12 + 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 +Cartesian Gradient R N= 9 + 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 +Nonadiabatic coupling R N= 9 + 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 +Dipole Moment R N= 3 + 4.29742853E-01 7.93714042E-01 2.89424748E-16 +Quadrupole Moment R N= 6 + -1.32532272E+00 1.87449664E+00 -5.49173918E-01 -5.26004180E+00 -2.02510843E-15 + -3.22171000E-16 +QEq coupling tensors R N= 18 + -2.01609156E+00 2.29777639E-01 -1.70195699E+00 0.00000000E+00 0.00000000E+00 + 3.71804855E+00 -9.05517239E-03 -1.81615845E-02 2.21935293E-02 0.00000000E+00 + 0.00000000E+00 -1.31383569E-02 2.99640433E-02 6.95355685E-03 -1.65413888E-02 + 0.00000000E+00 0.00000000E+00 -1.34226545E-02 +ClPar MaxAn I 200 +ClPar NIntPar I 4 +ClPar NRealPar I 16 +ClPar NBits I 30 +ClPar LenBitVec I 1 +ClPar Read BitMap I N= 200 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 +ClPar Default BitMap I N= 200 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 2016 2016 2016 2016 + 2016 2016 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 480 480 480 480 + 480 480 +ClPar Int Params I N= 800 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 0 0 0 0 + 0 0 +ClPar Real Params R N= 3200 + 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 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 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 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 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 + 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 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 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 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 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 + 2.26000000E-01 4.43000000E-01 1.01000000E-01 1.47000000E-01 1.92000000E-01 + 2.40000000E-01 2.75000000E-01 3.03000000E-01 3.25000000E-01 4.13000000E-01 + 9.60000000E-02 1.35000000E-01 1.73000000E-01 2.11000000E-01 2.48000000E-01 + 2.86000000E-01 3.23000000E-01 3.60000000E-01 8.20000000E-02 1.04000000E-01 + 1.17000000E-01 1.23000000E-01 1.29000000E-01 1.34000000E-01 1.40000000E-01 + 1.44000000E-01 1.48000000E-01 1.52000000E-01 1.55000000E-01 1.76000000E-01 + 1.98000000E-01 2.19000000E-01 2.39000000E-01 2.60000000E-01 2.80000000E-01 + 3.00000000E-01 7.90000000E-02 9.90000000E-02 1.19000000E-01 1.26000000E-01 + 1.33000000E-01 1.40000000E-01 1.47000000E-01 1.53000000E-01 1.58000000E-01 + 1.63000000E-01 1.57000000E-01 1.70000000E-01 1.87000000E-01 2.03000000E-01 + 2.19000000E-01 2.34000000E-01 2.50000000E-01 2.65000000E-01 7.40000000E-02 + 9.20000000E-02 1.05000000E-01 1.06000000E-01 1.07000000E-01 1.08000000E-01 + 1.09000000E-01 1.10000000E-01 1.11000000E-01 1.12000000E-01 1.13000000E-01 + 1.14000000E-01 1.15000000E-01 1.16000000E-01 1.17000000E-01 1.17000000E-01 + 1.26000000E-01 1.36000000E-01 1.46000000E-01 1.55000000E-01 1.65000000E-01 + 1.73000000E-01 1.82000000E-01 1.90000000E-01 1.62000000E-01 1.78000000E-01 + 1.10000000E-01 1.35000000E-01 1.59000000E-01 1.83000000E-01 2.07000000E-01 + 2.31000000E-01 6.90000000E-02 9.40000000E-02 1.02000000E-01 1.03000000E-01 + 1.05000000E-01 1.07000000E-01 1.09000000E-01 1.11000000E-01 1.13000000E-01 + 1.15000000E-01 1.17000000E-01 1.19000000E-01 1.20000000E-01 1.22000000E-01 + 1.24000000E-01 1.26000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 1.28000000E-01 + 5.47000000E-01 5.92000000E-01 5.99000000E-01 6.68000000E-01 6.98000000E-01 + 7.18000000E-01 7.26000000E-01 7.36000000E-01 7.43000000E-01 7.45000000E-01 + 6.07000000E-01 6.66000000E-01 6.93000000E-01 7.09000000E-01 7.20000000E-01 + 7.29000000E-01 7.35000000E-01 7.40000000E-01 6.08000000E-01 7.34000000E-01 + 7.46000000E-01 7.55000000E-01 7.61000000E-01 7.65000000E-01 7.67000000E-01 + 7.60000000E-01 7.49000000E-01 7.33000000E-01 7.08000000E-01 6.48000000E-01 + 6.88000000E-01 7.09000000E-01 7.22000000E-01 7.31000000E-01 7.38000000E-01 + 7.44000000E-01 6.09000000E-01 7.30000000E-01 7.41000000E-01 7.52000000E-01 + 7.58000000E-01 7.63000000E-01 7.66000000E-01 7.58000000E-01 7.48000000E-01 + 7.33000000E-01 7.01000000E-01 6.45000000E-01 6.86000000E-01 7.08000000E-01 + 7.22000000E-01 7.31000000E-01 7.38000000E-01 7.44000000E-01 6.08000000E-01 + 7.28000000E-01 7.32000000E-01 7.32000000E-01 7.32000000E-01 7.32000000E-01 + 7.32000000E-01 7.32000000E-01 7.32000000E-01 7.32000000E-01 7.32000000E-01 + 7.31000000E-01 7.31000000E-01 7.31000000E-01 7.31000000E-01 7.46000000E-01 + 7.37000000E-01 7.46000000E-01 7.51000000E-01 7.55000000E-01 7.58000000E-01 + 7.47000000E-01 7.32000000E-01 7.11000000E-01 7.41000000E-01 7.07000000E-01 + 7.47000000E-01 7.43000000E-01 7.42000000E-01 7.42000000E-01 7.43000000E-01 + 7.44000000E-01 6.05000000E-01 7.24000000E-01 7.26000000E-01 7.26000000E-01 + 7.27000000E-01 7.27000000E-01 7.27000000E-01 7.28000000E-01 7.28000000E-01 + 7.28000000E-01 7.28000000E-01 7.28000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 7.29000000E-01 + 1.00000000E+00 1.70400000E+00 3.23000000E-01 3.89000000E-01 4.67000000E-01 + 5.67000000E-01 6.36000000E-01 6.64000000E-01 6.96000000E-01 8.79000000E-01 + 3.07000000E-01 3.48000000E-01 4.00000000E-01 4.54000000E-01 5.09000000E-01 + 5.65000000E-01 6.21000000E-01 6.77000000E-01 2.56000000E-01 3.50000000E-01 + 3.64000000E-01 3.62000000E-01 3.62000000E-01 3.62000000E-01 3.62000000E-01 + 3.65000000E-01 3.69000000E-01 3.75000000E-01 3.86000000E-01 4.46000000E-01 + 4.55000000E-01 4.74000000E-01 4.96000000E-01 5.20000000E-01 5.45000000E-01 + 5.70000000E-01 2.45000000E-01 3.22000000E-01 3.55000000E-01 3.59000000E-01 + 3.64000000E-01 3.70000000E-01 3.76000000E-01 3.85000000E-01 3.95000000E-01 + 4.08000000E-01 3.76000000E-01 4.23000000E-01 4.22000000E-01 4.32000000E-01 + 4.45000000E-01 4.59000000E-01 4.75000000E-01 4.91000000E-01 2.28000000E-01 + 2.92000000E-01 2.89000000E-01 2.92000000E-01 2.95000000E-01 2.98000000E-01 + 3.00000000E-01 3.03000000E-01 3.06000000E-01 3.09000000E-01 3.12000000E-01 + 3.15000000E-01 3.18000000E-01 3.21000000E-01 3.24000000E-01 3.61000000E-01 + 3.69000000E-01 3.70000000E-01 3.72000000E-01 3.75000000E-01 3.78000000E-01 + 3.85000000E-01 3.94000000E-01 4.07000000E-01 5.26000000E-01 6.21000000E-01 + 3.36000000E-01 3.54000000E-01 3.71000000E-01 3.86000000E-01 4.02000000E-01 + 4.17000000E-01 2.09000000E-01 2.94000000E-01 2.65000000E-01 2.72000000E-01 + 2.78000000E-01 2.84000000E-01 2.91000000E-01 2.97000000E-01 3.03000000E-01 + 3.10000000E-01 3.16000000E-01 3.22000000E-01 3.29000000E-01 3.35000000E-01 + 3.41000000E-01 3.48000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 3.54000000E-01 + 2.74000000E-01 2.96000000E-01 5.80000000E-02 4.40000000E-02 3.90000000E-02 + 3.90000000E-02 3.60000000E-02 3.20000000E-02 2.90000000E-02 3.30000000E-02 + 5.50000000E-02 3.90000000E-02 3.30000000E-02 3.00000000E-02 2.80000000E-02 + 2.60000000E-02 2.50000000E-02 2.50000000E-02 4.50000000E-02 4.40000000E-02 + 3.30000000E-02 2.50000000E-02 2.10000000E-02 1.80000000E-02 1.50000000E-02 + 1.80000000E-02 2.10000000E-02 2.50000000E-02 3.30000000E-02 5.00000000E-02 + 3.80000000E-02 3.10000000E-02 2.70000000E-02 2.40000000E-02 2.20000000E-02 + 2.10000000E-02 4.30000000E-02 4.00000000E-02 3.20000000E-02 2.50000000E-02 + 2.10000000E-02 1.80000000E-02 1.60000000E-02 1.90000000E-02 2.20000000E-02 + 2.80000000E-02 3.10000000E-02 4.70000000E-02 3.40000000E-02 2.80000000E-02 + 2.40000000E-02 2.10000000E-02 1.90000000E-02 1.80000000E-02 3.90000000E-02 + 3.60000000E-02 2.50000000E-02 2.50000000E-02 2.50000000E-02 2.60000000E-02 + 2.60000000E-02 2.60000000E-02 2.70000000E-02 2.70000000E-02 2.70000000E-02 + 2.70000000E-02 2.80000000E-02 2.80000000E-02 2.80000000E-02 3.30000000E-02 + 3.30000000E-02 2.60000000E-02 2.10000000E-02 1.80000000E-02 1.60000000E-02 + 1.80000000E-02 2.20000000E-02 2.60000000E-02 4.90000000E-02 8.20000000E-02 + 3.00000000E-02 2.40000000E-02 2.10000000E-02 1.80000000E-02 1.60000000E-02 + 1.50000000E-02 3.50000000E-02 3.60000000E-02 2.20000000E-02 2.30000000E-02 + 2.40000000E-02 2.40000000E-02 2.50000000E-02 2.50000000E-02 2.60000000E-02 + 2.70000000E-02 2.70000000E-02 2.80000000E-02 2.80000000E-02 2.90000000E-02 + 3.00000000E-02 3.00000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 3.10000000E-02 + -5.92770000E-01 -9.17860000E-01 -2.48668000E+00 -8.36430000E-01 -3.18470000E-01 + -4.55790000E-01 -5.70730000E-01 -6.77940000E-01 -7.69860000E-01 -8.51140000E-01 + -1.51908000E+00 -3.18830000E-01 -2.17900000E-01 -3.01260000E-01 -3.72750000E-01 + -4.15890000E-01 -4.45930000E-01 -5.91320000E-01 -9.56370000E-01 -2.88450000E-01 + -4.87320000E-01 -6.34280000E-01 -7.17490000E-01 -7.91980000E-01 -8.68490000E-01 + -9.17520000E-01 -9.54890000E-01 -9.88460000E-01 -1.50055000E+00 -2.92560000E-01 + -2.14960000E-01 -2.91180000E-01 -3.42080000E-01 -3.84480000E-01 -4.34940000E-01 + -5.24310000E-01 -8.10070000E-01 -2.58460000E-01 -4.35550000E-01 -5.32740000E-01 + -6.08450000E-01 -6.10260000E-01 -7.58920000E-01 -8.90130000E-01 -8.45780000E-01 + -8.69340000E-01 -1.38234000E+00 -2.64850000E-01 -1.97280000E-01 -2.65040000E-01 + -2.94470000E-01 -3.16570000E-01 -3.63590000E-01 -4.57840000E-01 -8.61830000E-01 + -1.33004000E+00 -6.82880000E-01 -6.25540000E-01 -6.35430000E-01 -8.79690000E-01 + -9.02690000E-01 -9.59650000E-01 -1.01869000E+00 -6.81170000E-01 -7.96500000E-01 + -9.04950000E-01 -8.34790000E-01 -9.01560000E-01 -9.74130000E-01 -1.03326000E+00 + -7.15490000E-01 -7.83960000E-01 -8.28380000E-01 -5.61040000E-01 -1.03841000E+00 + -8.62500000E-01 -9.59300000E-01 -1.01755000E+00 -7.64900000E-01 -1.29467000E+00 + -1.92740000E-01 -2.56010000E-01 -3.19820000E-01 -1.94660000E-01 -2.60090000E-01 + -4.27990000E-01 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 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 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 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 + 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 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 + 1.90440000E+00 1.36430000E+00 4.84000000E-01 7.31600000E+00 9.24030000E+00 + 8.78200000E+00 5.61290000E+00 4.83340000E+00 3.24870000E+00 2.71090000E+00 + 2.01130000E+00 1.50282000E+01 1.46788000E+01 2.13546000E+01 2.38290000E+01 + 1.98022000E+01 1.50468000E+01 1.09745000E+01 1.27219000E+01 1.51949000E+01 + 1.57342000E+01 2.79222000E+01 1.61366000E+01 2.21252000E+01 2.45676000E+01 + 1.96016000E+01 1.79818000E+01 1.72396000E+01 5.01500000E+00 1.92898000E+01 + 2.34477000E+01 2.23993000E+01 2.96586000E+01 2.60081000E+01 2.15580000E+01 + 1.69594000E+01 1.56479000E+01 1.86897000E+01 1.93530000E+01 3.43444000E+01 + 1.98480000E+01 2.72140000E+01 3.02182000E+01 2.41099000E+01 2.21176000E+01 + 2.12047000E+01 7.56690000E+00 2.37264000E+01 2.93879000E+01 3.01278000E+01 + 4.19382000E+01 3.85203000E+01 3.39511000E+01 2.75774000E+01 1.56460000E+01 + 1.04460000E+01 2.01830000E+01 4.99540000E+01 4.76430000E+01 1.18690000E+01 + 1.08670000E+01 9.96100000E+00 9.18700000E+00 3.67980000E+01 1.34070000E+01 + 9.38200000E+00 9.81700000E+00 8.90800000E+00 8.40700000E+00 8.05800000E+00 + 1.29600000E+01 2.67920000E+01 2.49300000E+01 3.45630000E+01 1.11860000E+01 + 1.09440000E+01 1.01270000E+01 9.40400000E+00 1.36660000E+01 7.81000000E+00 + 6.93600000E+01 5.86630000E+01 4.79690000E+01 4.52740000E+01 3.92290000E+01 + 3.30660000E+01 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 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 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 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 + 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 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 0.00000000E+00 0.00000000E+00 0.00000000E+00 +Gaussian Version C N= 2 +ES64L-G16RevC.01 diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index e676371e7..4aff98a81 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -100,6 +100,18 @@ def test_load_fchk_hf_sto3g_num(): 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]) + # hirshfeld is under label `Type 6 charges` in fchk + 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]) + + def test_load_fchk_h_sto3g_num(): mol = load_fchk_helper('h_sto3g.fchk') assert mol.title == 'h_sto3g' From 4089f3c407f4f22cf244e3d0c9a4f8870d3a644b Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Wed, 11 Aug 2021 11:20:35 -0400 Subject: [PATCH 067/144] Fix pycodestyle E241 --- iodata/test/test_fchk.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index 4aff98a81..3379d6152 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -102,14 +102,14 @@ def test_load_fchk_hf_sto3g_num(): 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]) + 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(): From c0bf877bb2868dea36e580b9c85ff461773030b4 Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Thu, 12 Aug 2021 16:35:14 -0400 Subject: [PATCH 068/144] Add code to dump new saved charges for fchk --- iodata/formats/fchk.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 8fabcf322..89eaa207e 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -632,6 +632,12 @@ def dump_one(f: TextIO, data: IOData): _dump_real_arrays("ESP Charges", data.atcharges["esp"], f) if 'npa' in data.atcharges: _dump_real_arrays("NPA Charges", data.atcharges["npa"], f) + if 'mbs' in data.atcharges: + _dump_real_arrays("MBS Charges", data.atcharges["mbs"], f) + if 'hirshfeld' in data.atcharges: + _dump_real_arrays("Type 6 Charges", data.atcharges["hirshfeld"], f) + if 'cm5' in data.atcharges: + _dump_real_arrays("Type 7 Charges", data.atcharges["cm5"], f) # write atomic gradient if data.atgradient is not None: From 1cd3f177fc55893913214f29f3e6ca532e676cc8 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 27 Aug 2021 07:48:25 +0200 Subject: [PATCH 069/144] Remove redundant code --- iodata/formats/molden.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 7b1b43a8a..9289b1b26 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -637,33 +637,25 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): return # --- CFOUR 2.1 - cfour_obasis = obasis - if cfour_obasis is not None and \ - _is_normalized_properly(cfour_obasis, atcoords, coeffsa, coeffsb): - lit.warn('Corrected for CFOUR errors in Molden/MKL file.') - result['obasis'] = cfour_obasis - return - - cfour_coeff_correction = _fix_mo_coeffs_cfour(cfour_obasis) + cfour_coeff_correction = _fix_mo_coeffs_cfour(obasis) if cfour_coeff_correction is not None: coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] if coeffsb is None: coeffsb_cfour = None else: coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] - if _is_normalized_properly(cfour_obasis, + if _is_normalized_properly(obasis, atcoords, coeffsa_cfour, coeffsb_cfour): lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') - result['obasis'] = cfour_obasis + result['obasis'] = obasis if result['mo'].kind == 'restricted': result['mo'].coeffs[:] = coeffsa_cfour else: result['mo'].coeffsa[:] = coeffsa_cfour result['mo'].coeffsb[:] = coeffsb_cfour return - result['obasis'] = cfour_obasis # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) From 08c1bccfe0ccff96004b8855b0cb2e15e3e1c803 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 27 Aug 2021 07:57:06 +0200 Subject: [PATCH 070/144] Fix new linter messages for latest Pylint --- .pylintrc | 78 +++++++++++++++++++++++++++++++++++++- doc/conf.py | 20 +++++----- iodata/formats/gamess.py | 2 +- iodata/formats/json.py | 36 +++++++++--------- iodata/formats/qchemlog.py | 2 +- iodata/test/test_json.py | 2 +- iodata/utils.py | 10 ++--- 7 files changed, 113 insertions(+), 37 deletions(-) diff --git a/.pylintrc b/.pylintrc index 715d3b73a..95fdcf0e2 100644 --- a/.pylintrc +++ b/.pylintrc @@ -50,7 +50,83 @@ confidence= # --enable=similarities". If you want to run only the classes checker, but have # no Warning level messages displayed, use"--disable=all --enable=classes # --disable=W" -disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call,too-many-arguments,too-many-locals,too-few-public-methods,fixme,invalid-name,duplicate-code,unsubscriptable-object,no-member,too-many-lines +disable= + print-statement, + parameter-unpacking, + unpacking-in-except, + old-raise-syntax, + backtick, + long-suffix, + old-ne-operator, + old-octal-literal, + import-star-module-level, + raw-checker-failed, + bad-inline-option, + locally-disabled, + locally-enabled, + file-ignored, + suppressed-message, + useless-suppression, + deprecated-pragma, + apply-builtin, + basestring-builtin, + buffer-builtin, + cmp-builtin, + coerce-builtin, + execfile-builtin, + file-builtin, + long-builtin, + raw_input-builtin, + reduce-builtin, + standarderror-builtin, + unicode-builtin, + xrange-builtin, + coerce-method, + delslice-method, + getslice-method, + setslice-method, + no-absolute-import, + old-division, + dict-iter-method, + dict-view-method, + next-method-called, + metaclass-assignment, + indexing-exception, + raising-string, + reload-builtin, + oct-method, + hex-method, + nonzero-method, + cmp-method, + input-builtin, + round-builtin, + intern-builtin, + unichr-builtin, + map-builtin-not-iterating, + zip-builtin-not-iterating, + range-builtin-not-iterating, + filter-builtin-not-iterating, + using-cmp-argument, + eq-without-hash, + div-method, + idiv-method, + rdiv-method, + exception-message-attribute, + invalid-str-codec, + sys-max-int, + bad-python3-import, + deprecated-string-function, + deprecated-str-translate-call, + too-many-arguments, + too-many-locals, + too-few-public-methods, + fixme, + invalid-name, + duplicate-code, + unsubscriptable-object, + no-member, + too-many-lines, + unspecified-encoding # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/doc/conf.py b/doc/conf.py index 1a84c3c74..0c7f2e389 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -86,18 +86,18 @@ master_doc = 'index' # General information about the project. -project = u'IOData' -copyright = u'2019, The IODATA Development Team' -author = u'The IODATA Development Team' +project = 'IOData' +copyright = '2019, The IODATA Development Team' +author = 'The IODATA Development Team' -# The version info for the project you're documenting, acts as replacement for +# 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 = u'' +version = '' # The full version, including alpha/beta/rc tags. -release = u'' +release = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -166,8 +166,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'IOData.tex', u'IOData Documentation', - u'The HORTON Developers', 'manual'), + (master_doc, 'IOData.tex', 'IOData Documentation', + 'The HORTON Developers', 'manual'), ] # -- Options for manual page output --------------------------------------- @@ -175,7 +175,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'iodata', u'IOData Documentation', + (master_doc, 'iodata', 'IOData Documentation', [author], 1) ] @@ -185,7 +185,7 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'IOData', u'IOData Documentation', + (master_doc, 'IOData', 'IOData Documentation', author, 'IOData', 'One line description of project.', 'Miscellaneous'), ] diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index 63c757d87..627fb4119 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -121,7 +121,7 @@ def _read_masses(lit: LineIterator, result: dict) -> np.ndarray: 'atnums', 'atcoords']) def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" - result = dict() + result = {} while True: try: line = next(lit) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 691324f3e..18ea7026a 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -621,7 +621,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: # Remove all null entries and empty dicts in json # QCEngine seems to add null entries and empty dicts even for optional and empty keys fix_keys = {k: v for k, v in json_in.items() if v is not None} - fix_subkeys = dict() + fix_subkeys = {} for key in fix_keys: if isinstance(fix_keys[key], dict): fix_subkeys[key] = {k: v for k, v in fix_keys[key].items() if v is not None} @@ -772,8 +772,8 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "{}: QCSchema topology requires '{}' key".format(lit.filename, key) ) - topology_dict = dict() - extra_dict = dict() + topology_dict = {} + extra_dict = {} # Save schema name & version extra_dict["schema_name"] = "qcschema_molecule" @@ -966,7 +966,7 @@ def _find_passthrough_dict(result: dict, keys: set) -> dict: # Avoid altering original dict result = result.copy() - passthrough_dict = dict() + passthrough_dict = {} parsed_keys = keys.intersection(result.keys()) for key in parsed_keys: del result[key] @@ -998,7 +998,7 @@ def _load_qcschema_basis(_result: dict, _lit: LineIterator) -> dict: QCSchema Basis schema is not yet implemented in IOData. """ - # basis_dict = dict() + # basis_dict = {} # return basis_dict raise NotImplementedError("qcschema_basis is not yet implemented in IOData.") @@ -1045,7 +1045,7 @@ def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: It may contain ``atmasses``, ``bonds``, ``g_rot``, ``obasis``, ``run_type`` & ``title`` keys and corresponding values as well. """ - extra_dict = dict() + extra_dict = {} input_dict = _parse_input_keys(result, lit) extra_dict["input"] = input_dict["extra"] @@ -1094,8 +1094,8 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: "{}: QCSchema `qcschema_input` file requires '{}' key".format(lit.filename, key) ) # Store all extra keys in extra_dict and gather at end - input_dict = dict() - extra_dict = dict() + input_dict = {} + extra_dict = {} # Save schema name & version extra_dict["schema_name"] = "qcschema_input" @@ -1208,8 +1208,8 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: It may contain ``obasis`` and ``extra`` keys and corresponding values as well. """ - model_dict = dict() - extra_dict = dict() + model_dict = {} + extra_dict = {} if "method" not in model: raise FileFormatError("{}: QCSchema `model` requires a `method`".format(lit.filename)) @@ -1275,7 +1275,7 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: keep_stdout = True else: keep_stdout = protocols["stdout"] - protocols_dict = dict() + protocols_dict = {} if wavefunction not in {"all", "orbitals_and_eigenvalues", "return_results", "none"}: raise FileFormatError( "{}: Invalid `protocols` `wavefunction` keyword.".format(lit.filename) @@ -1306,7 +1306,7 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: ``title`` keys and corresponding values as well. """ - extra_dict = dict() + extra_dict = {} output_dict = _parse_output_keys(result, lit) extra_dict["output"] = output_dict["extra"] @@ -1358,8 +1358,8 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: ) # Store all extra keys in extra_dict and gather at end - output_dict = dict() - extra_dict = dict() + output_dict = {} + extra_dict = {} extra_dict["schema_name"] = "qcschema_output" extra_dict["schema_version"] = _version_check(result, 2, "qcschema_output", lit) @@ -1632,7 +1632,7 @@ def _dump_qcschema_input(data: IOData) -> dict: input_dict["driver"] = data.extra["input"]["driver"] if "model" not in data.extra["input"]: raise FileFormatError("qcschema_input requires `model` field in extra['input'].") - input_dict["model"] = dict() + input_dict["model"] = {} if data.lot is None: raise FileFormatError("qcschema_input requires specifed `lot`.") input_dict["model"]["method"] = data.lot @@ -1648,7 +1648,7 @@ def _dump_qcschema_input(data: IOData) -> dict: if "id" in data.extra["input"]: input_dict["id"] = data.extra["input"]["id"] if "protocols" in data.extra["input"]: - input_dict["protocols"] = dict() + input_dict["protocols"] = {} # Remove 'keep_' from protocols keys (added in IOData for readability) for keep in data.extra["input"]["protocols"]: input_dict["protocols"][keep[5:]] = data.extra["input"]["protocols"][keep] @@ -1692,7 +1692,7 @@ def _dump_qcschema_output(data: IOData) -> dict: output_dict["driver"] = data.extra["input"]["driver"] if "model" not in data.extra["input"]: raise FileFormatError("qcschema_output requires `model` field in extra['input'].") - output_dict["model"] = dict() + output_dict["model"] = {} if data.lot is None: raise FileFormatError("qcschema_output requires specifed `lot`.") output_dict["model"]["method"] = data.lot @@ -1724,7 +1724,7 @@ def _dump_qcschema_output(data: IOData) -> dict: if "id" in data.extra["input"]: output_dict["id"] = data.extra["input"]["id"] if "protocols" in data.extra["input"]: - output_dict["protocols"] = dict() + output_dict["protocols"] = {} # Remove 'keep_' from protocols keys (added in IOData for readability) for keep in data.extra["input"]["protocols"]: output_dict["protocols"][keep[5:]] = data.extra["input"]["protocols"][keep] diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 893a1e95d..55d09d513 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -266,7 +266,7 @@ def _helper_orbital_energies_restricted(lit: LineIterator) -> Tuple: def _helper_orbital_energies_unrestricted(lit: LineIterator) -> Tuple: """Load occupied and virtual orbital energies for unrestricted calculation.""" - subdata = dict() + subdata = {} # alpha occupied MOs subdata['mo_a_occ'] = _helper_section('-- Occupied --', '-- Virtual --', lit, backward=True) # alpha unoccupied MOs diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index f4083d7ca..a2d243f1b 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -218,7 +218,7 @@ def test_inout_molssi_qcschema_molecule(tmpdir, filename): # Remove all null entries and empty dicts in json # QCEngine seems to add null entries and empty dicts even for optional and empty keys fix_keys = {k: v for k, v in mol1_preproc.items() if v is not None} - fix_subkeys = dict() + fix_subkeys = {} for key in fix_keys: if isinstance(fix_keys[key], dict): fix_subkeys[key] = {k: v for k, v in fix_keys[key].items() if v is not None} diff --git a/iodata/utils.py b/iodata/utils.py index c8da5c269..facb8e53a 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -37,15 +37,15 @@ # 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(u'atomic unit of length') -electronvolt: float = 1 / spc.value(u'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(u'Bohr radius') +meter: float = 1 / spc.value('Bohr radius') nanometer: float = 1e-9 * meter -second: float = 1 / spc.value(u'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(u'electron mass') * spc.value(u'Avogadro constant')) +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') From 3ca8536f6dc6e02c42ceac97b32148f5bbea6af2 Mon Sep 17 00:00:00 2001 From: amandadumi Date: Mon, 30 Aug 2021 22:29:34 -0400 Subject: [PATCH 071/144] adding h2o test --- iodata/test/data/h2o_ccpvdz_cfour.molden | 336 +++++++++++++++++++++++ iodata/test/test_molden.py | 5 +- 2 files changed, 339 insertions(+), 2 deletions(-) create mode 100644 iodata/test/data/h2o_ccpvdz_cfour.molden diff --git a/iodata/test/data/h2o_ccpvdz_cfour.molden b/iodata/test/data/h2o_ccpvdz_cfour.molden new file mode 100644 index 000000000..4368ebc7c --- /dev/null +++ b/iodata/test/data/h2o_ccpvdz_cfour.molden @@ -0,0 +1,336 @@ +[Molden Format] +[ATOMS] AU +O 1 8 0.0000000000 0.0000000000 0.0000000000 +[Molden Format] +[GTO] + 1 0 +s 9 1.00 + 11720.0000000000 7.100000000000000E-004 + 1759.00000000000 5.470000000000000E-003 + 400.800000000000 2.783700000000000E-002 + 113.700000000000 0.104800000000000 + 37.0300000000000 0.283062000000000 + 13.2700000000000 0.448719000000000 + 5.02500000000000 0.270952000000000 + 1.01300000000000 1.545800000000000E-002 + 0.302300000000000 -2.585000000000000E-003 +s 9 1.00 + 11720.0000000000 -1.600000000000000E-004 + 1759.00000000000 -1.263000000000000E-003 + 400.800000000000 -6.267000000000000E-003 + 113.700000000000 -2.571600000000000E-002 + 37.0300000000000 -7.092400000000000E-002 + 13.2700000000000 -0.165411000000000 + 5.02500000000000 -0.116955000000000 + 1.01300000000000 0.557368000000000 + 0.302300000000000 0.572759000000000 +s 9 1.00 + 11720.0000000000 0.000000000000000E+000 + 1759.00000000000 0.000000000000000E+000 + 400.800000000000 0.000000000000000E+000 + 113.700000000000 0.000000000000000E+000 + 37.0300000000000 0.000000000000000E+000 + 13.2700000000000 0.000000000000000E+000 + 5.02500000000000 0.000000000000000E+000 + 1.01300000000000 0.000000000000000E+000 + 0.302300000000000 1.00000000000000 +p 4 1.00 + 17.7000000000000 4.301800000000000E-002 + 3.85400000000000 0.228913000000000 + 1.04600000000000 0.508728000000000 + 0.275300000000000 0.460531000000000 +p 4 1.00 + 17.7000000000000 0.000000000000000E+000 + 3.85400000000000 0.000000000000000E+000 + 1.04600000000000 0.000000000000000E+000 + 0.275300000000000 1.00000000000000 +d 1 1.00 + 1.18500000000000 1.00000000000000 + + +[MO] + Sym= A + Ene= -20.7001505863133 + Spin= Alpha + Occup= 1.0 + 1 1.0002306751 + 2 0.0024756258 + 3 0.0002313333 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.0005641051 + 11 -0.0011828259 + 12 -0.0005641051 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= -1.25252834132722 + Spin= Alpha + Occup= 1.0 + 1 0.0019570464 + 2 1.0107190924 + 3 -0.0079117688 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.0000274822 + 11 -0.0026984315 + 12 -0.0000274822 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= -0.572399522971029 + Spin= Alpha + Occup= 1.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.9865945479 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0166013578 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= -0.572399522971027 + Spin= Alpha + Occup= 1.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.9865945479 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0166013578 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 9.400243483509207E-003 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.7967818495 + 6 0.0000000000 + 7 0.0000000000 + 8 0.2399029598 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 1.13604126310501 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 1.3645826706 + 7 0.0000000000 + 8 0.0000000000 + 9 -1.6837989968 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 1.13604126310501 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 1.3645826706 + 5 0.0000000000 + 6 0.0000000000 + 7 -1.6837989968 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 1.18661100682885 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 1.4834397026 + 6 0.0000000000 + 7 0.0000000000 + 8 -1.6667037039 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 1.20425012316674 + Spin= Alpha + Occup= 0.0 + 1 0.5600486787 + 2 2.4274856355 + 3 -3.2043851670 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.1526965089 + 11 0.1568027798 + 12 0.1526965089 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.86543398793250 + Spin= Alpha + Occup= 0.0 + 1 0.0071431511 + 2 0.0517470643 + 3 -0.0247158181 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.2799206862 + 11 -0.5860261112 + 12 0.2799206862 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.87492401567841 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 1.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.87492401567841 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 1.0000000000 + Sym= A + Ene= 2.90248478458832 + Spin= Alpha + Occup= 0.0 + 1 0.0000000000 + 2 0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.0000000000 + 11 0.0000000000 + 12 0.0000000000 + 13 0.0000000000 + 14 1.0000000000 + 15 0.0000000000 + Sym= A + Ene= 2.90248478458833 + Spin= Alpha + Occup= 0.0 + 1 -0.0000000000 + 2 -0.0000000000 + 3 0.0000000000 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 0.5000000000 + 11 0.0000000000 + 12 -0.5000000000 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 + Sym= A + Ene= 5.44588451900774 + Spin= Alpha + Occup= 0.0 + 1 0.4265980641 + 2 3.5512321174 + 3 -0.7818339229 + 4 0.0000000000 + 5 0.0000000000 + 6 0.0000000000 + 7 0.0000000000 + 8 0.0000000000 + 9 0.0000000000 + 10 -0.7785719737 + 11 -0.7680000366 + 12 -0.7785719737 + 13 0.0000000000 + 14 0.0000000000 + 15 0.0000000000 diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index a01bb150c..49b693ef1 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -200,7 +200,7 @@ def test_load_molden_nh3_molden_cart(): assert_allclose(charges, molden_charges, atol=1.e-3) -def test_load_molden_h_cfour(): +def test_load_molden_cfour(): # The file tested here is created with CFOUR 2.1. file_list = [ 'h_sonly_sph_cfour.molden', @@ -212,7 +212,8 @@ def test_load_molden_h_cfour(): 'h_ponly_cart_cfour.molden', 'h_donly_cart_cfour.molden', 'h_fonly_cart_cfour.molden', - 'h_gonly_cart_cfour.molden'] + 'h_gonly_cart_cfour.molden', + 'h2o_ccpvdz_cfour.molden'] for i in file_list: with path('iodata.test.data', i) as fn_molden: From ba35751de3b43566e4d8ab8c72d76779bac303c3 Mon Sep 17 00:00:00 2001 From: leila-pujal Date: Tue, 31 Aug 2021 09:15:23 -0400 Subject: [PATCH 072/144] Add new check_load_dump_consistency test --- iodata/test/test_fchk.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index 3379d6152..06a4486b9 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -585,6 +585,10 @@ def test_dump_fchk_from_fchk_h2o(tmpdir): 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') + + 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') From 4a0d3389db3babd3a3256035e7ae309d0f92da89 Mon Sep 17 00:00:00 2001 From: Michael Richer Date: Sat, 9 Oct 2021 13:37:25 -0400 Subject: [PATCH 073/144] update documentation --- iodata/iodata.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/iodata/iodata.py b/iodata/iodata.py index dcc4a896e..47e890866 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -129,20 +129,25 @@ class IOData: www.basissetexchange.org. one_ints Dictionary where keys are names and values are numpy arrays with - one-body operators, typically integrals of a one-body operator - with a pair of (Gaussian) basis functions. Names can start with ``olp`` + one-body operators, typically integrals of a one-body operator with + a pair of (Gaussian) basis functions. Names can start with ``olp`` (overlap), ``kin`` (kinetic energy), ``na`` (nuclear attraction), - ``core`` (core hamiltonian), etc. When relevant, these names must have a - suffix ``_ao`` or ``_mo`` to clarify in which basis the integrals are - computed. ``_ao`` is used to denote integrals in a non-orthogonal - (atomic orbital) basis. ``_mo`` is used to denote an orthogonal - (molecular orbital) basis. For the overlap integrals, this suffix can be - omitted because it is only useful to compute them in the atomic-orbital - basis. + ``core`` (core hamiltonian), etc., or ``one`` (general one-electron + integral). When relevant, these names must have a suffix ``_ao`` or + ``_mo`` to clarify in which basis the integrals are computed. ``_ao`` + is used to denote integrals in a non-orthogonal (atomic orbital) basis. + ``_mo`` is used to denote an orthogonal (molecular orbital) basis. For + the overlap integrals, this suffix can be omitted because it is only + useful to compute them in the atomic-orbital basis. one_rdms Dictionary where keys are names and values are one-particle density matrices. Names can be ``scf``, ``post_scf``, ``scf_spin``, - ``post_scf_spin``. These matrices are always expressed in the AO basis. + ``post_scf_spin``. When relevant, these names must have a suffix + ``_ao`` or ``_mo`` to clarify in which basis the RDMs are computed. + ``_ao`` is used to denote a non-orthogonal (atomic orbital) basis. + ``_mo`` is used to denote an orthogonal (molecular orbital) basis. + For the SCF RDMs, this suffix can be omitted because it is only useful + to compute them in the atomic-orbital basis. run_type The type of calculation that lead to the results stored in IOData, which must be one of the following: 'energy', 'energy_force', 'opt', 'scan', @@ -156,17 +161,18 @@ class IOData: A suitable name for the data. two_ints Dictionary where keys are names and values are numpy arrays with - two-body operators, typically integrals of two-body operator - with four of (Gaussian) basis functions. Names can start with ``er`` - (electron repulsion) or ``two`` (general pairswise interaction). When - relevant, these names must have a suffix ``_ao`` or ``_mo`` to clarify - in which basis the integrals are computed, see one_ints for more - details. Array indexes are in physicist's notation. + two-body operators, typically integrals of two-body operator with four + of (Gaussian) basis functions. Names can start with ``er`` (electron + repulsion) or ``two`` (general pairswise interaction). When relevant, + these names must have a suffix ``_ao`` or ``_mo`` to clarify in which + basis the integrals are computed. See ``one_ints`` for more details. + Array indexes are in physicist's notation. two_rdms Dictionary where keys are names and values are two-particle density - matrices. Names can be ``post_scf`` or ``post_scf_spin``. These matrices - are always expressed in the AO basis. Array indexes are in physicist's - notation. + matrices. Names can be ``post_scf`` or ``post_scf_spin``. When relevant, + these names must have a suffix ``_ao`` or ``_mo`` to clarify in which + basis the RDMs are computed. See ``one_rdms`` for more details. + Array indexes are in physicist's notation. """ From 7defb7ea2ba843ed7db447cbe7088c2c62a78d95 Mon Sep 17 00:00:00 2001 From: Michael Richer Date: Sat, 9 Oct 2021 14:10:28 -0400 Subject: [PATCH 074/144] Update {one,two}_rdm keys --- iodata/formats/fchk.py | 8 ++++---- iodata/test/common.py | 2 +- iodata/test/test_fchk.py | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 89eaa207e..1fea1416e 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -164,8 +164,8 @@ def load_one(lit: LineIterator) -> dict: _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') - _load_dm('Spin {} Density'.format(lot), fchk, one_rdms, 'post_scf_spin') + _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 @@ -617,9 +617,9 @@ def dump_one(f: TextIO, data: IOData): title = "Total SCF Density" elif key == "scf_spin": title = "Spin SCF Density" - elif key == "post_scf": + elif key == "post_scf_ao": title = "Total {0} Density".format(level) - elif key == "post_scf_spin": + elif key == "post_scf_spin_ao": title = "Spin {0} Density".format(level) else: title = "Total SCF Density" diff --git a/iodata/test/common.py b/iodata/test/common.py index 661220750..4d75b946f 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -131,7 +131,7 @@ def compare_mols(mol1, mol2, atol=1.0e-8, rtol=0.0): cases = [ ('one_ints', ['olp', 'kin_ao', 'na_ao']), ('two_ints', ['er_ao']), - ('one_rdms', ['scf', 'scf_spin', 'post_scf', 'post_scf_spin']), + ('one_rdms', ['scf', 'scf_spin', 'post_scf_ao', 'post_scf_spin_ao']), ] for attrname, keys in cases: d1 = getattr(mol1, attrname) diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index 06a4486b9..18f71f00b 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -272,7 +272,7 @@ def check_load_azirine(key, numbers): """Perform some basic checks on a azirine fchk file.""" mol = load_fchk_helper('2h-azirine-{}.fchk'.format(key)) assert mol.obasis.nbasis == 33 - dm = mol.one_rdms['post_scf'] + 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) @@ -299,7 +299,7 @@ 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)) assert mol.obasis.nbasis == 9 - dm = mol.one_rdms['post_scf'] + 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) @@ -307,7 +307,7 @@ def check_load_nitrogen(key, numbers, numbers_spin): 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'] + 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]) @@ -333,7 +333,7 @@ def check_normalization_dm_azirine(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'] + 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) From 4bbba1788e764c604a78392935db7bc09ef55d5f Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 18 Oct 2021 21:23:34 +0200 Subject: [PATCH 075/144] Deal with pylint issues --- .pylintrc | 3 ++- .roberto.yaml | 6 +++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index 95fdcf0e2..f73a67630 100644 --- a/.pylintrc +++ b/.pylintrc @@ -126,7 +126,8 @@ disable= unsubscriptable-object, no-member, too-many-lines, - unspecified-encoding + unspecified-encoding, + bad-option-value # Enable the message, report, category or checker with the given id(s). You can # either give multiple identifier separated by comma (,) or put this option diff --git a/.roberto.yaml b/.roberto.yaml index 68253a049..d35bcb71a 100644 --- a/.roberto.yaml +++ b/.roberto.yaml @@ -1,7 +1,11 @@ absolute: true # Force absolute comparison for cardboardlint project: name: iodata - requirements: [[sympy, sympy]] + requirements: [ + [sympy, sympy], + # pylint 2.11.* seems to have bugs which break the CI. + ["pylint <2.11.0", "pylint<2.11.0"] + ] packages: - dist_name: qc-iodata tools: From 6ec7ce81827988d057235dc3a502b753f1db95f5 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 18 Oct 2021 09:44:55 +0200 Subject: [PATCH 076/144] Fix PDB load_one for Cl atoms --- iodata/formats/pdb.py | 2 +- iodata/test/data/indomethacin-dimer.pdb | 88 +++++++++++++++++++++++++ iodata/test/test_pdb.py | 9 +++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 iodata/test/data/indomethacin-dimer.pdb diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 1370e06a9..2959593bc 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -68,7 +68,7 @@ def _parse_pdb_atom_line(line): words = line[12:16].split() # assign atomic number symbol = words[0].title() - atnum = sym2num.get(symbol, sym2num.get(symbol[0], None)) + atnum = sym2num.get(symbol, sym2num.get(symbol[:2], sym2num.get(symbol[0], None))) # atom name, residue name, chain id, & residue sequence number attype = line[12:16].strip() restype = line[17:20].strip() diff --git a/iodata/test/data/indomethacin-dimer.pdb b/iodata/test/data/indomethacin-dimer.pdb new file mode 100644 index 000000000..45d840865 --- /dev/null +++ b/iodata/test/data/indomethacin-dimer.pdb @@ -0,0 +1,88 @@ +TITLE indomethacin_indomethacin +REMARK THIS IS A SIMULATION BOX +CRYST1 3000.000 3000.000 3000.000 90.00 90.00 90.00 P 1 1 +MODEL 1 +ATOM 1 C1 XXX 1 1002.3891002.1441003.016 1.00 0.00 +ATOM 2 C2 XXX 1 1001.0891001.9451002.264 1.00 0.00 +ATOM 3 C3 XXX 1 999.8401002.0381002.867 1.00 0.00 +ATOM 4 C4 XXX 1 998.9231001.8711001.764 1.00 0.00 +ATOM 5 C5 XXX 1 999.6481001.6901000.605 1.00 0.00 +ATOM 6 N6 XXX 1 1001.0471001.7681000.868 1.00 0.00 +ATOM 7 C7 XXX 1 1002.1311001.735 999.982 1.00 0.00 +ATOM 8 O8 XXX 1 1003.2631001.4431000.335 1.00 0.00 +ATOM 9 C9 XXX 1 1001.8731002.152 998.556 1.00 0.00 +ATOM 10 C10 XXX 1 1002.4821001.445 997.502 1.00 0.00 +ATOM 11 C11 XXX 1 1002.2311001.801 996.167 1.00 0.00 +ATOM 12 C12 XXX 1 1001.3961002.889 995.878 1.00 0.00 +ATOM 13 C13 XXX 1 1000.8251003.634 996.921 1.00 0.00 +ATOM 14 C14 XXX 1 1001.0701003.275 998.257 1.00 0.00 +ATOM 15 CL15 XXX 1 1001.0771003.328 994.215 1.00 0.00 +ATOM 16 C16 XXX 1 998.9921001.379 999.401 1.00 0.00 +ATOM 17 C17 XXX 1 997.5841001.355 999.390 1.00 0.00 +ATOM 18 C18 XXX 1 996.8471001.5931000.568 1.00 0.00 +ATOM 19 C19 XXX 1 997.5241001.8391001.783 1.00 0.00 +ATOM 20 O20 XXX 1 995.4701001.5461000.491 1.00 0.00 +ATOM 21 C21 XXX 1 994.6241001.7061001.643 1.00 0.00 +ATOM 22 C22 XXX 1 999.5591002.2611004.338 1.00 0.00 +ATOM 23 C23 XXX 1 999.6311003.7241004.772 1.00 0.00 +ATOM 24 O24 XXX 1 1000.6441004.3881004.825 1.00 0.00 +ATOM 25 O25 XXX 1 998.4921004.2351005.231 1.00 0.00 +ATOM 26 H26 XXX 1 1003.0141001.2471002.966 1.00 0.00 +ATOM 27 H27 XXX 1 1002.2421002.3781004.075 1.00 0.00 +ATOM 28 H28 XXX 1 1002.9601002.9751002.585 1.00 0.00 +ATOM 29 H29 XXX 1 1003.1441000.613 997.724 1.00 0.00 +ATOM 30 H30 XXX 1 1002.6821001.232 995.359 1.00 0.00 +ATOM 31 H31 XXX 1 1000.1901004.486 996.692 1.00 0.00 +ATOM 32 H32 XXX 1 1000.6211003.856 999.061 1.00 0.00 +ATOM 33 H33 XXX 1 999.5391001.142 998.495 1.00 0.00 +ATOM 34 H34 XXX 1 997.0531001.139 998.468 1.00 0.00 +ATOM 35 H35 XXX 1 996.9941001.9891002.715 1.00 0.00 +ATOM 36 H36 XXX 1 994.7821002.6911002.100 1.00 0.00 +ATOM 37 H37 XXX 1 994.8381000.9261002.384 1.00 0.00 +ATOM 38 H38 XXX 1 993.5711001.6251001.346 1.00 0.00 +ATOM 39 H39 XXX 1 1000.2501001.6871004.964 1.00 0.00 +ATOM 40 H40 XXX 1 998.5591001.8821004.580 1.00 0.00 +ATOM 41 H41 XXX 1 998.8251005.1191005.462 1.00 0.00 +ATOM 42 C1 XXX 2 1000.227 998.3851002.728 1.00 0.00 +ATOM 43 C2 XXX 2 1000.499 998.3121001.240 1.00 0.00 +ATOM 44 C3 XXX 2 1001.780 998.2301000.707 1.00 0.00 +ATOM 45 C4 XXX 2 1001.547 998.162 999.286 1.00 0.00 +ATOM 46 C5 XXX 2 1000.190 998.227 999.053 1.00 0.00 +ATOM 47 N6 XXX 2 999.467 998.2451000.284 1.00 0.00 +ATOM 48 C7 XXX 2 998.090 998.1271000.528 1.00 0.00 +ATOM 49 O8 XXX 2 997.577 998.3871001.604 1.00 0.00 +ATOM 50 C9 XXX 2 997.233 997.574 999.418 1.00 0.00 +ATOM 51 C10 XXX 2 996.008 998.194 999.106 1.00 0.00 +ATOM 52 C11 XXX 2 995.197 997.685 998.078 1.00 0.00 +ATOM 53 C12 XXX 2 995.597 996.541 997.372 1.00 0.00 +ATOM 54 C13 XXX 2 996.796 995.893 997.701 1.00 0.00 +ATOM 55 C14 XXX 2 997.608 996.399 998.730 1.00 0.00 +ATOM 56 CL15 XXX 2 994.588 995.910 996.088 1.00 0.00 +ATOM 57 C16 XXX 2 999.708 998.359 997.738 1.00 0.00 +ATOM 58 C17 XXX 2 1000.633 998.316 996.675 1.00 0.00 +ATOM 59 C18 XXX 2 1002.013 998.164 996.922 1.00 0.00 +ATOM 60 C19 XXX 2 1002.484 998.098 998.251 1.00 0.00 +ATOM 61 O20 XXX 2 1002.867 998.134 995.839 1.00 0.00 +ATOM 62 C21 XXX 2 1004.285 997.924 995.971 1.00 0.00 +ATOM 63 C22 XXX 2 1003.084 998.2691001.473 1.00 0.00 +ATOM 64 C23 XXX 2 1003.474 996.9371002.105 1.00 0.00 +ATOM 65 O24 XXX 2 1003.050 996.5131003.158 1.00 0.00 +ATOM 66 O25 XXX 2 1004.401 996.2461001.445 1.00 0.00 +ATOM 67 H26 XXX 2 999.596 999.2491002.966 1.00 0.00 +ATOM 68 H27 XXX 2 1001.137 998.4801003.329 1.00 0.00 +ATOM 69 H28 XXX 2 999.705 997.4841003.070 1.00 0.00 +ATOM 70 H29 XXX 2 995.688 999.071 999.665 1.00 0.00 +ATOM 71 H30 XXX 2 994.258 998.172 997.832 1.00 0.00 +ATOM 72 H31 XXX 2 997.094 994.998 997.161 1.00 0.00 +ATOM 73 H32 XXX 2 998.535 995.890 998.986 1.00 0.00 +ATOM 74 H33 XXX 2 998.655 998.510 997.524 1.00 0.00 +ATOM 75 H34 XXX 2 1000.287 998.404 995.650 1.00 0.00 +ATOM 76 H35 XXX 2 1003.540 998.024 998.484 1.00 0.00 +ATOM 77 H36 XXX 2 1004.483 996.964 996.465 1.00 0.00 +ATOM 78 H37 XXX 2 1004.738 998.732 996.559 1.00 0.00 +ATOM 79 H38 XXX 2 1004.753 997.909 994.980 1.00 0.00 +ATOM 80 H39 XXX 2 1003.053 999.0221002.265 1.00 0.00 +ATOM 81 H40 XXX 2 1003.896 998.5821000.806 1.00 0.00 +ATOM 82 H41 XXX 2 1004.458 995.4871002.050 1.00 0.00 +TER +ENDMDL diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index c1b33eb3e..5663e1bb2 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -218,3 +218,12 @@ def test_load_ch5plus_bonds(): with path("iodata.test.data", "ch5plus.pdb") as fn_pdb: mol = load_one(fn_pdb) assert_equal(mol.bonds[:, :2], [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5]]) + + +def test_indomethacin_dimer(): + with path("iodata.test.data", "indomethacin-dimer.pdb") as fn_pdb: + mol = load_one(fn_pdb) + assert mol.atnums[13] == 6 + assert mol.atnums[14] == 17 + assert mol.atnums[15] == 6 + assert mol.atnums[55] == 17 From 390b22d4e636aaa97db4c4b3790b7b3aeec22cbe Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 19 Oct 2021 17:41:00 +0200 Subject: [PATCH 077/144] Add warnings when guessing the element --- iodata/formats/pdb.py | 56 +++++++++++++++++++++++++++++++++-------- iodata/test/test_pdb.py | 51 +++++++++++++++++++------------------ 2 files changed, 72 insertions(+), 35 deletions(-) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 2959593bc..5b0f89fe6 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -41,7 +41,36 @@ PATTERNS = ['*.pdb'] -def _parse_pdb_atom_line(line): +def _parse_pdb_atom_line(line, lit): + """Parse an ATOM or HETATM line from a PDB file. + + Parameters + ---------- + line + A string with a single ATOM or HETATM line. + lit + The line iterator which read the line, used for generating warnings when needed. + + Returns + ------- + atnum + Atomic number. + atname + The atom name. + resname + The residua name. + chainid + The chain ID. + resnum + The residue number. + atcoord + Cartesian coordinates of the atomic nucleus. + occupancy + The occupancy (usually from the XRD analysis). + bfactor + The temperature factor (usually from the XRD analysis). + + """ # Overview of ATOM records # COLUMNS DATA TYPE FIELD DEFINITION # ------------------------------------------------------------------------------------- @@ -62,16 +91,21 @@ def _parse_pdb_atom_line(line): # 79 - 80 LString(2) charge Charge on the atom. # Get element symbol from position 77:78 in pdb format - words = line[76:78].split() - if not words: + symbol = line[76:78].strip() + if len(symbol) > 0: + atnum = sym2num.get(symbol) + else: # If not present, guess it from position 13:16 (atom name) - words = line[12:16].split() - # assign atomic number - symbol = words[0].title() - atnum = sym2num.get(symbol, sym2num.get(symbol[:2], sym2num.get(symbol[0], None))) + atname = line[12:16].strip() + atnum = sym2num.get(atname, sym2num.get(atname[:2].title(), sym2num.get(atname[0], None))) + lit.warn("Using the atom name in the PDB file to guess the chemical element.") + if atnum is None: + atnum = 0 + lit.warn("Failed to determine the atomic number.") + # atom name, residue name, chain id, & residue sequence number - attype = line[12:16].strip() - restype = line[17:20].strip() + atname = line[12:16].strip() + resname = line[17:20].strip() chainid = line[21] resnum = int(line[22:26]) # add x, y, and z @@ -83,7 +117,7 @@ def _parse_pdb_atom_line(line): # get occupancies & temperature factor occupancy = float(line[54:60]) bfactor = float(line[60:66]) - return atnum, attype, restype, chainid, resnum, atcoord, occupancy, bfactor + return atnum, atname, resname, chainid, resnum, atcoord, occupancy, bfactor def _parse_pdb_conect_line(line): @@ -133,7 +167,7 @@ def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, t 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) + bfactor) = _parse_pdb_atom_line(line, lit) atnums.append(atnum) attypes.append(attype) restypes.append(restype) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index 5663e1bb2..5907e5cc9 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -62,9 +62,22 @@ def check_water(mol): assert_equal(mol.bonds[:, :2], [[0, 1], [1, 2]]) -def check_load_dump_consistency(tmpdir, fn): - """Check if dumping and loading an PDB file results in the same data.""" - mol0 = load_one(str(fn)) +@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 path('iodata.test.data', fn_base) as fn_pdb: + if should_warn: + with pytest.warns(FileFormatWarning) as record: + mol0 = load_one(str(fn_pdb)) + assert len(record) > 1 + else: + mol0 = load_one(str(fn_pdb)) + # write pdb file in a temporary folder & then read it fn_tmp = os.path.join(tmpdir, 'test.pdb') dump_one(mol0, fn_tmp) @@ -87,21 +100,10 @@ def check_load_dump_consistency(tmpdir, fn): assert_equal(mol0.bonds, mol1.bonds) -@pytest.mark.parametrize("fn_base", [ - "water_single.pdb", - "water_single_model.pdb", - "ch5plus.pdb", - "2luv.pdb", - "2bcw.pdb", -]) -def test_load_dump_consistency(fn_base, tmpdir): - with path('iodata.test.data', fn_base) as fn_pdb: - check_load_dump_consistency(tmpdir, fn_pdb) - +def test_load_dump_xyz_consistency(tmpdir): + with path('iodata.test.data', 'water.xyz') as fn_xyz: + mol0 = load_one(str(fn_xyz)) -def check_load_dump_xyz_consistency(tmpdir, fn): - """Check if dumping PDB from an xyz file results in the same data.""" - mol0 = load_one(str(fn)) # write xyz file in a temporary folder & then read it fn_tmp = os.path.join(tmpdir, 'test.pdb') dump_one(mol0, fn_tmp) @@ -123,15 +125,12 @@ def check_load_dump_xyz_consistency(tmpdir, fn): assert mol1.bonds is None -def test_load_dump_xyz_consistency(tmpdir): - with path('iodata.test.data', 'water.xyz') as fn_xyz: - check_load_dump_xyz_consistency(tmpdir, fn_xyz) - - def test_load_peptide_2luv(): # test pdb of small peptide with path('iodata.test.data', '2luv.pdb') as fn_pdb: - mol = load_one(str(fn_pdb)) + with pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_pdb)) + assert len(record) == 271 assert mol.title.startswith("INTEGRIN") assert_equal(len(mol.atnums), 547) restypes = mol.atffparams.get('restypes') @@ -222,7 +221,11 @@ def test_load_ch5plus_bonds(): def test_indomethacin_dimer(): with path("iodata.test.data", "indomethacin-dimer.pdb") as fn_pdb: - mol = load_one(fn_pdb) + with pytest.warns(FileFormatWarning) as record: + mol = load_one(fn_pdb) + assert len(record) == 82 + for item in record: + assert "Using the atom name in the PDB" in item.message.args[0] assert mol.atnums[13] == 6 assert mol.atnums[14] == 17 assert mol.atnums[15] == 6 From 84a11728a9f54443a1bb20c775c392312ed3ed7b Mon Sep 17 00:00:00 2001 From: amandadumi Date: Fri, 19 Nov 2021 13:57:35 -0500 Subject: [PATCH 078/144] b This is a combination of 2 commits. user-set threshold --- iodata/formats/molden.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 9289b1b26..3acae19d7 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -75,10 +75,10 @@ @document_load_one("Molden", ['atcoords', 'atnums', 'atcorenums', 'mo', 'obasis'], ['title']) -def load_one(lit: LineIterator) -> dict: +def load_one(lit: LineIterator, threshold: float = 1e-4) -> dict: """Do not edit this docstring. It will be overwritten.""" result = _load_low(lit) - _fix_molden_from_buggy_codes(result, lit) + _fix_molden_from_buggy_codes(result, lit, threshold) return result @@ -326,7 +326,7 @@ def _load_helper_coeffs(lit: LineIterator) -> Tuple: def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, orb_alpha: np.ndarray, orb_beta: np.ndarray, - threshold: float = 1e-4) -> bool: + threshold: float) -> bool: """Test the normalization of the occupied and virtual orbitals. Parameters @@ -582,7 +582,7 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return None -def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): +def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: float): """Detect errors in the data loaded from a molden or mkl file and correct. This function can recognize erroneous files created by PSI4, ORCA and @@ -609,13 +609,13 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): else: raise ValueError('Molecular orbital kind={0} not recognized'.format(result['mo'].kind)) - if _is_normalized_properly(obasis, atcoords, coeffsa, coeffsb): + if _is_normalized_properly(obasis, atcoords, coeffsa, coeffsb, threshold): # The file is good. No need to change obasis. return # --- ORCA orca_obasis = _fix_obasis_orca(obasis) - if _is_normalized_properly(orca_obasis, atcoords, coeffsa, coeffsb): + if _is_normalized_properly(orca_obasis, atcoords, coeffsa, coeffsb, threshold): lit.warn('Corrected for typical ORCA errors in Molden/MKL file.') result['obasis'] = orca_obasis return @@ -623,7 +623,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): # --- PSI4 < 1.0 psi4_obasis = _fix_obasis_psi4(obasis) if psi4_obasis is not None and \ - _is_normalized_properly(psi4_obasis, atcoords, coeffsa, coeffsb): + _is_normalized_properly(psi4_obasis, atcoords, coeffsa, coeffsb, threshold): lit.warn('Corrected for PSI4 < 1.0 errors in Molden/MKL file.') result['obasis'] = psi4_obasis return @@ -631,7 +631,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): # -- Turbomole turbom_obasis = _fix_obasis_turbomole(obasis) if turbom_obasis is not None and \ - _is_normalized_properly(turbom_obasis, atcoords, coeffsa, coeffsb): + _is_normalized_properly(turbom_obasis, atcoords, coeffsa, coeffsb, threshold): lit.warn('Corrected for Turbomole errors in Molden/MKL file.') result['obasis'] = turbom_obasis return @@ -647,7 +647,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): if _is_normalized_properly(obasis, atcoords, coeffsa_cfour, - coeffsb_cfour): + coeffsb_cfour, threshold): lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') result['obasis'] = obasis if result['mo'].kind == 'restricted': @@ -659,7 +659,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) - if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb): + if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb, threshold): lit.warn('Corrected for unnormalized contractions in Molden/MKL file.') result['obasis'] = normed_obasis return @@ -672,7 +672,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator): coeffsb_psi4 = None else: coeffsb_psi4 = coeffsb / psi4_coeff_correction[:, np.newaxis] - if _is_normalized_properly(normed_obasis, atcoords, coeffsa_psi4, coeffsb_psi4): + if _is_normalized_properly(normed_obasis, atcoords, coeffsa_psi4, coeffsb_psi4, threshold): lit.warn('Corrected for PSI4 <= 1.3.2 errors in Molden/MKL file.') result['obasis'] = normed_obasis if result['mo'].kind == 'restricted': From 6e2ee075dbecf93f04b985776801ef3ac63737bc Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Fri, 28 Apr 2023 16:06:42 -0400 Subject: [PATCH 079/144] Fix UnicodeDecodeError when pip installing on Windows --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 128d9e85b..d036dd09f 100755 --- a/setup.py +++ b/setup.py @@ -43,7 +43,7 @@ def get_version_info(): def get_readme(): """Load README.rst for display on PyPI.""" - with open('README.rst') as fhandle: + with open('README.rst', encoding="utf-8") as fhandle: return fhandle.read() From 25b72fabb6b6286555e204d3c7ce709d257db7c5 Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Mon, 1 May 2023 20:21:16 -0400 Subject: [PATCH 080/144] Fix Numpy AttributeError --- iodata/formats/extxyz.py | 10 +++++----- iodata/formats/wfx.py | 4 ++-- iodata/test/test_cube.py | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index 40dcb2088..7f6b24232 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -65,16 +65,16 @@ def _convert_title_value(value: str): else: # Do the same but return it as a numpy array try: - converted_value = np.array(list_of_splits, dtype=np.int) + converted_value = np.array(list_of_splits, dtype=int) except ValueError: try: - converted_value = np.array(list_of_splits, dtype=np.float) + 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=np.bool) + dtype=bool) except ValueError: - converted_value = np.array(list_of_splits, dtype=np.str) + converted_value = np.array(list_of_splits, dtype=str) return converted_value @@ -132,7 +132,7 @@ def _parse_title(title: str): # A dict of predefined iodata atrributes with their names and dtype convertion functions def load_cellvecs(word): - return np.array(word.split(), dtype=np.float).reshape([3, 3]) * angstrom + return np.array(word.split(), dtype=float).reshape([3, 3]) * angstrom iodata_attrs = {'energy': ('energy', float), 'Lattice': ('cellvecs', load_cellvecs), 'charge': ('charge', float)} diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index d0f862dc8..c1d786d90 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -129,9 +129,9 @@ def load_data_wfx(lit: LineIterator) -> dict: assert len(value) == 1 result[lbs_float[key]] = float(value[0]) elif key in lbs_afloat: - result[lbs_afloat[key]] = np.fromstring(" ".join(value), dtype=np.float, sep=" ") + result[lbs_afloat[key]] = np.fromstring(" ".join(value), dtype=float, sep=" ") elif key in lbs_aint: - result[lbs_aint[key]] = np.fromstring(" ".join(value), dtype=np.int, sep=" ") + result[lbs_aint[key]] = np.fromstring(" ".join(value), dtype=int, sep=" ") elif key in lbs_other: result[lbs_other[key]] = value else: diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index f8cd5f787..53df83794 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -41,11 +41,11 @@ def test_load_aelta(): 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=np.float) * 12 + [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=np.float) + [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) From 09508768c345e7b3c7e6e500e88663cbf7a02939 Mon Sep 17 00:00:00 2001 From: "Paul W. Ayers" <15361871+PaulWAyers@users.noreply.github.com> Date: Wed, 3 May 2023 10:01:39 -0400 Subject: [PATCH 081/144] Update ci.yml to use v3 of Actions When merging the last pull request on `iodata` I got an warning message: `Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: actions/checkout@v2, actions/setup-python@v2. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.` The fix seems to be to upgrade to version 3 of actions and I made the appropriate increment. --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d63316c72..72d79aadb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: # Tell Roberto to upload coverage results ROBERTO_UPLOAD_COVERAGE: 1 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 2 - name: Fetch base branch (usually master) @@ -41,7 +41,7 @@ jobs: git fetch origin ${GITHUB_BASE_REF} --depth=2 fi - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} architecture: x64 @@ -80,8 +80,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }} steps: - - uses: actions/checkout@v2 - - uses: actions/cache@v2 + - uses: actions/checkout@v3 + - uses: actions/cache@v3 with: path: | ~/miniconda3 From 6d673a75031101e2910a3af24ccb76c0c55808ba Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Fri, 13 Jan 2023 19:13:51 -0500 Subject: [PATCH 082/144] Fix DeprecationWarning: path is deprecated. DeprecationWarning: path is deprecated. Use files() instead. Refer to https://importlib-resources.readthedocs.io/en/latest/using.html#migrating-from-legacy for migration advice. --- iodata/test/common.py | 8 ++--- iodata/test/test_charmm.py | 6 ++-- iodata/test/test_chgcar.py | 8 ++--- iodata/test/test_cli.py | 10 +++---- iodata/test/test_cp2klog.py | 35 ++++++++++++---------- iodata/test/test_cube.py | 15 +++++----- iodata/test/test_extxyz.py | 11 +++---- iodata/test/test_fchk.py | 14 ++++----- iodata/test/test_fcidump.py | 15 +++++----- iodata/test/test_gamess.py | 7 ++--- iodata/test/test_gaussianinput.py | 29 +++++++++--------- iodata/test/test_gaussianlog.py | 6 ++-- iodata/test/test_gromacs.py | 8 ++--- iodata/test/test_inputs.py | 31 +++++++++++-------- iodata/test/test_iodata.py | 14 ++++----- iodata/test/test_json.py | 31 +++++++++---------- iodata/test/test_locpot.py | 7 +++-- iodata/test/test_mol2.py | 23 +++++++-------- iodata/test/test_molden.py | 49 ++++++++++++++++--------------- iodata/test/test_mwfn.py | 8 ++--- iodata/test/test_orcalog.py | 6 ++-- iodata/test/test_overlap.py | 29 +++++++++--------- iodata/test/test_pdb.py | 24 +++++++-------- iodata/test/test_poscar.py | 13 ++++---- iodata/test/test_qchemlog.py | 13 ++++---- iodata/test/test_sdf.py | 23 +++++++-------- iodata/test/test_wfn.py | 22 +++++++------- iodata/test/test_wfx.py | 25 ++++++++-------- iodata/test/test_xyz.py | 27 ++++++++--------- 29 files changed, 259 insertions(+), 258 deletions(-) diff --git a/iodata/test/common.py b/iodata/test/common.py index 661220750..f33e876b0 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -20,7 +20,6 @@ import os from contextlib import contextmanager - import numpy as np from numpy.testing import assert_equal, assert_allclose import pytest @@ -31,10 +30,9 @@ from ..utils import FileFormatWarning try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path - + from importlib.resources import as_file, files __all__ = ['compute_mulliken_charges', 'compute_1rdm', 'compare_mols', 'check_orthonormal', 'load_one_warning'] @@ -191,7 +189,7 @@ def load_one_warning(filename: str, fmt: str = None, match: str = None, **kwargs The instance of IOData with data loaded from the input files. """ - with path('iodata.test.data', filename) as fn: + with as_file(files("iodata.test.data").joinpath(filename)) as fn: if match is None: return load_one(str(fn), fmt, **kwargs) with pytest.warns(FileFormatWarning, match=match): diff --git a/iodata/test/test_charmm.py b/iodata/test/test_charmm.py index 56825eaef..179d4d404 100644 --- a/iodata/test/test_charmm.py +++ b/iodata/test/test_charmm.py @@ -26,14 +26,14 @@ from ..utils import angstrom, amu try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_crambin(): # test CHARMM crd file of crambin - with path('iodata.test.data', 'crambin.crd') as fn_crd: + with as_file(files("iodata.test.data").joinpath("crambin.crd")) as fn_crd: mol = load_one(str(fn_crd)) assert len(mol.title) == 125 assert mol.atcoords.shape == (648, 3) diff --git a/iodata/test/test_chgcar.py b/iodata/test/test_chgcar.py index 8074fb4a7..681b4874c 100644 --- a/iodata/test/test_chgcar.py +++ b/iodata/test/test_chgcar.py @@ -26,13 +26,13 @@ from ..utils import angstrom, volume try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_chgcar_oxygen(): - with path('iodata.test.data', 'CHGCAR.oxygen') as fn: + 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) @@ -46,7 +46,7 @@ def test_load_chgcar_oxygen(): def test_load_chgcar_water(): - with path('iodata.test.data', 'CHGCAR.water') as fn: + with as_file(files("iodata.test.data").joinpath("CHGCAR.water")) as fn: mol = load_one(str(fn)) assert mol.title == 'unknown system' assert_equal(mol.atnums, [8, 1, 1]) diff --git a/iodata/test/test_cli.py b/iodata/test/test_cli.py index 5988ce5c0..aced03ea2 100644 --- a/iodata/test/test_cli.py +++ b/iodata/test/test_cli.py @@ -18,25 +18,23 @@ # -- """Unit tests for iodata.__main__.""" - import os import functools import subprocess - from numpy.testing import assert_equal, assert_allclose from ..__main__ import convert from ..api import load_one, load_many try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def _check_convert_one(myconvert, tmpdir): outfn = os.path.join(tmpdir, 'tmp.xyz') - with path('iodata.test.data', 'hf_sto3g.fchk') as infn: + with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as infn: myconvert(infn, outfn) iodata = load_one(outfn) assert iodata.natom == 2 @@ -71,7 +69,7 @@ def myconvert(infn, outfn): def _check_convert_many(myconvert, tmpdir): outfn = os.path.join(tmpdir, 'tmp.xyz') - with path('iodata.test.data', 'peroxide_relaxed_scan.fchk') as infn: + with as_file(files("iodata.test.data").joinpath("peroxide_relaxed_scan.fchk")) as infn: myconvert(infn, outfn) trj = list(load_many(outfn)) assert len(trj) == 13 diff --git a/iodata/test/test_cp2klog.py b/iodata/test/test_cp2klog.py index 612538515..e3b9e2752 100644 --- a/iodata/test/test_cp2klog.py +++ b/iodata/test/test_cp2klog.py @@ -19,7 +19,6 @@ """Test iodata.formats.cp2klog module.""" import pytest - from numpy.testing import assert_equal, assert_allclose from .common import truncated_file, check_orthonormal @@ -28,13 +27,13 @@ from ..overlap import compute_overlap try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_atom_si_uks(): - with path('iodata.test.data', 'atom_si.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("atom_si.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [14]) assert_equal(mol.atcorenums, [4]) @@ -59,7 +58,7 @@ def test_atom_si_uks(): def test_atom_o_rks(): - with path('iodata.test.data', 'atom_om2.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("atom_om2.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [8]) assert_equal(mol.atcorenums, [6]) @@ -80,7 +79,7 @@ def test_atom_o_rks(): def test_carbon_gs_ae_contracted(): - with path('iodata.test.data', 'carbon_gs_ae_contracted.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("carbon_gs_ae_contracted.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) @@ -97,7 +96,8 @@ def test_carbon_gs_ae_contracted(): def test_carbon_gs_ae_uncontracted(): - with path('iodata.test.data', 'carbon_gs_ae_uncontracted.cp2k.out') as fn_out: + source = files("iodata.test.data").joinpath("carbon_gs_ae_uncontracted.cp2k.out") + with as_file(source) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) @@ -114,7 +114,7 @@ def test_carbon_gs_ae_uncontracted(): def test_carbon_gs_pp_contracted(): - with path('iodata.test.data', 'carbon_gs_pp_contracted.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("carbon_gs_pp_contracted.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) @@ -131,7 +131,8 @@ def test_carbon_gs_pp_contracted(): def test_carbon_gs_pp_uncontracted(): - with path('iodata.test.data', 'carbon_gs_pp_uncontracted.cp2k.out') as fn_out: + source = files("iodata.test.data").joinpath("carbon_gs_pp_uncontracted.cp2k.out") + with as_file(source) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) @@ -148,7 +149,7 @@ def test_carbon_gs_pp_uncontracted(): def test_carbon_sc_ae_contracted(): - with path('iodata.test.data', 'carbon_sc_ae_contracted.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("carbon_sc_ae_contracted.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) @@ -162,7 +163,8 @@ def test_carbon_sc_ae_contracted(): def test_carbon_sc_ae_uncontracted(): - with path('iodata.test.data', 'carbon_sc_ae_uncontracted.cp2k.out') as fn_out: + source = files("iodata.test.data").joinpath("carbon_sc_ae_uncontracted.cp2k.out") + with as_file(source) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [6]) @@ -176,7 +178,7 @@ def test_carbon_sc_ae_uncontracted(): def test_carbon_sc_pp_contracted(): - with path('iodata.test.data', 'carbon_sc_pp_contracted.cp2k.out') as fn_out: + with as_file(files("iodata.test.data").joinpath("carbon_sc_pp_contracted.cp2k.out")) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) @@ -190,7 +192,8 @@ def test_carbon_sc_pp_contracted(): def test_carbon_sc_pp_uncontracted(): - with path('iodata.test.data', 'carbon_sc_pp_uncontracted.cp2k.out') as fn_out: + source = files("iodata.test.data").joinpath("carbon_sc_pp_uncontracted.cp2k.out") + with as_file(source) as fn_out: mol = load_one(str(fn_out)) assert_equal(mol.atnums, [6]) assert_equal(mol.atcorenums, [4]) @@ -204,7 +207,8 @@ def test_carbon_sc_pp_uncontracted(): def test_errors(tmpdir): - with path('iodata.test.data', 'carbon_sc_pp_uncontracted.cp2k.out') as fn_test: + source = files("iodata.test.data").joinpath("carbon_sc_pp_uncontracted.cp2k.out") + with as_file(source) as fn_test: with truncated_file(fn_test, 0, 0, tmpdir) as fn: with pytest.raises(IOError): load_one(fn) @@ -217,7 +221,8 @@ def test_errors(tmpdir): with truncated_file(fn_test, 405, 10, tmpdir) as fn: with pytest.raises(IOError): load_one(fn) - with path('iodata.test.data', 'carbon_gs_pp_uncontracted.cp2k.out') as fn_test: + source = files("iodata.test.data").joinpath("carbon_gs_pp_uncontracted.cp2k.out") + with as_file(source) as fn_test: with truncated_file(fn_test, 456, 10, tmpdir) as fn: with pytest.raises(IOError): load_one(fn) diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 53df83794..834e2a4fe 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -19,20 +19,19 @@ # pylint: disable=unsubscriptable-object """Test iodata.formats.cube module.""" - import numpy as np from numpy.testing import assert_equal, assert_allclose from ..api import load_one, dump_one try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_aelta(): - with path('iodata.test.data', 'aelta.cube') as fn_cube: + 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_equal(mol.natom, 72) @@ -59,7 +58,7 @@ def test_load_aelta(): def test_load_dump_load_aelta(tmpdir): - with path('iodata.test.data', 'aelta.cube') as fn_cube1: + 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') @@ -98,7 +97,7 @@ def test_load_dump_load_aelta(tmpdir): def test_load_dump_h2o_5points(tmpdir): # load cube file - with path('iodata.test.data', 'cubegen_h2o_5points.cube') as fn_cube1: + 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') @@ -112,7 +111,7 @@ def test_load_dump_h2o_5points(tmpdir): def test_load_dump_ch4_6points(tmpdir): # load cube file - with path('iodata.test.data', 'cubegen_ch4_6points.cube') as fn_cube1: + 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') @@ -126,7 +125,7 @@ def test_load_dump_ch4_6points(tmpdir): def test_load_dump_nh3_7points(tmpdir): # load cube file - with path('iodata.test.data', 'cubegen_nh3_7points.cube') as fn_cube1: + 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') diff --git a/iodata/test/test_extxyz.py b/iodata/test/test_extxyz.py index 5e6ac1396..04051e670 100644 --- a/iodata/test/test_extxyz.py +++ b/iodata/test/test_extxyz.py @@ -23,14 +23,15 @@ from ..api import load_one, load_many from ..utils import angstrom + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_fcc_extended(): - with path('iodata.test.data', 'al_fcc.xyz') as fn_xyz: + 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') assert isinstance(mol.energy, float) @@ -51,7 +52,7 @@ def test_load_fcc_extended(): def test_load_mgo(): - with path('iodata.test.data', 'mgo.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("mgo.xyz")) as fn_xyz: 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) @@ -62,7 +63,7 @@ def test_load_mgo(): def test_load_many_extended(): - with path('iodata.test.data', 'water_extended_trajectory.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_extended_trajectory.xyz")) as fn_xyz: mols = list(load_many(str(fn_xyz), fmt='extxyz')) assert len(mols) == 3 assert 'some_label' in mols[0].extra diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index 06a4486b9..87dd95e3d 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -19,9 +19,7 @@ # pylint: disable=unsubscriptable-object,no-member """Test iodata.formats.fchk module.""" - import os - import numpy as np from numpy.testing import assert_equal, assert_allclose @@ -35,20 +33,20 @@ from .test_molekel import compare_mols_diff_formats try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_fchk_nonexistent(): with pytest.raises(IOError): - with path('iodata.test.data', 'fubar_crap.fchk') as fn: + with as_file(files("iodata.test.data").joinpath("fubar_crap.fchk")) as fn: load_one(str(fn)) def load_fchk_helper(fn_fchk): """Load a testing fchk file with iodata.iodata.load_one.""" - with path('iodata.test.data', fn_fchk) as fn: + with as_file(files("iodata.test.data").joinpath(fn_fchk)) as fn: return load_one(fn) @@ -389,7 +387,7 @@ def test_load_monosilicic_acid_hf_lan(): def load_fchk_trj_helper(fn_fchk): """Load a trajectory from a testing fchk file with iodata.iodata.load_many.""" - with path('iodata.test.data', fn_fchk) as fn: + with as_file(files("iodata.test.data").joinpath(fn_fchk)) as fn: return list(load_many(fn)) @@ -543,7 +541,7 @@ def test_load_nbasis_indep(tmpdir): 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 path('iodata.test.data', 'li2_g09_nbasis_indep.fchk') as fnin: + with as_file(files("iodata.test.data").joinpath("li2_g09_nbasis_indep.fchk")) as fnin: fnout = os.path.join(tmpdir, 'tmpg03.fchk') with open(fnin) as fin, open(fnout, "w") as fout: for line in fin: diff --git a/iodata/test/test_fcidump.py b/iodata/test/test_fcidump.py index 810ac02a7..2623f03e1 100644 --- a/iodata/test/test_fcidump.py +++ b/iodata/test/test_fcidump.py @@ -19,20 +19,19 @@ """Test iodata.formats.fcidump module.""" import os - import numpy as np from numpy.testing import assert_equal, assert_allclose from ..api import load_one, dump_one try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_fcidump_psi4_h2(): - with path('iodata.test.data', 'FCIDUMP.psi4.h2') as fn: + 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_equal(mol.nelec, 2) @@ -59,7 +58,7 @@ def test_load_fcidump_psi4_h2(): def test_load_fcidump_molpro_h2(): - with path('iodata.test.data', 'FCIDUMP.molpro.h2') as fn: + 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_equal(mol.nelec, 2) @@ -87,13 +86,13 @@ def test_load_fcidump_molpro_h2(): def test_dump_load_fcidimp_consistency_ao(tmpdir): # Setup IOData - with path('iodata.test.data', 'water.xyz') as fn: + with as_file(files("iodata.test.data").joinpath("water.xyz")) as fn: mol0 = load_one(str(fn)) mol0.nelec = 10 mol0.spinpol = 0 - with path('iodata.test.data', 'psi4_h2_one.npy') as fn: + with as_file(files("iodata.test.data").joinpath("psi4_h2_one.npy")) as fn: mol0.one_ints = {'core_mo': np.load(str(fn))} - with path('iodata.test.data', 'psi4_h2_two.npy') as fn: + with as_file(files("iodata.test.data").joinpath("psi4_h2_two.npy")) as fn: mol0.two_ints = {'two_mo': np.load(str(fn))} # Dump to a file and load it again diff --git a/iodata/test/test_gamess.py b/iodata/test/test_gamess.py index 4c48eeaf3..e0fb633cd 100644 --- a/iodata/test/test_gamess.py +++ b/iodata/test/test_gamess.py @@ -18,20 +18,19 @@ # -- """Test iodata.formats.gamess module.""" - from numpy.testing import assert_equal, assert_allclose from ..api import load_one from ..utils import angstrom try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_one_gamess_punch(): - with path('iodata.test.data', 'PCGamess_PUNCH.dat') as f: + with as_file(files("iodata.test.data").joinpath("PCGamess_PUNCH.dat")) as f: data = load_one(str(f)) N = len(["CL", "H", "H", "H", "H", "F", "F", "F", "F", "H", "F"]) assert data.title == "Simple example sample optimization with Hessian output for Toon" diff --git a/iodata/test/test_gaussianinput.py b/iodata/test/test_gaussianinput.py index 0732b69c8..6ed5031e9 100644 --- a/iodata/test/test_gaussianinput.py +++ b/iodata/test/test_gaussianinput.py @@ -23,52 +23,53 @@ from ..api import load_one from ..utils import angstrom + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_water_com(): # test .com with Link 0 section - with path('iodata.test.data', 'water.com') as fn_xyz: - mol = load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water.com")) as fn: + mol = load_one(str(fn)) check_water(mol, 'water') def test_load_water_gjf(): # test .com without Link 0 section - with path('iodata.test.data', 'water.gjf') as fn_xyz: - mol = load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water.gjf")) as fn: + mol = load_one(str(fn)) check_water(mol, 'water') def test_load_multi_link(): # test .com with multiple #link 0 contents - with path('iodata.test.data', 'water_multi_link.com') as fn_xyz: - mol = load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water_multi_link.com")) as fn: + mol = load_one(str(fn)) check_water(mol, 'water') def test_load_multi_route(): # test .com with multiple route contents - with path('iodata.test.data', 'water_multi_route.com') as fn_xyz: - mol = load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water_multi_route.com")) as fn: + mol = load_one(str(fn)) check_water(mol, 'water') def test_load_multi_title(): # test .com with multiple title and concatenate - with path('iodata.test.data', 'water_multi_title.com') as fn_xyz: - mol = load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water_multi_title.com")) as fn: + mol = load_one(str(fn)) check_water(mol, 'water water') def test_load_error(): # test error raises when loading .com with z-matrix with assert_raises(ValueError): - with path('iodata.test.data', 'water_z.com') as fn_xyz: - load_one(str(fn_xyz)) + with as_file(files("iodata.test.data").joinpath("water_z.com")) as fn: + load_one(str(fn)) def check_water(mol, title): diff --git a/iodata/test/test_gaussianlog.py b/iodata/test/test_gaussianlog.py index 4c0850e81..b5551211a 100644 --- a/iodata/test/test_gaussianlog.py +++ b/iodata/test/test_gaussianlog.py @@ -23,14 +23,14 @@ from ..api import load_one try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def load_log_helper(fn_log): """Load a testing Gaussian log file with iodata.load_one.""" - with path('iodata.test.data', fn_log) as fn: + with as_file(files("iodata.test.data").joinpath(fn_log)) as fn: return load_one(fn) diff --git a/iodata/test/test_gromacs.py b/iodata/test/test_gromacs.py index b5d174d9b..9d0382c5e 100644 --- a/iodata/test/test_gromacs.py +++ b/iodata/test/test_gromacs.py @@ -25,14 +25,14 @@ from ..utils import nanometer, picosecond try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_water(): # test gro file of one water - with path('iodata.test.data', 'water.gro') as fn_gro: + with as_file(files("iodata.test.data").joinpath("water.gro")) as fn_gro: mol = load_one(str(fn_gro)) check_water(mol) @@ -52,7 +52,7 @@ def check_water(mol): def test_load_many(): - with path('iodata.test.data', 'water2.gro') as fn_gro: + 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 diff --git a/iodata/test/test_inputs.py b/iodata/test/test_inputs.py index 1d3660f29..8c8c03dbc 100644 --- a/iodata/test/test_inputs.py +++ b/iodata/test/test_inputs.py @@ -18,7 +18,6 @@ # -- """Test iodata.inputs module.""" - import os import numpy as np @@ -27,9 +26,9 @@ from ..api import load_one, write_input try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def check_load_input_and_compare(fname: str, fname_expected: str): @@ -52,7 +51,7 @@ def check_load_input_and_compare(fname: str, fname_expected: str): def test_input_gaussian_from_xyz(tmpdir): # load geometry from xyz file & add level of theory & basis set - with path('iodata.test.data', 'water_number.xyz') as fn: + with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn: mol = load_one(fn) mol.nelec = 10 mol.lot = 'ub3lyp' @@ -82,7 +81,8 @@ def test_input_gaussian_from_xyz(tmpdir): """ write_input(mol, fname, fmt='gaussian', template=template, extra_cmd="nosymmetry") # compare saved input to expected input - with path('iodata.test.data', 'input_gaussian_h2o_opt_ub3lyp.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_gaussian_h2o_opt_ub3lyp.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) @@ -95,25 +95,27 @@ def test_input_gaussian_from_iodata(tmpdir): fname = os.path.join(tmpdir, 'input_from_iodata.com') write_input(mol, fname, fmt='gaussian') # compare saved input to expected input - with path('iodata.test.data', 'input_gaussian_hcl_anion_opt_hf.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_gaussian_hcl_anion_opt_hf.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) def test_input_gaussian_from_fchk(tmpdir): # load fchk - with path('iodata.test.data', 'water_hfs_321g.fchk') as fn: + 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') # compare saved input to expected input - with path('iodata.test.data', 'input_gaussian_hcl_sp_rhf.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_gaussian_hcl_sp_rhf.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) def test_input_orca_from_xyz(tmpdir): # load geometry from xyz file & add level of theory & basis set - with path('iodata.test.data', 'water_number.xyz') as fn: + with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn: mol = load_one(fn) mol.nelec = 10 mol.lot = 'B3LYP' @@ -138,7 +140,8 @@ def test_input_orca_from_xyz(tmpdir): grid_stuff = "Grid4 TightSCF NOFINALGRID" write_input(mol, fname, fmt='orca', template=template, grid_stuff=grid_stuff) # compare saved input to expected input - with path('iodata.test.data', 'input_orca_h2o_sp_b3lyp.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_orca_h2o_sp_b3lyp.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) @@ -151,17 +154,19 @@ def test_input_orca_from_iodata(tmpdir): fname = os.path.join(tmpdir, 'input_from_iodata.com') write_input(mol, fname, fmt='orca') # compare saved input to expected input - with path('iodata.test.data', 'input_orca_hcl_anion_opt_hf.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_orca_hcl_anion_opt_hf.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) def test_input_orca_from_molden(tmpdir): # load orca molden - with path('iodata.test.data', 'nh3_orca.molden') as fn: + 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') # compare saved input to expected input - with path('iodata.test.data', 'input_orca_nh3_sp_hf.txt') as fname_expected: + source = files("iodata.test.data").joinpath("input_orca_nh3_sp_hf.txt") + with as_file(source) as fname_expected: check_load_input_and_compare(fname, fname_expected) diff --git a/iodata/test/test_iodata.py b/iodata/test/test_iodata.py index 8718de69c..46ee887b1 100644 --- a/iodata/test/test_iodata.py +++ b/iodata/test/test_iodata.py @@ -18,7 +18,6 @@ # -- """Test iodata.iodata module.""" - import numpy as np from numpy.testing import assert_allclose, assert_equal import pytest @@ -26,10 +25,11 @@ from .common import compute_1rdm from ..api import load_one, IOData from ..overlap import compute_overlap + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_typecheck(): @@ -62,7 +62,7 @@ def test_unknown_format(): def test_dm_water_sto3g_hf(): - with path('iodata.test.data', 'water_sto3g_hf_g03.fchk') as fn_fchk: + 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) @@ -71,7 +71,7 @@ def test_dm_water_sto3g_hf(): def test_dm_lih_sto3g_hf(): - with path('iodata.test.data', 'li_h_3-21G_hf_g09.fchk') as fn_fchk: + 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'] @@ -88,7 +88,7 @@ def test_dm_lih_sto3g_hf(): def test_dm_ch3_rohf_g03(): - with path('iodata.test.data', 'ch3_rohf_sto3g_g03.fchk') as fn_fchk: + with as_file(files("iodata.test.data").joinpath("ch3_rohf_sto3g_g03.fchk")) as fn_fchk: mol = load_one(str(fn_fchk)) olp = compute_overlap(mol.obasis, mol.atcoords) dm = compute_1rdm(mol) @@ -316,7 +316,7 @@ def test_derived1(): # pylint: disable=protected-access # When loading a file with molecular orbitals, nelec, charge and spinpol are # derived from the mo object: - with path('iodata.test.data', 'ch3_rohf_sto3g_g03.fchk') as fn_fchk: + with as_file(files("iodata.test.data").joinpath("ch3_rohf_sto3g_g03.fchk")) as fn_fchk: mol = load_one(str(fn_fchk)) assert mol.nelec == mol.mo.nelec assert mol.charge == mol.atcorenums.sum() - mol.mo.nelec diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index a2d243f1b..65bda6726 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -27,11 +27,11 @@ from ..api import dump_one, load_one from ..utils import FileFormatError, FileFormatWarning - try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files + # Tests for qcschema_molecule # GEOMS: dict of str: np.ndarray(N, 3) @@ -64,7 +64,7 @@ @pytest.mark.parametrize("filename, atnums, charge, spinpol, geometry, nwarn", MOL_FILES) def test_qcschema_molecule(filename, atnums, charge, spinpol, geometry, nwarn): """Test qcschema_molecule parsing using manually generated files.""" - with path("iodata.test.data", filename) as qcschema_molecule: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: if nwarn == 0: mol = load_one(str(qcschema_molecule)) else: @@ -108,7 +108,7 @@ def test_qcschema_molecule(filename, atnums, charge, spinpol, geometry, nwarn): @pytest.mark.parametrize("filename, atnums, charge, spinpol, nwarn", MOLSSI_MOL_FILES) def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): """Test qcschema_molecule parsing using MolSSI-sourced files.""" - with path("iodata.test.data", filename) as qcschema_molecule: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(qcschema_molecule)) @@ -136,7 +136,7 @@ def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): @pytest.mark.parametrize("filename, unparsed_dict", PASSTHROUGH_MOL_FILES) def test_passthrough_qcschema_molecule(filename, unparsed_dict): """Test qcschema_molecule parsing for passthrough of unparsed keys.""" - with path("iodata.test.data", filename) as qcschema_molecule: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(qcschema_molecule)) @@ -169,7 +169,7 @@ def _check_provenance(mol1, mol2): @pytest.mark.parametrize("filename, nwarn", INOUT_MOL_FILES) def test_inout_qcschema_molecule(tmpdir, filename, nwarn): """Test that loading and dumping qcschema_molecule files retains all data.""" - with path("iodata.test.data", filename) as qcschema_molecule: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: if nwarn == 0: mol = load_one(str(qcschema_molecule)) else: @@ -202,7 +202,7 @@ def test_inout_qcschema_molecule(tmpdir, filename, nwarn): @pytest.mark.parametrize("filename", INOUT_MOLSSI_MOL_FILES) def test_inout_molssi_qcschema_molecule(tmpdir, filename): """Test that loading and dumping qcschema_molecule files retains all relevant data.""" - with path("iodata.test.data", filename) as qcschema_molecule: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(qcschema_molecule)) mol1_preproc = json.loads(qcschema_molecule.read_bytes()) @@ -238,7 +238,8 @@ def test_inout_molssi_qcschema_molecule(tmpdir, filename): def test_ghost(tmpdir): - with path("iodata.test.data", "water_cluster_ghost.json") as qcschema_molecule: + source = files("iodata.test.data").joinpath("water_cluster_ghost.json") + 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') @@ -262,7 +263,7 @@ def test_ghost(tmpdir): "filename, explicit_basis, lot, obasis_name, run_type, geometry", INPUT_FILES ) def test_qcschema_input(filename, explicit_basis, lot, obasis_name, run_type, geometry): - with path('iodata.test.data', filename) as qcschema_input: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: try: mol = load_one(str(qcschema_input)) assert mol.lot == lot @@ -288,7 +289,7 @@ def test_qcschema_input(filename, explicit_basis, lot, obasis_name, run_type, ge @pytest.mark.parametrize("filename, unparsed_dict, location", PASSTHROUGH_INPUT_FILES) def test_passthrough_qcschema_input(filename, unparsed_dict, location): """Test qcschema_molecule parsing for passthrough of unparsed keys.""" - with path("iodata.test.data", filename) as qcschema_input: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: mol = load_one(str(qcschema_input)) assert mol.extra[location]["unparsed"] == unparsed_dict @@ -307,7 +308,7 @@ def test_passthrough_qcschema_input(filename, unparsed_dict, location): @pytest.mark.parametrize("filename, nwarn", INOUT_INPUT_FILES) def test_inout_qcschema_input(tmpdir, filename, nwarn): """Test that loading and dumping qcschema_molecule files retains all data.""" - with path("iodata.test.data", filename) as qcschema_input: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: if nwarn == 0: mol = load_one(str(qcschema_input)) else: @@ -345,7 +346,7 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): @pytest.mark.parametrize("filename, lot, obasis_name, run_type, nwarn", OUTPUT_FILES) def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): - with path("iodata.test.data", filename) as qcschema_output: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_output: if nwarn == 0: mol = load_one(str(qcschema_output)) else: @@ -370,7 +371,7 @@ def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): @pytest.mark.parametrize("filename, error", BAD_OUTPUT_FILES) def test_bad_qcschema_files(filename, error): # FIXME: these will move - with path('iodata.test.data', filename) as qcschema_input: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: with pytest.raises(error): load_one(str(qcschema_input)) @@ -384,7 +385,7 @@ def test_bad_qcschema_files(filename, error): @pytest.mark.parametrize("filename", INOUT_OUTPUT_FILES) def test_inout_qcschema_output(tmpdir, filename): """Test that loading and dumping qcschema_molecule files retains all data.""" - with path("iodata.test.data", filename) as qcschema_input: + with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: mol = load_one(str(qcschema_input)) mol1 = json.loads(qcschema_input.read_bytes()) diff --git a/iodata/test/test_locpot.py b/iodata/test/test_locpot.py index 7a4de1530..55e4e8f63 100644 --- a/iodata/test/test_locpot.py +++ b/iodata/test/test_locpot.py @@ -23,14 +23,15 @@ from ..api import load_one from ..utils import angstrom, electronvolt, volume + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_locpot_oxygen(): - with path('iodata.test.data', 'LOCPOT.oxygen') as fn: + 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_equal(mol.atnums[0], 8) diff --git a/iodata/test/test_mol2.py b/iodata/test/test_mol2.py index 707af6c81..57d33957f 100644 --- a/iodata/test/test_mol2.py +++ b/iodata/test/test_mol2.py @@ -19,7 +19,6 @@ """Test iodata.formats.mol2 module.""" import os - import pytest from numpy.testing import assert_equal, assert_allclose @@ -29,21 +28,21 @@ from ..periodic import bond2num try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_mol2_load_one(): # test mol2 one structure - with path('iodata.test.data', 'caffeine.mol2') as fn_mol: + with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_mol: mol = load_one(str(fn_mol)) check_example(mol) def test_mol2_formaterror(tmpdir): # test if mol2 file has the wrong ending - with path('iodata.test.data', 'caffeine.mol2') as fn_test: + with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_test: with truncated_file(fn_test, 2, 0, tmpdir) as fn: with pytest.raises(IOError): load_one(str(fn)) @@ -51,13 +50,13 @@ def test_mol2_formaterror(tmpdir): def test_mol2_symbol(): # test mol2 files with element symbols with two characters - with path('iodata.test.data', 'silioh3.mol2') as fn_mol: + with as_file(files("iodata.test.data").joinpath("silioh3.mol2")) as fn_mol: mol = load_one(str(fn_mol)) assert_equal(mol.atnums, [14, 3, 8, 1, 8, 1, 8, 1]) def test_bondtypes_benzene(): - with path('iodata.test.data', 'benzene.mol2') as fn_test: + with as_file(files("iodata.test.data").joinpath("benzene.mol2")) as fn_test: mol = load_one(str(fn_test)) assert_equal(mol.bonds[:, 2], [bond2num["ar"]] * 6 + [bond2num["1"]] * 6) @@ -100,14 +99,14 @@ def check_load_dump_consistency(tmpdir, fn): def test_load_dump_consistency(tmpdir): - with path('iodata.test.data', 'caffeine.mol2') as fn_mol2: + with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_mol2: check_load_dump_consistency(tmpdir, fn_mol2) - with path('iodata.test.data', 'benzene.mol2') as fn_mol2: + with as_file(files("iodata.test.data").joinpath("benzene.mol2")) as fn_mol2: check_load_dump_consistency(tmpdir, fn_mol2) def test_load_many(): - with path('iodata.test.data', 'caffeine.mol2') as fn_mol2: + with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_mol2: mols = list(load_many(str(fn_mol2))) assert len(mols) == 2 check_example(mols[0]) @@ -118,7 +117,7 @@ def test_load_many(): def test_load_dump_many_consistency(tmpdir): - with path('iodata.test.data', 'caffeine.mol2') as fn_mol2: + 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') @@ -133,7 +132,7 @@ def test_load_dump_many_consistency(tmpdir): def test_load_dump_wrong_bond_num(tmpdir): - with path('iodata.test.data', 'silioh3.mol2') as fn_mol: + 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') diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 49b693ef1..c5c95e2f3 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -20,7 +20,6 @@ """Test iodata.formats.molden module.""" import os - import attr import numpy as np from numpy.testing import assert_allclose, assert_equal @@ -33,15 +32,14 @@ from ..overlap import compute_overlap, OVERLAP_CONVENTIONS from ..utils import LineIterator, angstrom, FileFormatWarning - try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_molden_li2_orca(): - with path('iodata.test.data', 'li2.molden.input') as fn_molden: + with as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -71,7 +69,7 @@ def test_load_molden_li2_orca(): def test_load_molden_h2o_orca(): - with path('iodata.test.data', 'h2o.molden.input') as fn_molden: + with as_file(files("iodata.test.data").joinpath("h2o.molden.input")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -99,7 +97,7 @@ def test_load_molden_h2o_orca(): def test_load_molden_nh3_molden_pure(): # The file tested here is created with molden. It should be read in # properly without altering normalization and sign conventions. - with path('iodata.test.data', 'nh3_molden_pure.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_molden_pure.molden")) as fn_molden: mol = load_one(str(fn_molden)) # Check geometry assert_equal(mol.atnums, [7, 1, 1, 1]) @@ -118,7 +116,7 @@ def test_load_molden_nh3_molden_pure(): def test_load_molden_low_nh3_molden_cart(): - with path('iodata.test.data', 'nh3_molden_cart.molden') as fn_molden: + 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'] @@ -186,7 +184,7 @@ def test_load_molden_low_nh3_molden_cart(): def test_load_molden_nh3_molden_cart(): # The file tested here is created with molden. It should be read in # properly without altering normalization and sign conventions. - with path('iodata.test.data', 'nh3_molden_cart.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_molden_cart.molden")) as fn_molden: mol = load_one(str(fn_molden)) # Check normalization @@ -216,7 +214,7 @@ def test_load_molden_cfour(): 'h2o_ccpvdz_cfour.molden'] for i in file_list: - with path('iodata.test.data', i) as fn_molden: + with as_file(files("iodata.test.data").joinpath(i)) as fn_molden: print(str(fn_molden)) mol = load_one(str(fn_molden)) # Check normalization @@ -228,7 +226,7 @@ def test_load_molden_cfour(): def test_load_molden_nh3_orca(): # The file tested here is created with ORCA. It should be read in # properly by altering normalization and sign conventions. - with path('iodata.test.data', 'nh3_orca.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_orca.molden")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -248,7 +246,7 @@ def test_load_molden_nh3_orca(): def test_load_molden_nh3_psi4(): # The file tested here is created with PSI4 (pre 1.0). It should be read in # properly by altering normalization conventions. - with path('iodata.test.data', 'nh3_psi4.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_psi4.molden")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -268,7 +266,7 @@ def test_load_molden_nh3_psi4(): def test_load_molden_nh3_psi4_1(): # The file tested here is created with PSI4 (version 1.0). # It should be read in properly by renormalizing the contractions. - with path('iodata.test.data', 'nh3_psi4_1.0.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_psi4_1.0.molden")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -290,7 +288,8 @@ def test_load_molden_high_am_psi4(case): # The file tested here is created with PSI4 1.3.2. # This is a special case because it contains higher angular momenta than # officially supported by the Molden format. Most virtual orbitals were removed. - with path('iodata.test.data', f'psi4_{case}_cc_pvqz_pure.molden') as fn_molden: + source = files("iodata.test.data").joinpath(f"psi4_{case}_cc_pvqz_pure.molden") + with as_file(source) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -311,7 +310,8 @@ def test_load_molden_high_am_orca(case): # The file tested here is created with ORCA. # This is a special case because it contains higher angular momenta than # officially supported by the Molden format. Most virtual orbitals were removed. - with path('iodata.test.data', f'orca_{case}_cc_pvqz_pure.molden') as fn_molden: + source = files("iodata.test.data").joinpath(f"orca_{case}_cc_pvqz_pure.molden") + with as_file(source) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -324,7 +324,7 @@ def test_load_molden_high_am_orca(case): def test_load_molden_he2_ghost_psi4_1(): # The file tested here is created with PSI4 (version 1.0). - with path('iodata.test.data', 'he2_ghost_psi4_1.0.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("he2_ghost_psi4_1.0.molden")) as fn_molden: mol = load_one(str(fn_molden)) np.testing.assert_equal(mol.atcorenums, np.array([0.0, 2.0])) @@ -342,7 +342,8 @@ def test_load_molden_he2_ghost_psi4_1(): def test_load_molden_h2o_6_31g_d_cart_psi4(): # The file tested here is created with PSI4 1.3.2. It should be read in # properly after fixing for errors in AO normalization conventions. - with path('iodata.test.data', 'h2o_psi4_1.3.2_6-31G_d_cart.molden') as fn_molden: + source = files("iodata.test.data").joinpath("h2o_psi4_1.3.2_6-31G_d_cart.molden") + with as_file(source) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -362,7 +363,8 @@ def test_load_molden_h2o_6_31g_d_cart_psi4(): def test_load_molden_nh3_aug_cc_pvqz_cart_psi4(): # The file tested here is created with PSI4 1.3.2. It should be read in # properly after fixing for errors in AO normalization conventions. - with path('iodata.test.data', 'nh3_psi4_1.3.2_aug_cc_pvqz_cart.molden') as fn_molden: + source = files("iodata.test.data").joinpath("nh3_psi4_1.3.2_aug_cc_pvqz_cart.molden") + with as_file(source) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -381,7 +383,7 @@ def test_load_molden_nh3_aug_cc_pvqz_cart_psi4(): def test_load_molden_nh3_molpro2012(): # The file tested here is created with MOLPRO2012. - with path('iodata.test.data', 'nh3_molpro2012.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_molpro2012.molden")) as fn_molden: mol = load_one(str(fn_molden)) # Check normalization @@ -397,7 +399,8 @@ def test_load_molden_nh3_molpro2012(): def test_load_molden_neon_turbomole(): # The file tested here is created with Turbomole 7.1. - with path('iodata.test.data', 'neon_turbomole_def2-qzvp.molden') as fn_molden: + source = files("iodata.test.data").joinpath("neon_turbomole_def2-qzvp.molden") + with as_file(source) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -414,7 +417,7 @@ def test_load_molden_neon_turbomole(): def test_load_molden_nh3_turbomole(): # The file tested here is created with Turbomole 7.1 - with path('iodata.test.data', 'nh3_turbomole.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("nh3_turbomole.molden")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -434,7 +437,7 @@ def test_load_molden_nh3_turbomole(): def test_load_molden_f(): - with path('iodata.test.data', 'F.molden') as fn_molden: + with as_file(files("iodata.test.data").joinpath("F.molden")) as fn_molden: with pytest.warns(FileFormatWarning) as record: mol = load_one(str(fn_molden)) assert len(record) == 1 @@ -464,7 +467,7 @@ def test_load_molden_f(): ("ch3_rohf_sto3g_g03.fchk", None), ]) def test_load_dump_consistency(tmpdir, fn, match): - with path('iodata.test.data', fn) as file_name: + with as_file(files("iodata.test.data").joinpath(fn)) as file_name: if match is None: mol1 = load_one(str(file_name)) else: diff --git a/iodata/test/test_mwfn.py b/iodata/test/test_mwfn.py index c97226c09..a72657ecf 100644 --- a/iodata/test/test_mwfn.py +++ b/iodata/test/test_mwfn.py @@ -18,21 +18,21 @@ # -- """Test iodata.formats.mwfn module.""" - import numpy as np from numpy.testing import assert_equal, assert_allclose + from ..api import load_one from ..overlap import compute_overlap try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def load_helper(fn): """Load a test file with iodata.iodata.load_one.""" - with path('iodata.test.data', fn) as absfn: + with as_file(files("iodata.test.data").joinpath(fn)) as absfn: return load_one(absfn) diff --git a/iodata/test/test_orcalog.py b/iodata/test/test_orcalog.py index 90cb8605b..7c3aa59d4 100644 --- a/iodata/test/test_orcalog.py +++ b/iodata/test/test_orcalog.py @@ -26,13 +26,13 @@ from ..utils import angstrom try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_water_number(): - with path('iodata.test.data', 'water_orca.out') as fn: + with as_file(files("iodata.test.data").joinpath("water_orca.out")) as fn: mol = load_one(fn) # Test atomic numbers and number of atoms assert mol.natom == 3 diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index 20cc30c4b..9091eae23 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -19,7 +19,6 @@ """Test iodata.overlap & iodata.overlap_accel modules.""" import itertools - import attr import numpy as np from numpy.testing import assert_allclose @@ -30,9 +29,9 @@ from ..overlap import compute_overlap, OVERLAP_CONVENTIONS try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_normalization_basics_segmented(): @@ -56,27 +55,27 @@ def test_normalization_basics_generalized(): def test_load_fchk_hf_sto3g_num(): - with path('iodata.test.data', 'load_fchk_hf_sto3g_num.npy') as fn_npy: + with as_file(files("iodata.test.data").joinpath("load_fchk_hf_sto3g_num.npy")) as fn_npy: ref = np.load(str(fn_npy)) - with path('iodata.test.data', 'hf_sto3g.fchk') as fn_fchk: + 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) def test_load_fchk_o2_cc_pvtz_pure_num(): - with path('iodata.test.data', - 'load_fchk_o2_cc_pvtz_pure_num.npy') as fn_npy: + source = files("iodata.test.data").joinpath("load_fchk_o2_cc_pvtz_pure_num.npy") + with as_file(source) as fn_npy: ref = np.load(str(fn_npy)) - with path('iodata.test.data', 'o2_cc_pvtz_pure.fchk') as fn_fchk: + 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) def test_load_fchk_o2_cc_pvtz_cart_num(): - with path('iodata.test.data', - 'load_fchk_o2_cc_pvtz_cart_num.npy') as fn_npy: + source = files("iodata.test.data").joinpath("load_fchk_o2_cc_pvtz_cart_num.npy") + with as_file(source) as fn_npy: ref = np.load(str(fn_npy)) - with path('iodata.test.data', 'o2_cc_pvtz_cart.fchk') as fn_fchk: + 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) @@ -93,7 +92,7 @@ def test_overlap_l1(): def test_overlap_two_basis_exceptions(): - with path('iodata.test.data', 'hf_sto3g.fchk') as fn_fchk: + with as_file(files("iodata.test.data").joinpath("hf_sto3g.fchk")) as fn_fchk: data = load_one(fn_fchk) with pytest.raises(TypeError): compute_overlap(data.obasis, data.atcoords, data.obasis, None) @@ -111,7 +110,7 @@ def test_overlap_two_basis_exceptions(): @pytest.mark.parametrize("fn", FNS_TWO_BASIS) def test_overlap_two_basis_same(fn): - with path('iodata.test.data', fn) as pth: + with as_file(files("iodata.test.data").joinpath(fn)) as pth: mol = load_one(pth) olp_a = compute_overlap(mol.obasis, mol.atcoords, mol.obasis, mol.atcoords) olp_b = compute_overlap(mol.obasis, mol.atcoords) @@ -120,9 +119,9 @@ def test_overlap_two_basis_same(fn): @pytest.mark.parametrize("fn0,fn1", itertools.combinations_with_replacement(FNS_TWO_BASIS, 2)) def test_overlap_two_basis_different(fn0, fn1): - with path('iodata.test.data', fn0) as pth0: + with as_file(files("iodata.test.data").joinpath(fn0)) as pth0: mol0 = load_one(pth0) - with path('iodata.test.data', fn1) as pth1: + with as_file(files("iodata.test.data").joinpath(fn1)) as pth1: mol1 = load_one(pth1) # Direct computation of the off-diagonal block. olp_a = compute_overlap(mol0.obasis, mol0.atcoords, mol1.obasis, mol1.atcoords) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index c1b33eb3e..292455cd4 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -19,30 +19,30 @@ """Test iodata.formats.pdb module.""" import os - import numpy as np from numpy.testing import assert_equal, assert_allclose import pytest from ..api import load_one, load_many, dump_one, dump_many from ..utils import angstrom, FileFormatWarning + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files @pytest.mark.parametrize("case", ["single", "single_model"]) def test_load_water(case): # test pdb of water - with path('iodata.test.data', 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 path('iodata.test.data', '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) @@ -95,7 +95,7 @@ def check_load_dump_consistency(tmpdir, fn): "2bcw.pdb", ]) def test_load_dump_consistency(fn_base, tmpdir): - with path('iodata.test.data', fn_base) as fn_pdb: + with as_file(files("iodata.test.data").joinpath(fn_base)) as fn_pdb: check_load_dump_consistency(tmpdir, fn_pdb) @@ -124,13 +124,13 @@ def check_load_dump_xyz_consistency(tmpdir, fn): def test_load_dump_xyz_consistency(tmpdir): - with path('iodata.test.data', 'water.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water.xyz")) as fn_xyz: check_load_dump_xyz_consistency(tmpdir, fn_xyz) def test_load_peptide_2luv(): # test pdb of small peptide - with path('iodata.test.data', '2luv.pdb') as fn_pdb: + with as_file(files("iodata.test.data").joinpath("2luv.pdb")) as fn_pdb: mol = load_one(str(fn_pdb)) assert mol.title.startswith("INTEGRIN") assert_equal(len(mol.atnums), 547) @@ -150,7 +150,7 @@ def test_load_peptide_2luv(): @pytest.mark.parametrize("case", ['trajectory', 'trajectory_no_model']) def test_load_many(case): - with path('iodata.test.data', f"water_{case}.pdb") as fn_pdb: + with as_file(files("iodata.test.data").joinpath(f"water_{case}.pdb")) as fn_pdb: mols = list(load_many(str(fn_pdb))) assert len(mols) == 5 for mol in mols: @@ -166,7 +166,7 @@ def test_load_many(case): @pytest.mark.parametrize("case", ['trajectory', 'trajectory_no_model']) def test_load_dump_many_consistency(case, tmpdir): - with path('iodata.test.data', f"water_{case}.pdb") as fn_pdb: + 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') @@ -181,7 +181,7 @@ def test_load_dump_many_consistency(case, tmpdir): def test_load_2bcw(): # test pdb with multiple chains - with path("iodata.test.data", "2bcw.pdb") as fn_pdb: + with as_file(files("iodata.test.data").joinpath("2bcw.pdb")) as fn_pdb: mol = load_one(fn_pdb) assert mol.title == """\ COORDINATES OF THE N-TERMINAL DOMAIN OF RIBOSOMAL PROTEIN L11,C- @@ -215,6 +215,6 @@ def test_load_2bcw(): def test_load_ch5plus_bonds(): - with path("iodata.test.data", "ch5plus.pdb") as fn_pdb: + with as_file(files("iodata.test.data").joinpath("ch5plus.pdb")) as fn_pdb: mol = load_one(fn_pdb) assert_equal(mol.bonds[:, :2], [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5]]) diff --git a/iodata/test/test_poscar.py b/iodata/test/test_poscar.py index 0c79496ab..6eda95851 100644 --- a/iodata/test/test_poscar.py +++ b/iodata/test/test_poscar.py @@ -20,7 +20,6 @@ """Test iodata.formats.poscar module.""" import os - import numpy as np from numpy.testing import assert_equal, assert_allclose @@ -28,13 +27,13 @@ from ..utils import angstrom, volume try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_poscar_water(): - with path('iodata.test.data', 'POSCAR.water') as fn: + 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_equal(mol.atnums, [8, 1, 1]) @@ -44,7 +43,7 @@ def test_load_poscar_water(): def test_load_poscar_cubicbn_cartesian(): - with path('iodata.test.data', 'POSCAR.cubicbn_cartesian') as fn: + with as_file(files("iodata.test.data").joinpath("POSCAR.cubicbn_cartesian")) as fn: mol = load_one(str(fn)) assert mol.title == 'Cubic BN' assert_equal(mol.atnums, [5, 7]) @@ -54,7 +53,7 @@ def test_load_poscar_cubicbn_cartesian(): def test_load_poscar_cubicbn_direct(): - with path('iodata.test.data', 'POSCAR.cubicbn_direct') as fn: + with as_file(files("iodata.test.data").joinpath("POSCAR.cubicbn_direct")) as fn: mol = load_one(str(fn)) assert mol.title == 'Cubic BN' assert_equal(mol.atnums, [5, 7]) @@ -64,7 +63,7 @@ def test_load_poscar_cubicbn_direct(): def test_load_dump_consistency(tmpdir): - with path('iodata.test.data', 'water_element.xyz') as fn: + 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], diff --git a/iodata/test/test_qchemlog.py b/iodata/test/test_qchemlog.py index 2f2775816..2fab1bf83 100644 --- a/iodata/test/test_qchemlog.py +++ b/iodata/test/test_qchemlog.py @@ -26,14 +26,14 @@ from ..utils import LineIterator, angstrom, kjmol try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_load_qchemlog_low_h2o(): """Test load_qchemlog_low with water_hf_ccpvtz_freq_qchem.out.""" - with path('iodata.test.data', 'water_hf_ccpvtz_freq_qchem.out') as fq: + with as_file(files("iodata.test.data").joinpath("water_hf_ccpvtz_freq_qchem.out")) as fq: data = load_qchemlog_low(LineIterator(str(fq))) # check loaded data @@ -114,7 +114,8 @@ def test_load_qchemlog_low_h2o(): def test_load_one_qchemlog_freq(): - with path('iodata.test.data', 'water_hf_ccpvtz_freq_qchem.out') as fn_qchemlog: + 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') assert_allclose(mol.energy, -76.0571936393) assert mol.g_rot == 1 @@ -196,7 +197,7 @@ def test_load_one_qchemlog_freq(): def test_load_qchemlog_low_qchemlog_h2o_dimer_eda2(): """Test load_qchemlog_low with h2o_dimer_eda_qchem5.3.out.""" # pylint: disable=too-many-statements - with path('iodata.test.data', 'h2o_dimer_eda_qchem5.3.out') as fq: + with as_file(files("iodata.test.data").joinpath("h2o_dimer_eda_qchem5.3.out")) as fq: data = load_qchemlog_low(LineIterator(str(fq))) # check loaded data @@ -283,7 +284,7 @@ def test_load_qchemlog_low_qchemlog_h2o_dimer_eda2(): def test_load_one_h2o_dimer_eda2(): """Test load_one with h2o_dimer_eda_qchem5.3.out.""" # pylint: disable=too-many-statements - with path('iodata.test.data', 'h2o_dimer_eda_qchem5.3.out') as fn_qchemlog: + 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') # check loaded data diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 2efe47be5..a5571ad73 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -19,7 +19,6 @@ """Test iodata.formats.sdf module.""" import os - import pytest from numpy.testing import assert_equal, assert_allclose @@ -28,21 +27,21 @@ from ..utils import angstrom, FileFormatError try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def test_sdf_load_one_example(): # test sdf one structure - with path('iodata.test.data', 'example.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_sdf: mol = load_one(str(fn_sdf)) check_example(mol) def test_sdf_load_one_formamide(): # test sdf one structure - with path('iodata.test.data', 'formamide.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("formamide.sdf")) as fn_sdf: mol = load_one(str(fn_sdf)) assert mol.title == "713" assert mol.natom == 6 @@ -53,7 +52,7 @@ def test_sdf_load_one_formamide(): def test_sdf_formaterror(tmpdir): # test if sdf file has the wrong ending without $$$$ - with path('iodata.test.data', 'example.sdf') as fn_test: + with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_test: with truncated_file(fn_test, 36, 0, tmpdir) as fn: with pytest.raises(IOError): load_one(str(fn)) @@ -91,17 +90,17 @@ def check_load_dump_consistency(tmpdir, fn): def test_load_dump_consistency(tmpdir): - with path('iodata.test.data', 'example.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_sdf: check_load_dump_consistency(tmpdir, fn_sdf) - with path('iodata.test.data', 'formamide.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("formamide.sdf")) as fn_sdf: check_load_dump_consistency(tmpdir, fn_sdf) # The benzene mol2 file has aromatic bonds, which are less common in SDF files. - with path('iodata.test.data', 'benzene.mol2') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("benzene.mol2")) as fn_sdf: check_load_dump_consistency(tmpdir, fn_sdf) def test_load_many(): - with path('iodata.test.data', 'example.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_sdf: mols = list(load_many(str(fn_sdf))) assert len(mols) == 2 check_example(mols[0]) @@ -112,7 +111,7 @@ def test_load_many(): def test_load_dump_many_consistency(tmpdir): - with path('iodata.test.data', 'example.sdf') as fn_sdf: + 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') @@ -127,6 +126,6 @@ def test_load_dump_many_consistency(tmpdir): def test_v2000_check(): - with path('iodata.test.data', 'molv3000.sdf') as fn_sdf: + with as_file(files("iodata.test.data").joinpath("molv3000.sdf")) as fn_sdf: with pytest.raises(FileFormatError): load_one(fn_sdf) diff --git a/iodata/test/test_wfn.py b/iodata/test/test_wfn.py index 17a821a53..c1f8464fd 100644 --- a/iodata/test/test_wfn.py +++ b/iodata/test/test_wfn.py @@ -19,9 +19,7 @@ """Test iodata.formats.wfn module.""" import os - import numpy as np - from numpy.testing import assert_equal, assert_allclose from .common import compute_mulliken_charges, check_orthonormal, compare_mols @@ -31,16 +29,16 @@ from ..utils import LineIterator try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files -# TODO: removed density, kin, nucnuc checks +# 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 path('iodata.test.data', fn_wfn) as fn: + with as_file(files("iodata.test.data").joinpath(fn_wfn)) as fn: lit = LineIterator(str(fn)) return load_wfn_low(lit) @@ -130,7 +128,7 @@ def test_load_wfn_low_h2o(): def check_wfn(fn_wfn, nbasis, energy, charges_mulliken): """Check that MO are orthonormal & energy and charges match expected values.""" # load file - with path('iodata.test.data', fn_wfn) as file_wfn: + with as_file(files("iodata.test.data").joinpath(fn_wfn)) as file_wfn: mol = load_one(str(file_wfn)) # check number of basis functions assert mol.obasis.nbasis == nbasis @@ -243,7 +241,7 @@ def test_load_wfn_lih_cation_fci(): def test_load_one_lih_cation_cisd(): - with path('iodata.test.data', 'lih_cation_cisd.wfn') as file_wfn: + 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' @@ -259,7 +257,7 @@ def test_load_one_lih_cation_cisd(): def test_load_one_lih_cation_uhf(): - with path('iodata.test.data', 'lih_cation_uhf.wfn') as file_wfn: + 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' @@ -275,7 +273,7 @@ def test_load_one_lih_cation_uhf(): def test_load_one_lih_cation_rohf(): - with path('iodata.test.data', 'lih_cation_rohf.wfn') as file_wfn: + 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' @@ -292,7 +290,7 @@ def test_load_one_lih_cation_rohf(): def test_load_one_cah110_hf_sto3g_g09(): - with path('iodata.test.data', 'cah110_hf_sto3g_g09.wfn') as file_wfn: + 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' @@ -326,7 +324,7 @@ def check_load_dump_consistency(fn, tmpdir, fmt_from='wfn', fmt_to='wfn', atol=1 Format of filename to dump and then load again. """ - with path('iodata.test.data', fn) as file_name: + 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') dump_one(mol1, fn_tmp, fmt=fmt_to) diff --git a/iodata/test/test_wfx.py b/iodata/test/test_wfx.py index 5a3ebaf51..8ce05690d 100644 --- a/iodata/test/test_wfx.py +++ b/iodata/test/test_wfx.py @@ -19,7 +19,6 @@ """Test iodata.formats.wfn module.""" import os - import pytest import numpy as np from numpy.testing import assert_equal, assert_allclose @@ -33,14 +32,14 @@ compute_mulliken_charges, load_one_warning) try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path + from importlib.resources import as_file, files def helper_load_data_wfx(fn_wfx): """Load a testing WFX file with iodata.formats.wfx.load_data_wfx.""" - with path('iodata.test.data', fn_wfx) as fx: + with as_file(files("iodata.test.data").joinpath(fn_wfx)) as fx: lit = LineIterator(str(fx)) return load_data_wfx(lit) @@ -55,7 +54,7 @@ def check_load_dump_consistency(fn, tmpdir): tmpdir : str The temporary directory to dump and load the file. """ - with path('iodata.test.data', fn) as file_name: + 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') @@ -353,7 +352,7 @@ def test_load_data_wfx_water(): def test_parse_wfx_missing_tag_h2o(): """Check that missing sections result in an exception.""" - with path('iodata.test.data', 'water_sto3g_hf.wfx') as fn_wfx: + with as_file(files("iodata.test.data").joinpath("water_sto3g_hf.wfx")) as fn_wfx: lit = LineIterator(fn_wfx) with pytest.raises(IOError) as error: parse_wfx(lit, required_tags=[""]) @@ -362,7 +361,7 @@ def test_parse_wfx_missing_tag_h2o(): def test_load_data_wfx_h2o_error(): """Check that sections without a closing tag result in an exception.""" - with path('iodata.test.data', 'h2o_error.wfx') as fn_wfx: + with as_file(files("iodata.test.data").joinpath("h2o_error.wfx")) as fn_wfx: with pytest.raises(IOError) as error: load_one(str(fn_wfx)) assert str(error.value).endswith( @@ -371,7 +370,7 @@ def test_load_data_wfx_h2o_error(): def test_load_truncated_h2o(tmpdir): """Check that a truncated file raises an exception.""" - with path('iodata.test.data', 'water_sto3g_hf.wfx') as fn_wfx: + with as_file(files("iodata.test.data").joinpath("water_sto3g_hf.wfx")) as fn_wfx: with truncated_file(str(fn_wfx), 152, 0, tmpdir) as fn_truncated: with pytest.raises(IOError) as error: load_one(str(fn_truncated)) @@ -381,7 +380,7 @@ def test_load_truncated_h2o(tmpdir): def test_load_one_h2o(): """Test load_one with h2o sto-3g WFX input.""" - with path('iodata.test.data', 'water_sto3g_hf.wfx') as file_wfx: + 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], @@ -429,7 +428,7 @@ def test_load_one_h2o(): def test_load_one_h2(): """Test load_one with h2 ub3lyp_ccpvtz WFX input.""" - with path('iodata.test.data', 'h2_ub3lyp_ccpvtz.wfx') as file_wfx: + 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], @@ -468,7 +467,7 @@ def test_load_one_h2(): def test_load_one_lih_cation_cisd(): - with path('iodata.test.data', 'lih_cation_cisd.wfx') as file_wfx: + 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' @@ -491,7 +490,7 @@ def test_load_one_lih_cation_cisd(): def test_load_one_lih_cation_uhf(): - with path('iodata.test.data', 'lih_cation_uhf.wfx') as file_wfx: + 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' @@ -507,7 +506,7 @@ def test_load_one_lih_cation_uhf(): def test_load_one_lih_cation_rohf(): - with path('iodata.test.data', 'lih_cation_rohf.wfx') as file_wfx: + 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' diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index 4b768ee27..3e3abc21b 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -19,29 +19,28 @@ """Test iodata.formats.xyz module.""" import os - import numpy as np from numpy.testing import assert_equal, assert_allclose from ..api import load_one, load_many, dump_one, dump_many from ..utils import angstrom from ..formats.xyz import DEFAULT_ATOM_COLUMNS + try: - from importlib_resources import path + from importlib_resources import as_file, files except ImportError: - from importlib.resources import path - + from importlib.resources import as_file, files def test_load_water_number(): # test xyz with atomic numbers - with path('iodata.test.data', '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 path('iodata.test.data', '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) @@ -73,7 +72,7 @@ def check_water(mol): def test_load_fcc_columns(): - with path('iodata.test.data', 'al_fcc.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("al_fcc.xyz")) as fn_xyz: mol = load_one(str(fn_xyz), atom_columns=FCC_ATOM_COLUMNS) assert "zs" in mol.extra assert mol.extra["zs"].dtype == int @@ -108,27 +107,27 @@ def check_load_dump_consistency(tmpdir, fn, atom_columns=None): def test_load_dump_consistency(tmpdir): - with path('iodata.test.data', 'ch3_hf_sto3g.fchk') as fn_fchk: + with as_file(files("iodata.test.data").joinpath("ch3_hf_sto3g.fchk")) as fn_fchk: check_load_dump_consistency(tmpdir, str(fn_fchk)) def test_dump_xyz_water_element(tmpdir): - with path('iodata.test.data', 'water_element.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_element.xyz")) as fn_xyz: check_load_dump_consistency(tmpdir, str(fn_xyz)) def test_dump_xyz_water_number(tmpdir): - with path('iodata.test.data', 'water_number.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_number.xyz")) as fn_xyz: check_load_dump_consistency(tmpdir, str(fn_xyz)) def test_dump_xyz_fcc(tmpdir): - with path('iodata.test.data', 'al_fcc.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("al_fcc.xyz")) as fn_xyz: check_load_dump_consistency(tmpdir, str(fn_xyz), FCC_ATOM_COLUMNS) def test_load_many(): - with path('iodata.test.data', 'water_trajectory.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("water_trajectory.xyz")) as fn_xyz: mols = list(load_many(str(fn_xyz))) assert len(mols) == 5 for imol, mol in enumerate(mols): @@ -141,7 +140,7 @@ def test_load_many(): def test_load_many_dataset_emptylines(): - with path('iodata.test.data', 'dataset_blanklines.xyz') as fn_xyz: + with as_file(files("iodata.test.data").joinpath("dataset_blanklines.xyz")) as fn_xyz: mols = list(load_many(str(fn_xyz))) assert len(mols) == 3 # Check 1st item @@ -165,7 +164,7 @@ def test_load_many_dataset_emptylines(): def test_load_dump_many_consistency(tmpdir): - with path('iodata.test.data', 'water_trajectory.xyz') as fn_xyz: + 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') From a9a23461c2444ad801bef7ecac64de3676230973 Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Fri, 5 May 2023 16:28:14 -0400 Subject: [PATCH 083/144] Fix pycodestyle E302 expected 2 blank lines, found 1 --- iodata/test/test_xyz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index 3e3abc21b..5fa1eb2a3 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -31,6 +31,7 @@ except ImportError: from importlib.resources import as_file, files + def test_load_water_number(): # test xyz with atomic numbers with as_file(files('iodata.test.data').joinpath('water_number.xyz')) as fn_xyz: From 0deee4e7439c805ce26463f84c274c290758c9ee Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Sat, 6 May 2023 10:01:26 +0200 Subject: [PATCH 084/144] Depend on importlib_resources for py37 for as_file api --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d036dd09f..74d00e34e 100755 --- a/setup.py +++ b/setup.py @@ -75,5 +75,5 @@ def get_readme(): ], setup_requires=['numpy>=1.0'], install_requires=['numpy>=1.0', 'scipy', 'attrs>=20.1.0', - 'importlib_resources; python_version < "3.7"'], + 'importlib_resources; python_version < "3.8"'], ) From f21720b91f52a1b206c9d5c76cf1bf0b4de7275f Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 8 May 2023 14:37:42 +0200 Subject: [PATCH 085/144] Fix a few minor issues - Add same optino to Molekel format for consistency - Use a more specific option name (norm_threshold) to improve semantics - Add default value to some functions to consistency with rest of the code --- iodata/formats/molden.py | 43 +++++++++++++++++++++++++-------------- iodata/formats/molekel.py | 12 ++++++++--- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 3acae19d7..bd16e9635 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -74,11 +74,18 @@ } -@document_load_one("Molden", ['atcoords', 'atnums', 'atcorenums', 'mo', 'obasis'], ['title']) -def load_one(lit: LineIterator, threshold: float = 1e-4) -> dict: +@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."} +) +def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: """Do not edit this docstring. It will be overwritten.""" result = _load_low(lit) - _fix_molden_from_buggy_codes(result, lit, threshold) + _fix_molden_from_buggy_codes(result, lit, norm_threshold) return result @@ -326,7 +333,7 @@ def _load_helper_coeffs(lit: LineIterator) -> Tuple: def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, orb_alpha: np.ndarray, orb_beta: np.ndarray, - threshold: float) -> bool: + norm_threshold: float = 1e-4) -> bool: """Test the normalization of the occupied and virtual orbitals. Parameters @@ -339,8 +346,8 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, The alpha orbitals coefficients orb_beta The beta orbitals (may be None). - threshold - When the maximal error on the norm is large than the threshold, + norm_threshold + When the error on one of the orbitals norm exceeds norm_threshold, the function returns False. True is returned otherwise. """ @@ -374,7 +381,7 @@ def _is_normalized_properly(obasis: MolecularBasis, atcoords: np.ndarray, error_max = max(error_max, abs(norm - 1)) # final judgement - return error_max <= threshold + return error_max <= norm_threshold def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: @@ -582,7 +589,7 @@ def _fix_mo_coeffs_cfour(obasis: MolecularBasis) -> Union[MolecularBasis, None]: return None -def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: float): +def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold: float = 1e-4): """Detect errors in the data loaded from a molden or mkl file and correct. This function can recognize erroneous files created by PSI4, ORCA and @@ -594,6 +601,11 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo A dictionary with the data loaded in the ``load_molden`` function. lit The line iterator to read the data from, used for warnings. + norm_threshold + When the error on one of the orbitals norm exceeds norm_threshold, + the (corrected) data loaded from the Molden file is considered to be + incorrect, in which case other corrections are tested or an exception + is raised when no more corrections can be applied. """ # pylint: disable=too-many-return-statements @@ -609,13 +621,13 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo else: raise ValueError('Molecular orbital kind={0} not recognized'.format(result['mo'].kind)) - if _is_normalized_properly(obasis, atcoords, coeffsa, coeffsb, threshold): + if _is_normalized_properly(obasis, atcoords, coeffsa, coeffsb, norm_threshold): # The file is good. No need to change obasis. return # --- ORCA orca_obasis = _fix_obasis_orca(obasis) - if _is_normalized_properly(orca_obasis, atcoords, coeffsa, coeffsb, threshold): + 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 return @@ -623,7 +635,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo # --- PSI4 < 1.0 psi4_obasis = _fix_obasis_psi4(obasis) if psi4_obasis is not None and \ - _is_normalized_properly(psi4_obasis, atcoords, coeffsa, coeffsb, threshold): + _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 @@ -631,7 +643,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo # -- Turbomole turbom_obasis = _fix_obasis_turbomole(obasis) if turbom_obasis is not None and \ - _is_normalized_properly(turbom_obasis, atcoords, coeffsa, coeffsb, threshold): + _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 @@ -647,7 +659,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo if _is_normalized_properly(obasis, atcoords, coeffsa_cfour, - coeffsb_cfour, threshold): + coeffsb_cfour, norm_threshold): lit.warn('Corrected for CFOUR 2.1 errors in Molden/MKL file.') result['obasis'] = obasis if result['mo'].kind == 'restricted': @@ -659,7 +671,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo # --- Renormalized contractions normed_obasis = _fix_obasis_normalize_contractions(obasis) - if _is_normalized_properly(normed_obasis, atcoords, coeffsa, coeffsb, threshold): + 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 return @@ -672,7 +684,8 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, threshold: flo coeffsb_psi4 = None else: coeffsb_psi4 = coeffsb / psi4_coeff_correction[:, np.newaxis] - if _is_normalized_properly(normed_obasis, atcoords, coeffsa_psi4, coeffsb_psi4, threshold): + 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': diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 243e6ad15..24ed9c532 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -161,8 +161,14 @@ 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']) -def load_one(lit: LineIterator) -> dict: +@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."} +) +def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: """Do not edit this docstring. It will be overwritten.""" charge = None atnums = None @@ -249,7 +255,7 @@ def load_one(lit: LineIterator) -> dict: 'mo': mo, 'atcharges': atcharges, } - _fix_molden_from_buggy_codes(result, lit) + _fix_molden_from_buggy_codes(result, lit, norm_threshold) return result From c207a3a9be9250b4d5e4093b29c05e073806f351 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 8 May 2023 15:13:28 +0200 Subject: [PATCH 086/144] Add tests for norm_threshold option of load_one --- iodata/test/test_molden.py | 10 ++++++++++ iodata/test/test_molekel.py | 14 ++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index c5c95e2f3..c7eaa41fd 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -20,6 +20,8 @@ """Test iodata.formats.molden module.""" import os +import warnings + import attr import numpy as np from numpy.testing import assert_allclose, assert_equal @@ -68,6 +70,14 @@ def test_load_molden_li2_orca(): assert_allclose(charges, expected_charges, atol=1.e-5) +def test_load_molden_li2_orca_huge_threshold(): + with as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden: + with warnings.catch_warnings(): + warnings.simplefilter("error") + # The threshold is set very high, which skip a correction for ORCA. + mol = load_one(str(fn_molden), norm_threshold=1e4) + + def test_load_molden_h2o_orca(): with as_file(files("iodata.test.data").joinpath("h2o.molden.input")) as fn_molden: with pytest.warns(FileFormatWarning) as record: diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 1eb418201..9b232cccc 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -20,6 +20,7 @@ """Test iodata.formats.molekel module.""" import os +import warnings from numpy.testing import assert_equal, assert_allclose @@ -30,6 +31,11 @@ from ..overlap import compute_overlap from ..utils import angstrom +try: + from importlib_resources import as_file, files +except ImportError: + from importlib.resources import as_file, files + def compare_mols_diff_formats(mol1, mol2): """Compare two IOData objects loaded from different formats.""" @@ -152,3 +158,11 @@ def test_load_mkl_h2(): # check mo normalization olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) + + +def test_load_mkl_h2_huge_threshold(): + with as_file(files("iodata.test.data").joinpath("h2_sto3g.mkl")) as fn_molekel: + with warnings.catch_warnings(): + warnings.simplefilter("error") + # The threshold is set very high, which skip a correction for ORCA. + mol = load_one(str(fn_molekel), norm_threshold=1e4) From 9a4f1ae40c6bdc93dfa6ef174d04988bdd943919 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 8 May 2023 17:28:17 +0200 Subject: [PATCH 087/144] Fix pylint issues --- iodata/test/test_molden.py | 2 +- iodata/test/test_molekel.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index c7eaa41fd..ccb56ddad 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -75,7 +75,7 @@ def test_load_molden_li2_orca_huge_threshold(): with warnings.catch_warnings(): warnings.simplefilter("error") # The threshold is set very high, which skip a correction for ORCA. - mol = load_one(str(fn_molden), norm_threshold=1e4) + load_one(str(fn_molden), norm_threshold=1e4) def test_load_molden_h2o_orca(): diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 9b232cccc..f910cbc54 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -165,4 +165,4 @@ def test_load_mkl_h2_huge_threshold(): with warnings.catch_warnings(): warnings.simplefilter("error") # The threshold is set very high, which skip a correction for ORCA. - mol = load_one(str(fn_molekel), norm_threshold=1e4) + load_one(str(fn_molekel), norm_threshold=1e4) From 5d3b498adf0073fafc03327359217a6e2c79bda4 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Mon, 8 May 2023 21:07:13 +0200 Subject: [PATCH 088/144] More informative warning --- iodata/formats/pdb.py | 2 +- iodata/test/test_pdb.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 5b0f89fe6..34fec6e0c 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -101,7 +101,7 @@ def _parse_pdb_atom_line(line, lit): lit.warn("Using the atom name in the PDB file to guess the chemical element.") if atnum is None: atnum = 0 - lit.warn("Failed to determine the atomic number.") + lit.warn(f"Failed to determine the atomic number. atname='{atname}' symbol='{symbol}'") # atom name, residue name, chain id, & residue sequence number atname = line[12:16].strip() diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index 52ab91630..ae5c4cdbe 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -103,7 +103,7 @@ def test_load_dump_consistency(fn_base, should_warn, tmpdir): def test_load_dump_xyz_consistency(tmpdir): with as_file(files("iodata.test.data").joinpath("water.xyz")) as fn_xyz: mol0 = load_one(str(fn_xyz)) - + # write xyz file in a temporary folder & then read it fn_tmp = os.path.join(tmpdir, 'test.pdb') dump_one(mol0, fn_tmp) From feecb0be9647cf309716a96af7620e5222b002d1 Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Sat, 18 Nov 2023 07:56:34 -0500 Subject: [PATCH 089/144] Add workaround for scipy.special.factorial2 changes --- iodata/overlap.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 06725cd6a..095310afb 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -22,13 +22,31 @@ import attr import numpy as np -from scipy.special import binom, factorial2 +import scipy.special from .overlap_cartpure import tfs from .basis import convert_conventions, iter_cart_alphabet, MolecularBasis from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS -__all__ = ['OVERLAP_CONVENTIONS', 'compute_overlap', 'gob_cart_normalization'] +__all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] + + +def factorial2(n, exact=False): + """Wrap scipy.special.factorial2 to return 1.0 when the input is not positive. + + This is a temporary workaround while we wait for Scipy's update. + To learn more, see https://github.com/scipy/scipy/issues/18409. + + Parameters + ---------- + n : int or np.ndarray + Values to calculate n!! for. If n <= 0, the return value is 1. + """ + # Scipy 1.11.x returns an integer when n is an integer, but 1.10.x returns an array, + # so np.array(n) is passed to make sure the output is always an array. + out = scipy.special.factorial2(np.array(n), exact=exact) + out[out <= 0] = 1.0 + return out # pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches @@ -214,7 +232,9 @@ def __init__(self, n_max): Maximum angular momentum. """ - self.binomials = [[binom(n, i) for i in range(n + 1)] for n in range(n_max + 1)] + self.binomials = [ + [scipy.special.binom(n, i) for i in range(n + 1)] for n in range(n_max + 1) + ] facts = [factorial2(m, 2) for m in range(2 * n_max)] facts.insert(0, 1) self.facts = np.array(facts) From b3b73775fb05035c15f4a3f6c8ab5b035dccab93 Mon Sep 17 00:00:00 2001 From: Farnaz Heidar-Zadeh Date: Mon, 11 Dec 2023 02:05:12 +0100 Subject: [PATCH 090/144] Fix factorial2 to return 0 for n <= -2 --- iodata/overlap.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 095310afb..6ae5ab58f 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -32,7 +32,7 @@ def factorial2(n, exact=False): - """Wrap scipy.special.factorial2 to return 1.0 when the input is not positive. + """Wrap scipy.special.factorial2 to return 1.0 when the input is -1. This is a temporary workaround while we wait for Scipy's update. To learn more, see https://github.com/scipy/scipy/issues/18409. @@ -40,12 +40,14 @@ def factorial2(n, exact=False): Parameters ---------- n : int or np.ndarray - Values to calculate n!! for. If n <= 0, the return value is 1. + Values to calculate n!! for. If n={0, -1}, the return value is 1. + For n < -1, the return value is 0. """ # Scipy 1.11.x returns an integer when n is an integer, but 1.10.x returns an array, # so np.array(n) is passed to make sure the output is always an array. out = scipy.special.factorial2(np.array(n), exact=exact) out[out <= 0] = 1.0 + out[out <= -2] = 0.0 return out From b46248561a8e934b0d4ba231d78204fcf4739abb Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 3 May 2024 07:53:50 +0200 Subject: [PATCH 091/144] Fix Python 3.12 compatibility --- .gitignore | 3 +++ iodata/formats/extxyz.py | 3 +-- iodata/formats/qchemlog.py | 3 +-- iodata/test/test_utils.py | 11 ++++++++++- iodata/utils.py | 26 +++++++++++++++++++++++++- 5 files changed, 40 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 3da101502..fb57579a7 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,9 @@ celerybeat-schedule # dotenv .env +# direnv +.envrc + # virtualenv .venv venv/ diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index 7f6b24232..1db534aeb 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -27,7 +27,6 @@ """ -from distutils.util import strtobool import shlex from typing import Iterator @@ -35,7 +34,7 @@ from ..docstrings import document_load_one, document_load_many from ..periodic import sym2num, num2sym -from ..utils import angstrom, amu, LineIterator +from ..utils import angstrom, amu, LineIterator, strtobool from .xyz import load_one as load_one_xyz diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 55d09d513..58af4cdb7 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -22,14 +22,13 @@ """ from typing import Tuple -from distutils.util import strtobool import numpy as np from ..docstrings import document_load_one from ..orbitals import MolecularOrbitals from ..periodic import sym2num -from ..utils import LineIterator, angstrom, kcalmol, calmol, kjmol +from ..utils import LineIterator, angstrom, kcalmol, calmol, kjmol, strtobool __all__ = [] diff --git a/iodata/test/test_utils.py b/iodata/test/test_utils.py index 42b582b51..d3b55536a 100644 --- a/iodata/test/test_utils.py +++ b/iodata/test/test_utils.py @@ -18,9 +18,18 @@ # -- """Unit tests for iodata.utils.""" +import pytest -from ..utils import amu +from ..utils import amu, strtobool def test_amu(): assert abs(amu * 1.008 - 1837.47) < 1e-1 + + +def test_strtobool(): + assert strtobool("T") is True + assert strtobool("false") is False + assert strtobool("y") is True + with pytest.raises(ValueError): + strtobool("whatever") diff --git a/iodata/utils.py b/iodata/utils.py index facb8e53a..6035d9528 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -31,7 +31,7 @@ __all__ = ['LineIterator', 'Cube', 'set_four_index_element', 'volume', - 'derive_naturals', 'check_dm'] + 'derive_naturals', 'check_dm', 'strtobool'] # The unit conversion factors below can be used as follows: @@ -263,3 +263,27 @@ def check_dm(dm: np.ndarray, overlap: np.ndarray, eps: float = 1e-4, occ_max: fl if occupations.max() > occ_max + eps: 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 +} + + +def strtobool(value: str) -> bool: + """Interpret string as a boolean.""" + result = STRTOBOOL.get(value.lower()) + if result is None: + raise ValueError(f"'{value}' cannot be converted to boolean") + return result From 1d434cf8c5fc1be8dae3fd72b9902d57828bc93a Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 3 May 2024 19:19:19 +0200 Subject: [PATCH 092/144] Modernize project structure --- .cardboardlint.yml | 22 -- .github/workflows/ci.yml | 102 ------- .github/workflows/pytest.yml | 20 ++ .pycodestylerc | 3 - .pydocstylerc | 2 - .pylintrc | 502 ----------------------------------- .roberto.yaml | 24 -- .yamllint | 7 - environment.yml | 9 - pyproject.toml | 50 ++++ setup.py | 79 ------ 11 files changed, 70 insertions(+), 750 deletions(-) delete mode 100644 .cardboardlint.yml delete mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pytest.yml delete mode 100644 .pycodestylerc delete mode 100644 .pydocstylerc delete mode 100644 .pylintrc delete mode 100644 .roberto.yaml delete mode 100644 .yamllint delete mode 100644 environment.yml create mode 100644 pyproject.toml delete mode 100755 setup.py diff --git a/.cardboardlint.yml b/.cardboardlint.yml deleted file mode 100644 index 1dd42a944..000000000 --- a/.cardboardlint.yml +++ /dev/null @@ -1,22 +0,0 @@ -linters: - - import: - packages: ['iodata'] - - namespace: - filefilter: ['- */__init__.py', '- */test_*.py', '- *setup.py', '- tools/*', - '- doc/*.py', '+ *.py', '+ *.pyx'] - - pylint: - - pycodestyle: - config: .pycodestylerc - - autopep8: - config: .pycodestylerc - line-range: [79, 100] - - pydocstyle: - - whitespace: - filefilter: ['- iodata/test/data/*', '- *Makefile', '+ *'] - - header: - extra: [] - shebang: '#!/usr/bin/env python3' - - yamllint: - filefilter: ['- *conda.recipe/meta.yaml', '+ *.yml', '+ *.yaml'] - - rst-lint: - filefilter: ['+ README.rst'] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 72d79aadb..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,102 +0,0 @@ -name: CI -on: - push: - tags: - - '[1-9]+.[0-9]+.[0-9]+*' - branches: - - master - pull_request: - branches: - - master - -jobs: - # These are quick tests using Python's venv on different Python versions. - test-venv: - timeout-minutes: 30 - if: "! startsWith(github.ref, 'refs/tags')" - strategy: - fail-fast: false - matrix: - include: - - os: ubuntu-latest - python-version: 3.7 - - os: ubuntu-latest - python-version: 3.8 - - os: ubuntu-latest - python-version: 3.9 - - os: macos-latest - python-version: 3.7 - - runs-on: ${{ matrix.os }} - env: - # Tell Roberto to upload coverage results - ROBERTO_UPLOAD_COVERAGE: 1 - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 2 - - name: Fetch base branch (usually master) - run: | - if [[ -n "${GITHUB_HEAD_REF}" ]]; then - git fetch origin ${GITHUB_BASE_REF} --depth=2 - fi - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v3 - with: - python-version: ${{ matrix.python-version }} - architecture: x64 - - name: Install Pip and Roberto - run: | - python -m pip install --upgrade pip - python -m pip install roberto>=2.0.0 - - name: Test with Roberto - run: | - if [[ -n "${GITHUB_HEAD_REF}" ]]; then - ROBERTO_GIT_MERGE_BRANCH=${GITHUB_SHA} \ - ROBERTO_GIT_BRANCH=${GITHUB_BASE_REF} \ - python -m roberto - else - ROBERTO_TESTENV_USE=venv \ - python -m roberto robot - fi - - - test-conda: - # This is a slow test in a Conda environment, including deployment of - # tagged releases. - timeout-minutes: 30 - if: (github.ref == 'refs/heads/master') || startsWith(github.ref, 'refs/tags') - strategy: - fail-fast: false - - runs-on: ubuntu-latest - env: - ROBERTO_UPLOAD_COVERAGE: 1 - ROBERTO_PACKAGE_MANAGER: conda - ROBERTO_TESTENV_USE: conda - ROBERTO_DEPLOY_NOARCH: 1 - TWINE_USERNAME: theochem - TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_API_TOKEN }} - steps: - - uses: actions/checkout@v3 - - uses: actions/cache@v3 - with: - path: | - ~/miniconda3 - !~/miniconda3/conda-bld - !~/miniconda3/locks - !~/miniconda3/pkgs - !~/miniconda3/var - !~/miniconda3/envs/*/conda-bld - !~/miniconda3/envs/*/locks - !~/miniconda3/envs/*/pkgs - !~/miniconda3/envs/*/var - key: ${{ runner.os }}-conda-4 - - name: Install Roberto - run: | - python -m pip install roberto>=2.0.0 - - name: Test and deploy with Roberto - run: | - python -m roberto robot diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml new file mode 100644 index 000000000..dc03526e2 --- /dev/null +++ b/.github/workflows/pytest.yml @@ -0,0 +1,20 @@ +name: pytest +on: + push: + branches: + - main + tags-ignore: + - '**' + pull_request: +jobs: + tests: + runs-on: "ubuntu-latest" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.x + - name: Install development version + run: pip install -e .[dev] + - name: Run pytest + run: pytest -vv diff --git a/.pycodestylerc b/.pycodestylerc deleted file mode 100644 index b4686f6ff..000000000 --- a/.pycodestylerc +++ /dev/null @@ -1,3 +0,0 @@ -[pycodestyle] -max-line-length=100 -ignore=E741,W503 diff --git a/.pydocstylerc b/.pydocstylerc deleted file mode 100644 index 6133249c5..000000000 --- a/.pydocstylerc +++ /dev/null @@ -1,2 +0,0 @@ -[pydocstyle] -add_ignore=D100,D101,D102,D103,D104,D105 diff --git a/.pylintrc b/.pylintrc deleted file mode 100644 index f73a67630..000000000 --- a/.pylintrc +++ /dev/null @@ -1,502 +0,0 @@ -[MASTER] - -# A comma-separated list of package or module names from where C extensions may -# be loaded. Extensions are loading into the active Python interpreter and may -# run arbitrary code -extension-pkg-whitelist=numpy, iodata.overlap_accel - -# Add files or directories to the blacklist. They should be base names, not -# paths. -ignore=CVS - -# Add files or directories matching the regex patterns to the blacklist. The -# regex matches against base names, not paths. -ignore-patterns= - -# Python code to execute, usually for sys.path manipulation such as -# pygtk.require(). -#init-hook= - -# Use multiple processes to speed up Pylint. -jobs=1 - -# List of plugins (as comma separated values of python modules names) to load, -# usually to register additional checkers. -load-plugins= - -# Pickle collected data for later comparisons. -persistent=yes - -# Specify a configuration file. -#rcfile= - -# Allow loading of arbitrary C extensions. Extensions are imported into the -# active Python interpreter and may run arbitrary code. -unsafe-load-any-extension=no - - -[MESSAGES CONTROL] - -# Only show warnings with the listed confidence levels. Leave empty to show -# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED -confidence= - -# Disable the message, report, category or checker with the given id(s). You -# can either give multiple identifiers separated by comma (,) or put this -# option multiple times (only on the command line, not in the configuration -# file where it should appear only once).You can also use "--disable=all" to -# disable everything first and then reenable specific checks. For example, if -# you want to run only the similarities checker, you can use "--disable=all -# --enable=similarities". If you want to run only the classes checker, but have -# no Warning level messages displayed, use"--disable=all --enable=classes -# --disable=W" -disable= - print-statement, - parameter-unpacking, - unpacking-in-except, - old-raise-syntax, - backtick, - long-suffix, - old-ne-operator, - old-octal-literal, - import-star-module-level, - raw-checker-failed, - bad-inline-option, - locally-disabled, - locally-enabled, - file-ignored, - suppressed-message, - useless-suppression, - deprecated-pragma, - apply-builtin, - basestring-builtin, - buffer-builtin, - cmp-builtin, - coerce-builtin, - execfile-builtin, - file-builtin, - long-builtin, - raw_input-builtin, - reduce-builtin, - standarderror-builtin, - unicode-builtin, - xrange-builtin, - coerce-method, - delslice-method, - getslice-method, - setslice-method, - no-absolute-import, - old-division, - dict-iter-method, - dict-view-method, - next-method-called, - metaclass-assignment, - indexing-exception, - raising-string, - reload-builtin, - oct-method, - hex-method, - nonzero-method, - cmp-method, - input-builtin, - round-builtin, - intern-builtin, - unichr-builtin, - map-builtin-not-iterating, - zip-builtin-not-iterating, - range-builtin-not-iterating, - filter-builtin-not-iterating, - using-cmp-argument, - eq-without-hash, - div-method, - idiv-method, - rdiv-method, - exception-message-attribute, - invalid-str-codec, - sys-max-int, - bad-python3-import, - deprecated-string-function, - deprecated-str-translate-call, - too-many-arguments, - too-many-locals, - too-few-public-methods, - fixme, - invalid-name, - duplicate-code, - unsubscriptable-object, - no-member, - too-many-lines, - unspecified-encoding, - bad-option-value - -# Enable the message, report, category or checker with the given id(s). You can -# either give multiple identifier separated by comma (,) or put this option -# multiple time (only on the command line, not in the configuration file where -# it should appear only once). See also the "--disable" option for examples. -enable= - - -[REPORTS] - -# Python expression which should return a note less than 10 (10 is the highest -# note). You have access to the variables errors warning, statement which -# respectively contain the number of errors / warnings messages and the total -# number of statements analyzed. This is used by the global evaluation report -# (RP0004). -evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) - -# Template used to display messages. This is a python new-style format string -# used to format the message information. See doc for all details -#msg-template= - -# Set the output format. Available formats are text, parseable, colorized, json -# and msvs (visual studio).You can also give a reporter class, eg -# mypackage.mymodule.MyReporterClass. -output-format=text - -# Tells whether to display a full report or only the messages -reports=no - -# Activate the evaluation score. -score=yes - - -[REFACTORING] - -# Maximum number of nested blocks for function / method body -max-nested-blocks=5 - - -[MISCELLANEOUS] - -# List of note tags to take in consideration, separated by a comma. -notes=FIXME,XXX,TODO - - -[TYPECHECK] - -# List of decorators that produce context managers, such as -# contextlib.contextmanager. Add to this list to register other decorators that -# produce valid context managers. -contextmanager-decorators=contextlib.contextmanager - -# List of members which are set dynamically and missed by pylint inference -# system, and so shouldn't trigger E1101 when accessed. Python regular -# expressions are accepted. -generated-members= - -# Tells whether missing members accessed in mixin class should be ignored. A -# mixin class is detected if its name ends with "mixin" (case insensitive). -ignore-mixin-members=yes - -# This flag controls whether pylint should warn about no-member and similar -# checks whenever an opaque object is returned when inferring. The inference -# can return multiple potential results while evaluating a Python object, but -# some branches might not be evaluated, which results in partial inference. In -# that case, it might be useful to still emit no-member and other checks for -# the rest of the inferred objects. -ignore-on-opaque-inference=yes - -# List of class names for which member attributes should not be checked (useful -# for classes with dynamically set attributes). This supports the use of -# qualified names. -ignored-classes=optparse.Values,thread._local,_thread._local,numpy - -# List of module names for which member attributes should not be checked -# (useful for modules/projects where namespaces are manipulated during runtime -# and thus existing member attributes cannot be deduced by static analysis. It -# supports qualified module names, as well as Unix pattern matching. -ignored-modules=numpy - -# Show a hint with possible names when a member name was not found. The aspect -# of finding the hint is based on edit distance. -missing-member-hint=yes - -# The minimum edit distance a name should have in order to be considered a -# similar match for a missing member name. -missing-member-hint-distance=1 - -# The total number of similar names that should be taken in consideration when -# showing a hint for a missing member. -missing-member-max-choices=1 - - -[LOGGING] - -# Logging modules to check that the string format arguments are in logging -# function parameter format -logging-modules=logging - - -[SIMILARITIES] - -# Ignore comments when computing similarities. -ignore-comments=yes - -# Ignore docstrings when computing similarities. -ignore-docstrings=yes - -# Ignore imports when computing similarities. -ignore-imports=no - -# Minimum lines number of a similarity. -min-similarity-lines=4 - - -[SPELLING] - -# Spelling dictionary name. Available dictionaries: none. To make it working -# install python-enchant package. -spelling-dict= - -# List of comma separated words that should not be checked. -spelling-ignore-words= - -# A path to a file that contains private dictionary; one word per line. -spelling-private-dict-file= - -# Tells whether to store unknown words to indicated private dictionary in -# --spelling-private-dict-file option instead of raising a message. -spelling-store-unknown-words=no - - -[FORMAT] - -# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. -expected-line-ending-format= - -# Regexp for a line that is allowed to be longer than the limit. -ignore-long-lines=^\s*(# )??$ - -# Number of spaces of indent required inside a hanging or continued line. -indent-after-paren=4 - -# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 -# tab). -indent-string=' ' - -# Maximum number of characters on a single line. -max-line-length=100 - -# Maximum number of lines in a module -max-module-lines=1000 - -# List of optional constructs for which whitespace checking is disabled. `dict- -# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. -# `trailing-comma` allows a space between comma and closing bracket: (a, ). -# `empty-line` allows space-only lines. -no-space-check=trailing-comma,dict-separator - -# Allow the body of a class to be on the same line as the declaration if body -# contains single statement. -single-line-class-stmt=no - -# Allow the body of an if to be on the same line as the test if there is no -# else. -single-line-if-stmt=no - - -[VARIABLES] - -# List of additional names supposed to be defined in builtins. Remember that -# you should avoid to define new builtins when possible. -additional-builtins= - -# Tells whether unused global variables should be treated as a violation. -allow-global-unused-variables=yes - -# List of strings which can identify a callback function by name. A callback -# name must start or end with one of those strings. -callbacks=cb_,_cb - -# A regular expression matching the name of dummy variables (i.e. expectedly -# not used). -dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ - -# Argument names that match this expression will be ignored. Default to name -# with leading underscore -ignored-argument-names=_.*|^ignored_|^unused_ - -# Tells whether we should check for unused import in __init__ files. -init-import=no - -# List of qualified module names which can have objects that can redefine -# builtins. -redefining-builtins-modules=six.moves,future.builtins - - -[BASIC] - -# Naming hint for argument names -argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct argument names -argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for attribute names -attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct attribute names -attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Bad variable names which should always be refused, separated by a comma -bad-names=foo,bar,baz,toto,tutu,tata - -# Naming hint for class attribute names -class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Regular expression matching correct class attribute names -class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ - -# Naming hint for class names -class-name-hint=[A-Z_][a-zA-Z0-9]+$ - -# Regular expression matching correct class names -class-rgx=[A-Z_][a-zA-Z0-9]+$ - -# Naming hint for constant names -const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Regular expression matching correct constant names -const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ - -# Minimum line length for functions/classes that require docstrings, shorter -# ones are exempt. -docstring-min-length=-1 - -# Naming hint for function names -function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct function names -function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Good variable names which should always be accepted, separated by a comma -good-names=i,j,k,_,f - -# Include a hint for the correct naming format with invalid-name -include-naming-hint=no - -# Naming hint for inline iteration names -inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ - -# Regular expression matching correct inline iteration names -inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ - -# Naming hint for method names -method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct method names -method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Naming hint for module names -module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Regular expression matching correct module names -module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ - -# Colon-delimited sets of names that determine each other's naming style when -# the name regexes allow several styles. -name-group= - -# Regular expression which should only match function or class names that do -# not require a docstring. -no-docstring-rgx=((^_)|(^test_)) - -# List of decorators that produce properties, such as abc.abstractproperty. Add -# to this list to register other decorators that produce valid properties. -property-classes=abc.abstractproperty - -# Naming hint for variable names -variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - -# Regular expression matching correct variable names -variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ - - -[DESIGN] - -# Maximum number of arguments for function / method -max-args=5 - -# Maximum number of attributes for a class (see R0902). -max-attributes=7 - -# Maximum number of boolean expressions in a if statement -max-bool-expr=5 - -# Maximum number of branch for function / method body -max-branches=12 - -# Maximum number of locals for function / method body -max-locals=15 - -# Maximum number of parents for a class (see R0901). -max-parents=7 - -# Maximum number of public methods for a class (see R0904). -max-public-methods=20 - -# Maximum number of return / yield for function / method body -max-returns=6 - -# Maximum number of statements in function / method body -max-statements=50 - -# Minimum number of public methods for a class (see R0903). -min-public-methods=2 - - -[IMPORTS] - -# Allow wildcard imports from modules that define __all__. -allow-wildcard-with-all=no - -# Analyse import fallback blocks. This can be used to support both Python 2 and -# 3 compatible code, which means that the block might have code that exists -# only in one or another interpreter, leading to false positives when analysed. -analyse-fallback-blocks=no - -# Deprecated modules which should not be used, separated by a comma -deprecated-modules=regsub,TERMIOS,Bastion,rexec - -# Create a graph of external dependencies in the given file (report RP0402 must -# not be disabled) -ext-import-graph= - -# Create a graph of every (i.e. internal and external) dependencies in the -# given file (report RP0402 must not be disabled) -import-graph= - -# Create a graph of internal dependencies in the given file (report RP0402 must -# not be disabled) -int-import-graph= - -# Force import order to recognize a module as part of the standard -# compatibility libraries. -known-standard-library= - -# Force import order to recognize a module as part of a third party library. -known-third-party=enchant - - -[CLASSES] - -# List of method names used to declare (i.e. assign) instance attributes. -defining-attr-methods=__init__,__new__,setUp - -# List of member names, which should be excluded from the protected access -# warning. -exclude-protected=_asdict,_fields,_replace,_source,_make - -# List of valid names for the first argument in a class method. -valid-classmethod-first-arg=cls - -# List of valid names for the first argument in a metaclass class method. -valid-metaclass-classmethod-first-arg=mcs - - -[EXCEPTIONS] - -# Exceptions that will emit a warning when being caught. Defaults to -# "Exception" -overgeneral-exceptions=Exception diff --git a/.roberto.yaml b/.roberto.yaml deleted file mode 100644 index d35bcb71a..000000000 --- a/.roberto.yaml +++ /dev/null @@ -1,24 +0,0 @@ -absolute: true # Force absolute comparison for cardboardlint -project: - name: iodata - requirements: [ - [sympy, sympy], - # pylint 2.11.* seems to have bugs which break the CI. - ["pylint <2.11.0", "pylint<2.11.0"] - ] - packages: - - dist_name: qc-iodata - tools: - - write-py-version - - cardboardlint-static - - build-py-inplace - - cardboardlint-dynamic - - pytest - - upload-codecov - - build-sphinx-doc - - upload-docs-gh - - build-py-source - - build-conda - - deploy-pypi - - deploy-conda - - deploy-github diff --git a/.yamllint b/.yamllint deleted file mode 100644 index fe8f4d16e..000000000 --- a/.yamllint +++ /dev/null @@ -1,7 +0,0 @@ -extends: default - -rules: - document-start: disable - line-length: - max: 100 - truthy: disable diff --git a/environment.yml b/environment.yml deleted file mode 100644 index d4cf29bf6..000000000 --- a/environment.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: rtd-iodata -dependencies: - - python=3.7 - - numpy - - scipy - - attrs >=20.1.0 - - pip: - - sphinx-autodoc-typehints - - sphinxcontrib-napoleon diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..45e12f818 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,50 @@ +[build-system] +requires = ["setuptools>=65.0", "setuptools_scm[toml]>=7.1.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "qc-iodata" +authors = [ + { name="HORTON-ChemTools Dev Team", email="horton.chemtools@gmail.com" }, +] +description = "Python Input and Output Library for Quantum Chemistry" +readme = "README.rst" +license = {file = "LICENSE"} +requires-python = ">=3.9" +classifiers = [ + "Environment :: Console", + "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Topic :: Scientific/Engineering :: Physics", + "Topic :: Scientific/Engineering :: Chemistry", + "Intended Audience :: Science/Research", +] +dependencies = [ + "numpy>=1.0", + "scipy", + "attrs>=20.1.0", +] +dynamic = ["version"] + +[project.optional-dependencies] +dev = ["pytest", "pytest-xdist"] + +[project.urls] +Documentation = "https://iodata.readthedocs.io/en/latest/" +Issues = "https://github.com/theochem/iodata/issues/" +Source = "https://github.com/theochem/iodata/" + +[project.scripts] +iodata-convert = "iodata.__main__:main" + +[tool.pytest.ini_options] +addopts = "-n auto" + +[tool.setuptools] +packages = ["iodata"] + +[tool.setuptools_scm] +write_to = "iodata/_version.py" +version_scheme = "post-release" +local_scheme = "no-local-version" diff --git a/setup.py b/setup.py deleted file mode 100755 index 74d00e34e..000000000 --- a/setup.py +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env python3 -# IODATA is an input and output module for quantum chemistry. -# Copyright (C) 2011-2019 The IODATA Development Team -# -# This file is part of IODATA. -# -# IODATA is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 3 -# of the License, or (at your option) any later version. -# -# IODATA is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see -# -- -"""Installation script for IOData. - -Directly calling this script is only needed by IOData developers in special -circumstances. End users are recommended to install IOData with pip or conda. -Developers are recommended to use Roberto. -""" - - -import os - -from setuptools import setup - - -def get_version_info(): - """Read __version__ and DEV_CLASSIFIER from version.py, using exec, not import.""" - fn_version = os.path.join("iodata", "_version.py") - if os.path.isfile(fn_version): - myglobals = {} - with open(fn_version, "r") as f: - exec(f.read(), myglobals) # pylint: disable=exec-used - return myglobals["__version__"], myglobals["DEV_CLASSIFIER"] - return "0.0.0.post0", "Development Status :: 2 - Pre-Alpha" - - -def get_readme(): - """Load README.rst for display on PyPI.""" - with open('README.rst', encoding="utf-8") as fhandle: - return fhandle.read() - - -VERSION, DEV_CLASSIFIER = get_version_info() - -setup( - name='qc-iodata', - version=VERSION, - description='Python Input and Output Library for Quantum Chemistry.', - long_description=get_readme(), - author='HORTON-ChemTools Dev Team', - author_email='horton.chemtools@gmail.com', - url='https://github.com/theochem/iodata', - package_dir={'iodata': 'iodata'}, - packages=['iodata', 'iodata.formats', 'iodata.inputs', 'iodata.test', 'iodata.test.data'], - include_package_data=True, - entry_points={ - 'console_scripts': ['iodata-convert = iodata.__main__:main'] - }, - classifiers=[ - DEV_CLASSIFIER, - 'Environment :: Console', - 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', - 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 3', - 'Topic :: Scientific/Engineering :: Physics', - 'Topic :: Scientific/Engineering :: Chemistry', - 'Intended Audience :: Science/Research', - ], - setup_requires=['numpy>=1.0'], - install_requires=['numpy>=1.0', 'scipy', 'attrs>=20.1.0', - 'importlib_resources; python_version < "3.8"'], -) From 9862efc9add3d96291771cf3bc95046d9428d2df Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 3 May 2024 19:32:05 +0200 Subject: [PATCH 093/144] Add basic pre-commit --- .editorconfig | 18 ++++++++++++++++++ .github/workflows/pytest.yml | 19 +++++++++++++------ .pre-commit-config.yaml | 31 +++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 .editorconfig create mode 100644 .pre-commit-config.yaml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..df64623b1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +# EditorConfig is awesome: https://EditorConfig.org + +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 +max_line_length = 100 + +[Makefile] +indent_style = tab + +[{*.json,*.yml,*.yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index dc03526e2..f171e5a14 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -2,19 +2,26 @@ name: pytest on: push: branches: - - main + # Run tests for change on the main branch ... + - main tags-ignore: - - '**' + # ... but not for tags (avoids duplicate work). + - '**' pull_request: + # Run tests on pull requests jobs: tests: - runs-on: "ubuntu-latest" + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.12"] steps: - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 with: - python-version: 3.x + python-version: ${{ matrix.python-version }} - name: Install development version run: pip install -e .[dev] - - name: Run pytest + - name: Run Pytest run: pytest -vv diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..12e2786ec --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,31 @@ +exclude: 'iodata/test/data/.*' +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files + - id: check-ast + - id: check-case-conflict + - id: check-executables-have-shebangs + - id: check-json + - id: check-merge-conflict + - id: check-symlinks + - id: check-toml + - id: check-vcs-permalinks + - id: debug-statements + - id: detect-private-key + - id: destroyed-symlinks + - id: end-of-file-fixer + - id: fix-byte-order-marker + - id: mixed-line-ending + - id: pretty-format-json + args: ["--autofix", "--no-sort-keys"] + - id: trailing-whitespace +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: remove-crlf +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.1 + hooks: + - id: check-github-workflows From 819b63cdc07a39db9c30f4d33591a12564c584f3 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Fri, 3 May 2024 19:40:42 +0200 Subject: [PATCH 094/144] Try workflow for mac and windows --- .github/workflows/pytest.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index f171e5a14..c33ba83b8 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -11,10 +11,11 @@ on: # Run tests on pull requests jobs: tests: - runs-on: ubuntu-latest strategy: matrix: + os: [ubuntu-latest, macos-latest, windows-latest] python-version: ["3.9", "3.12"] + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} From ef9c592ea67cdb471563763e466e97b7f43373a7 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen Date: Tue, 14 May 2024 11:17:47 +0200 Subject: [PATCH 095/144] 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 12e2786ec..17e86c2b7 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 096/144] Add Ruff format config to pyproject.toml --- pyproject.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 45e12f818..fc87fa8c8 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 097/144] 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 e72be2fb1..d3f3b9878 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 72dc672a9..88e5146e4 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 e1317061c..fbbc6e24f 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 098/144] 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 0c7f2e389..40d043b6b 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 530e7362e..3addbd982 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 e33d03af1..5147f44a6 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 e8a3ccbe2..0459e5e49 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 ef8713f2f..98658e0da 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 96dcf0774..fbda54f7c 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 d34b794c1..71d614ba4 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 41466ad3c..b3f0b3fd6 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 92f4c90e7..117efa2ca 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 820ba0679..e972c0fbe 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 95f120fd8..8ec24d23c 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 bdc746100..0e66ce1c7 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 bf803baf0..527f91bdb 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 e247686c1..a66373f6e 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 1db534aeb..b5b287067 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 1fea1416e..72d16bdbd 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 6817d8041..28d1441f2 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 627fb4119..da241608e 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 c1c2c3825..1ce6622e9 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 a2b3b155d..ec1106e89 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 37e1c0e90..836a6708d 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 18ea7026a..caa0484e4 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 0708969e7..df9a74f7c 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 9fdd8f1de..6d8b02e63 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 bd16e9635..af19f8aad 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 24ed9c532..b81daf926 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 d3f3b9878..21e0a425a 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 738dccf92..e88fa4a43 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 34fec6e0c..928001a55 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 a2f59806d..014553aa8 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 58af4cdb7..0ced6c793 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 8e4d3b73f..9eb5e2a92 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 88e5146e4..2a5036eac 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 c1d786d90..360a0cc6f 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 0d1edca51..8cba51ebe 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 31d978bfd..25308a2f2 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 61c88b304..1c6a6e3cc 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 aa4f3d1f6..25f96fa3d 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 47e890866..876bab6bc 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 5082474ae..decd9741e 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 6ae5ab58f..acabdcfca 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 ae9f2bb03..13a86a010 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 7c8641050..86fd3ba9f 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 cc1f803a6..b36699347 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 6788e7b0b..12afe4e7e 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 179d4d404..cef174cf0 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 681b4874c..b569e2d50 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 aced03ea2..c2baa372f 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 e3b9e2752..bb1f4854f 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 834e2a4fe..b18a8967f 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 04051e670..d72269580 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 e90520bca..a8c363fbb 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 2623f03e1..537bae6af 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 e0fb633cd..2e5e64d6c 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 6ed5031e9..866747bc7 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 b5551211a..5cd8e3789 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 9d0382c5e..4274af09c 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 8c8c03dbc..137850e21 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 46ee887b1..18e297a47 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 65bda6726..47c6713d9 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 55e4e8f63..06f197cc7 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 57d33957f..12116e150 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 ccb56ddad..0df15abba 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 f910cbc54..1b90e024f 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 a72657ecf..d6e53bae3 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 f1adcf712..bbb627b6e 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 7c3aa59d4..b252dee65 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 9091eae23..ec3a3cbee 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 ae5c4cdbe..f4a48a507 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 6eda95851..d66ff6506 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 2fab1bf83..597c4d6d2 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 a5571ad73..07429f9c4 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 c1f8464fd..306c94c87 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 8ce05690d..53b19f4e2 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 5fa1eb2a3..31e4f4cfd 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 6035d9528..34499633b 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 3c22f57b3..ba8881b2a 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))) From ec43b39709e3debc299021617ed61a50f2c23dd5 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 10:33:13 +0200 Subject: [PATCH 099/144] Basic Ruff configuration --- .pre-commit-config.yaml | 4 +- doc/gen_inputs.py | 2 +- iodata/__init__.py | 8 ++- iodata/formats/cp2klog.py | 30 ++++---- iodata/formats/fcidump.py | 25 ++++--- iodata/formats/gaussianlog.py | 10 +-- iodata/overlap.py | 5 +- iodata/utils.py | 20 +++--- tools/harmonics.py | 126 +++++++++++++++++----------------- 9 files changed, 120 insertions(+), 110 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 17e86c2b7..ee60f72b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,10 +26,12 @@ repos: hooks: - id: remove-crlf - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.28.3 + rev: 0.28.4 hooks: - id: check-github-workflows - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.4.4 hooks: - id: ruff-format + - id: ruff + args: ["--fix", "--show-fixes"] diff --git a/doc/gen_inputs.py b/doc/gen_inputs.py index 0459e5e49..3b3ca09a7 100755 --- a/doc/gen_inputs.py +++ b/doc/gen_inputs.py @@ -75,7 +75,7 @@ def main(): print() template = getattr(module, "default_template", None) if template: - code_block_lines = [" " + l for l in template.split("\n")] + code_block_lines = [" " + ell for ell in template.split("\n")] print(TEMPLATE.format(code_block_lines="\n".join(code_block_lines))) print() print() diff --git a/iodata/__init__.py b/iodata/__init__.py index 98658e0da..080f1cbb4 100644 --- a/iodata/__init__.py +++ b/iodata/__init__.py @@ -19,10 +19,14 @@ """Input and Output Module.""" try: - from ._version import __version__ + from ._version import __version__, __version_tuple__ except ImportError: __version__ = "0.0.0.post0" + __version_tuple__ = (0, 0, 0, "a-dev") from .iodata import IOData -from .api import * +from .api import load_one, load_many, dump_one, dump_many, write_input + + +__all__ = ("IOData", "load_one", "load_many", "dump_one", "dump_many", "write_input") diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 527f91bdb..7c46782d8 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -44,7 +44,7 @@ def _get_cp2k_norm_corrections( - l: int, alphas: Union[float, np.ndarray] + ell: int, alphas: Union[float, np.ndarray] ) -> Union[float, np.ndarray]: """Compute the corrections for the normalization of the basis functions. @@ -54,7 +54,7 @@ def _get_cp2k_norm_corrections( Parameters ---------- - l + ell The angular momentum of the (pure) basis function. (s=0, p=1, ...) alphas The exponent or exponents of the Gaussian primitives for which the correction @@ -68,8 +68,8 @@ def _get_cp2k_norm_corrections( applied to the contraction coefficients. """ - expzet = 0.25 * (2 * l + 3) - prefac = np.sqrt(np.sqrt(np.pi) / 2.0 ** (l + 2) * factorialk(2 * l + 1, 2)) + expzet = 0.25 * (2 * ell + 3) + prefac = np.sqrt(np.sqrt(np.pi) / 2.0 ** (ell + 2) * factorialk(2 * ell + 1, 2)) zeta = 2.0 * alphas return zeta**expzet / prefac @@ -225,13 +225,13 @@ def _read_cp2k_occupations_energies( continue empty = 0 s = int(words[0]) - l = int(words[2 - restricted]) + ell = int(words[2 - restricted]) occ = float(words[3 - restricted]) ener = float(words[4 - restricted]) if restricted or words[1] == "alpha": - oe_alpha.append((l, s, occ, ener)) + oe_alpha.append((ell, s, occ, ener)) else: - oe_beta.append((l, s, occ, ener)) + oe_beta.append((ell, s, occ, ener)) return oe_alpha, oe_beta @@ -330,21 +330,21 @@ def _fill_orbitals( offset = 0 offsets = [] ls = np.concatenate([shell.angmoms for shell in obasis.shells]) - for l in sorted(set(ls)): + for ell in sorted(set(ls)): offsets.append(offset) - offset += (2 * l + 1) * (l == ls).sum() + offset += (2 * ell + 1) * (ell == ls).sum() del offset # Fill in the coefficients iorb = 0 - for l, state, occ, ener in oe: - cs = coeffs.get((l, state)) - stride = 2 * l + 1 - for im in range(2 * l + 1): + for ell, state, occ, ener in oe: + cs = coeffs.get((ell, state)) + stride = 2 * ell + 1 + for im in range(2 * ell + 1): orb_energies[iorb] = ener - orb_occupations[iorb] = occ / float((restricted + 1) * (2 * l + 1)) + orb_occupations[iorb] = occ / float((restricted + 1) * (2 * ell + 1)) for ic, c in enumerate(cs): - orb_coeffs[offsets[l] + stride * ic + im, iorb] = c + orb_coeffs[offsets[ell] + stride * ic + im, iorb] = c iorb += 1 diff --git a/iodata/formats/fcidump.py b/iodata/formats/fcidump.py index 28d1441f2..320161a3c 100644 --- a/iodata/formats/fcidump.py +++ b/iodata/formats/fcidump.py @@ -134,18 +134,21 @@ def dump_one(f: TextIO, data: IOData): # Write integrals and core energy 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): - for l in range(k + 1): - if (i * (i + 1)) / 2 + j >= (k * (k + 1)) / 2 + l: - value = two_mo[i, k, j, l] + for i0 in range(nactive): # pylint: disable=too-many-nested-blocks + for i1 in range(i0 + 1): + for i2 in range(nactive): + for i3 in range(i2 + 1): + if (i0 * (i0 + 1)) / 2 + i1 >= (i2 * (i2 + 1)) / 2 + i3: + value = two_mo[i0, i2, i1, i3] if value != 0.0: - 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] + print( + f"{value:23.16e} {i0 + 1:4d} {i1 + 1:4d} {i2 + 1:4d} {i3 + 1:4d}", + file=f, + ) + for i0 in range(nactive): + for i1 in range(i0 + 1): + value = one_mo[i0, i1] 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} {i0 + 1:4d} {i1 + 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) diff --git a/iodata/formats/gaussianlog.py b/iodata/formats/gaussianlog.py index ec1106e89..7bda7e1e1 100644 --- a/iodata/formats/gaussianlog.py +++ b/iodata/formats/gaussianlog.py @@ -133,12 +133,12 @@ def _load_fourindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: 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 + i0 = int(line[3:7]) - 1 + i1 = int(line[9:13]) - 1 + i2 = int(line[15:19]) - 1 + i3 = int(line[21:25]) - 1 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) + set_four_index_element(result, i0, i2, i1, i3, value) return result diff --git a/iodata/overlap.py b/iodata/overlap.py index acabdcfca..b1e93b5e5 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -25,9 +25,10 @@ import scipy.special from .overlap_cartpure import tfs -from .basis import convert_conventions, iter_cart_alphabet, MolecularBasis +from .basis import convert_conventions, iter_cart_alphabet, MolecularBasis, Shell from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS + __all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] @@ -259,7 +260,7 @@ def compute_overlap_gaussian_1d(self, x1, x2, n1, n2, two_at): 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 diff --git a/iodata/utils.py b/iodata/utils.py index 34499633b..fa2b93143 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -153,7 +153,7 @@ def shape(self): def set_four_index_element( - four_index_object: np.ndarray, i: int, j: int, k: int, l: int, value: float + four_index_object: np.ndarray, i0: int, i1: int, i2: int, i3: int, value: float ): """Assign values to a four index object, account for 8-fold index symmetry. @@ -164,20 +164,20 @@ def set_four_index_element( four_index_object The four-index object. It will be written to. shape=(nbasis, nbasis, nbasis, nbasis), dtype=float - i, j, k, l + i0, i1, i2, i3 The indices to assign to. value The value of the matrix element to store. """ - four_index_object[i, j, k, l] = value - four_index_object[j, i, l, k] = value - four_index_object[k, j, i, l] = value - four_index_object[i, l, k, j] = value - four_index_object[k, l, i, j] = value - four_index_object[l, k, j, i] = value - four_index_object[j, k, l, i] = value - four_index_object[l, i, j, k] = value + four_index_object[i0, i1, i2, i3] = value + four_index_object[i1, i0, i3, i2] = value + four_index_object[i2, i1, i0, i3] = value + four_index_object[i0, i3, i2, i1] = value + four_index_object[i2, i3, i0, i1] = value + four_index_object[i3, i2, i1, i0] = value + four_index_object[i1, i2, i3, i0] = value + four_index_object[i3, i0, i1, i2] = value def volume(cellvecs: np.ndarray) -> float: diff --git a/tools/harmonics.py b/tools/harmonics.py index ba8881b2a..62b5fdc25 100644 --- a/tools/harmonics.py +++ b/tools/harmonics.py @@ -30,7 +30,7 @@ def main(): args = parse_args() # Build the bare transformation matrices, without normalization - tfs = get_bare_transforms(args.lmax) + tfs = get_bare_transforms(args.ellmax) if args.norm == "none": # No changes needed. @@ -56,17 +56,17 @@ def parse_args(): parser = argparse.ArgumentParser(prog="harmonics") parser.add_argument("norm", help="Normalization convention", choices=["none", "L2"]) parser.add_argument("format", help="Output format", choices=["latex", "python"]) - parser.add_argument("lmax", help="Maximum angular momentum", type=int) + parser.add_argument("ellmax", help="Maximum angular momentum", type=int) return parser.parse_args() -def get_bare_transforms(lmax: int): - """Build transformation matrices up to lmax, without normalization constants. +def get_bare_transforms(ellmax: int): + """Build transformation matrices up to ellmax, without normalization constants. Parameters ---------- - lmax - Matrices for l going from 0 to lmax (inclusive) are returned. + ellmax + Matrices for ell going from 0 to ellmax (inclusive) are returned. Returns ------- @@ -76,41 +76,41 @@ def get_bare_transforms(lmax: int): """ x, y, z = sp.symbols("x y z", real=True) - rrsh = real_regular_solid_harmonics(x, y, z, lmax, sqrt=sp.sqrt) + rrsh = real_regular_solid_harmonics(x, y, z, ellmax, sqrt=sp.sqrt) rrsh.pop(0) tfs = [np.array([[sp.Integer(1)]])] - for l in range(1, lmax + 1): + for ell in range(1, ellmax + 1): tf = [] - for _m in range(2 * l + 1): + for _m in range(2 * ell + 1): row = [] poly = sp.Poly(rrsh.pop(0), x, y, z) lookup = dict( ((int(nx), int(ny), int(nz)), coeff) for (nx, ny, nz), coeff in zip(poly.monoms(), poly.coeffs()) ) - for nx, ny, nz in iter_mononomials(l): + for nx, ny, nz in iter_mononomials(ell): row.append(sp.sympify(lookup.get((nx, ny, nz), 0))) tf.append(row) tfs.append(np.array(tf)) return tfs -def iter_mononomials(l): +def iter_mononomials(ell): """Iterate over Cartesian mononomials of given angular momentum.""" - for nx in range(l, -1, -1): - for ny in range(l - nx, -1, -1): - nz = l - nx - ny + for nx in range(ell, -1, -1): + for ny in range(ell - nx, -1, -1): + nz = ell - nx - ny yield nx, ny, nz -def real_regular_solid_harmonics(x, y, z, lmax, sqrt=None): - """Evaluate the real regular solid harmonics up the l=lmax. +def real_regular_solid_harmonics(x, y, z, ellmax, sqrt=None): + """Evaluate the real regular solid harmonics up the ell=ellmax. Parameters ---------- x, y and z Cartesian coordinates where the function must be evaluated. - lmax + ellmax Maximum angular monentum quantum number to consider. sqrt An alternative sqrt function to use, e.g. sympy.sqrt. The default is @@ -119,7 +119,7 @@ def real_regular_solid_harmonics(x, y, z, lmax, sqrt=None): Returns ------- result - List of real regular solid harmonics up to angular momentum lmax. + List of real regular solid harmonics up to angular momentum ellmax. The order is following HORTON2 conventions, i.e. .. code-block:: @@ -138,32 +138,32 @@ def real_regular_solid_harmonics(x, y, z, lmax, sqrt=None): r2 = x * x + y * y + z * z offset2 = 0 offset1 = 1 - for l in range(2, lmax + 1): + for ell in range(2, ellmax + 1): # case m = 0 (cosine only) - p1 = 2 * l - 1 - p2 = l - 1 - p3 = l - c_l2_0 = result[offset2] # C_{l-2, 0} - c_l1_0 = result[offset1] # C_{l-1, 0} + p1 = 2 * ell - 1 + p2 = ell - 1 + p3 = ell + c_l2_0 = result[offset2] # C_{ell-2, 0} + c_l1_0 = result[offset1] # C_{ell-1, 0} result.append((p1 * z * c_l1_0 - p2 * r2 * c_l2_0) / p3) - # case m = 1 ... l - 2 - for m in range(1, l - 1): - p2 = sqrt((l + m - 1) * (l - m - 1)) - p3 = sqrt((l + m) * (l - m)) - c_l2_m = result[offset2 + 2 * m - 1] # C_{l-2, m} - c_l1_m = result[offset1 + 2 * m - 1] # C_{l-1, m} + # case m = 1 ... ell - 2 + for m in range(1, ell - 1): + p2 = sqrt((ell + m - 1) * (ell - m - 1)) + p3 = sqrt((ell + m) * (ell - m)) + c_l2_m = result[offset2 + 2 * m - 1] # C_{ell-2, m} + c_l1_m = result[offset1 + 2 * m - 1] # C_{ell-1, m} result.append((p1 * z * c_l1_m - p2 * r2 * c_l2_m) / p3) - s_l2_m = result[offset2 + 2 * m] # S_{l-2, m} - s_l1_m = result[offset1 + 2 * m] # S_{l-1, m} + s_l2_m = result[offset2 + 2 * m] # S_{ell-2, m} + s_l1_m = result[offset1 + 2 * m] # S_{ell-1, m} result.append((p1 * z * s_l1_m - p2 * r2 * s_l2_m) / p3) - # case m = l - 1 + # case m = ell - 1 p4 = sqrt(p1) - c_l1_m = result[offset1 + 2 * l - 3] # C_{l-1, l-1} + c_l1_m = result[offset1 + 2 * ell - 3] # C_{ell-1, ell-1} result.append(p4 * z * c_l1_m) - s_l1_m = result[offset1 + 2 * l - 2] # S_{l-1, l-1} + s_l1_m = result[offset1 + 2 * ell - 2] # S_{ell-1, ell-1} result.append(p4 * z * s_l1_m) - # case m = l - p5 = p4 / sqrt(2 * l) + # case m = ell + p5 = p4 / sqrt(2 * ell) result.append(p5 * (x * c_l1_m - y * s_l1_m)) result.append(p5 * (x * s_l1_m + y * c_l1_m)) # shift offsets @@ -186,25 +186,25 @@ def get_cart_l2_norm(alpha, nx, ny, nz): ) -def get_pure_l2_norm(alpha, l): +def get_pure_l2_norm(alpha, ell): """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 * ell - 1)) / (2 * alpha / sp.pi) ** sp.Rational(3, 2) / (4 * alpha) ** ell ) def include_l2_norm(tfs): """Correct the transformation matrices to work for L2 normalized functions.""" - for l, tf in enumerate(tfs): + for ell, tf in enumerate(tfs): # Multiply each columbn with the norm of a Cartesian function, to go # from normalized to unnormalized Cartesian functions, after which the # transform is applied. The choice of exponent is irrelevant, as long # as it is consistent - for i, (nx, ny, nz) in enumerate(iter_mononomials(l)): + for i, (nx, ny, nz) in enumerate(iter_mononomials(ell)): tf[:, i] *= get_cart_l2_norm(1, nx, ny, nz) # Divide everything by the norm of a pure function, to get results # for normalized pure functions - tf[:] /= get_pure_l2_norm(1, l) + tf[:] /= get_pure_l2_norm(1, ell) # Run simplify on each element for i in range(tf.size): tf.flat[i] = sp.simplify(tf.flat[i]) @@ -213,12 +213,12 @@ def include_l2_norm(tfs): def print_latex(tfs): """Print transformation matrices in Latex code.""" - def iter_pure_labels(l): + def iter_pure_labels(ell): """Iterate over labels for pure functions.""" - yield "C_{{{}0}}".format(l) - for m in range(1, l + 1): - yield "C_{{{}{}}}".format(l, m) - yield "S_{{{}{}}}".format(l, m) + yield "C_{{{}0}}".format(ell) + for m in range(1, ell + 1): + yield "C_{{{}{}}}".format(ell, m) + yield "S_{{{}{}}}".format(ell, m) def tostr(v): """Format an sympy expression as Latex code.""" @@ -226,10 +226,10 @@ def tostr(v): return r"\cdot" return sp.latex(v) - for l, tf in enumerate(tfs): + for ell, tf in enumerate(tfs): npure, ncart = tf.shape print(r"\left(\begin{array}{c}") - print(" ", r" \\ ".join(["b({})".format(label) for label in iter_pure_labels(l)])) + print(" ", r" \\ ".join(["b({})".format(label) for label in iter_pure_labels(ell)])) print(r"\end{array}\right)") print(" &=") print(r"\left(\begin{array}{" + ("c" * ncart) + "}") @@ -242,14 +242,14 @@ def tostr(v): print(r"\end{array}\right)") print(r"\left(\begin{array}{c}") els = [] - for nx, ny, nz in iter_mononomials(l): + for nx, ny, nz in iter_mononomials(ell): spoly = "x" * nx + "y" * ny + "z" * nz if spoly == "": spoly = "1" els.append("b({})".format(spoly)) print(" ", r" \\ ".join(els)) print(r"\end{array}\right)") - if l != len(tfs) - 1: + if ell != len(tfs) - 1: print(r"\\") @@ -263,15 +263,15 @@ def tostr(v): s = s[:-1] return s - for l, tf in enumerate(tfs): + for ell, tf in enumerate(tfs): npure, ncart = tf.shape - print("tf{} = np.array([".format(l)) + print("tf{} = np.array([".format(ell)) for ipure in range(npure): print( " [{}],".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))))) + print("tfs = [{}]".format(", ".join("tf{}".format(ell) for ell in range(len(tfs))))) def test_manual(): @@ -307,14 +307,14 @@ def test_library(): phi = sp.Symbol("phi", real=True) x, y, z, r = sp.symbols("x y z r", real=True) r2 = x * x + y * y + z * z - lmax = 5 - rrsh = real_regular_solid_harmonics(x, y, z, lmax, sqrt=sp.sqrt) + ellmax = 5 + rrsh = real_regular_solid_harmonics(x, y, z, ellmax, sqrt=sp.sqrt) - def _get_cartfn(l, m): + def _get_cartfn(ell, m): """Return the reference result in Cartesian coordinates with SymPy.""" # Take the complex spherical harmonics from SymPy and write in terms # of simple trigoniometric functions. - ref = sp.Ynm(l, abs(m), theta, phi) + ref = sp.Ynm(ell, abs(m), theta, phi) ref = ref.expand(func=True) ref = ref.subs(sp.exp(sp.I * phi), sp.cos(phi) + sp.I * sp.sin(phi)) # Take the definition of real functions from Wikipedia, not from @@ -327,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 * ell + 1)) * ref * r**ell).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))) @@ -337,13 +337,13 @@ def _get_cartfn(l, m): return sp.simplify(ref).expand() assert rrsh.pop(0) == 1 - for l in range(1, lmax + 1): - for m in range(l + 1): + for ell in range(1, ellmax + 1): + for m in range(ell + 1): # cosine line - assert rrsh.pop(0).expand() == _get_cartfn(l, m), (l, m) + assert rrsh.pop(0).expand() == _get_cartfn(ell, m), (ell, m) if m > 0: # sine like - assert rrsh.pop(0).expand() == _get_cartfn(l, -m), (l, m) + assert rrsh.pop(0).expand() == _get_cartfn(ell, -m), (ell, m) if __name__ == "__main__": From 0db9caedd0e94232f13c2d9b14f32ecea7e34076 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 10:38:52 +0200 Subject: [PATCH 100/144] Extend default configuration, mainly import sorting --- doc/conf.py | 1 - doc/gen_formats.py | 1 - doc/gen_formats_tab.py | 3 +-- doc/gen_inputs.py | 2 +- iodata/__init__.py | 3 +-- iodata/__main__.py | 3 ++- iodata/api.py | 7 +++---- iodata/attrutils.py | 1 - iodata/basis.py | 3 +-- iodata/docstrings.py | 3 +-- iodata/formats/charmm.py | 3 +-- iodata/formats/chgcar.py | 3 +-- iodata/formats/cp2klog.py | 5 ++--- iodata/formats/cube.py | 7 +++---- iodata/formats/extxyz.py | 8 +++----- iodata/formats/fchk.py | 8 +++----- iodata/formats/fcidump.py | 5 ++--- iodata/formats/gamess.py | 3 +-- iodata/formats/gaussianinput.py | 3 +-- iodata/formats/gaussianlog.py | 3 +-- iodata/formats/gromacs.py | 7 +++---- iodata/formats/json.py | 3 +-- iodata/formats/locpot.py | 3 +-- iodata/formats/mol2.py | 13 ++++++------- iodata/formats/molden.py | 15 +++++++-------- iodata/formats/molekel.py | 11 +++++------ iodata/formats/mwfn.py | 1 - iodata/formats/orcalog.py | 1 - iodata/formats/pdb.py | 13 ++++++------- iodata/formats/poscar.py | 5 ++--- iodata/formats/qchemlog.py | 2 +- iodata/formats/sdf.py | 13 ++++++------- iodata/formats/wfn.py | 5 ++--- iodata/formats/wfx.py | 11 +++++------ iodata/formats/xyz.py | 13 ++++++------- iodata/inputs/common.py | 1 - iodata/inputs/gaussian.py | 4 +--- iodata/inputs/orca.py | 3 +-- iodata/iodata.py | 1 - iodata/orbitals.py | 1 - iodata/overlap.py | 5 ++--- iodata/periodic.py | 1 - iodata/test/common.py | 5 +++-- iodata/test/test_attrutils.py | 2 +- iodata/test/test_basis.py | 12 ++++++------ iodata/test/test_charmm.py | 5 ++--- iodata/test/test_chgcar.py | 2 +- iodata/test/test_cli.py | 7 ++++--- iodata/test/test_cp2klog.py | 5 ++--- iodata/test/test_cube.py | 4 ++-- iodata/test/test_extxyz.py | 4 ++-- iodata/test/test_fchk.py | 9 ++++----- iodata/test/test_fcidump.py | 5 +++-- iodata/test/test_gamess.py | 2 +- iodata/test/test_gaussianinput.py | 2 +- iodata/test/test_gaussianlog.py | 2 +- iodata/test/test_gromacs.py | 4 ++-- iodata/test/test_inputs.py | 3 ++- iodata/test/test_iodata.py | 6 +++--- iodata/test/test_locpot.py | 2 +- iodata/test/test_mol2.py | 9 +++++---- iodata/test/test_molden.py | 10 +++++----- iodata/test/test_molekel.py | 6 +++--- iodata/test/test_mwfn.py | 2 +- iodata/test/test_orbitals.py | 2 +- iodata/test/test_orcalog.py | 2 +- iodata/test/test_overlap.py | 5 +++-- iodata/test/test_pdb.py | 7 ++++--- iodata/test/test_poscar.py | 5 +++-- iodata/test/test_qchemlog.py | 2 +- iodata/test/test_sdf.py | 7 ++++--- iodata/test/test_wfn.py | 7 ++++--- iodata/test/test_wfx.py | 10 +++++----- iodata/test/test_xyz.py | 7 ++++--- iodata/utils.py | 3 +-- pyproject.toml | 3 +++ 76 files changed, 172 insertions(+), 203 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 40d043b6b..18e0f4c87 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -22,7 +22,6 @@ import os import subprocess - # -- Fragile tricks for RTD ----------------------------------------------- # Normally sphinx-build should be called after iodata is installed somehow. diff --git a/doc/gen_formats.py b/doc/gen_formats.py index 3addbd982..b9f63a795 100755 --- a/doc/gen_formats.py +++ b/doc/gen_formats.py @@ -22,7 +22,6 @@ from iodata.api import FORMAT_MODULES - __all__ = [] diff --git a/doc/gen_formats_tab.py b/doc/gen_formats_tab.py index 5147f44a6..3ed19cbf7 100755 --- a/doc/gen_formats_tab.py +++ b/doc/gen_formats_tab.py @@ -20,12 +20,11 @@ # pylint: disable=unused-argument,redefined-builtin """Generate formats.rst.""" -from collections import defaultdict import inspect +from collections import defaultdict import iodata - __all__ = [] diff --git a/doc/gen_inputs.py b/doc/gen_inputs.py index 3b3ca09a7..d5c0d7b4c 100755 --- a/doc/gen_inputs.py +++ b/doc/gen_inputs.py @@ -21,8 +21,8 @@ """Generate formats.rst.""" from gen_formats import _format_words, _print_section -from iodata.api import INPUT_MODULES +from iodata.api import INPUT_MODULES __all__ = [] diff --git a/iodata/__init__.py b/iodata/__init__.py index 080f1cbb4..7e80f8cf2 100644 --- a/iodata/__init__.py +++ b/iodata/__init__.py @@ -25,8 +25,7 @@ __version_tuple__ = (0, 0, 0, "a-dev") +from .api import dump_many, dump_one, load_many, load_one, write_input from .iodata import IOData -from .api import load_one, load_many, dump_one, dump_many, write_input - __all__ = ("IOData", "load_one", "load_many", "dump_one", "dump_many", "write_input") diff --git a/iodata/__main__.py b/iodata/__main__.py index fbda54f7c..446268d6f 100755 --- a/iodata/__main__.py +++ b/iodata/__main__.py @@ -20,9 +20,10 @@ """CLI for file conversion.""" import argparse + import numpy as np -from .api import load_one, dump_one, load_many, dump_many, FORMAT_MODULES +from .api import FORMAT_MODULES, dump_many, dump_one, load_many, load_one try: from iodata.version import __version__ diff --git a/iodata/api.py b/iodata/api.py index 71d614ba4..2ca972ed9 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -19,16 +19,15 @@ """Functions to be used by end users.""" import os -from typing import Iterator -from types import ModuleType from fnmatch import fnmatch -from pkgutil import iter_modules from importlib import import_module +from pkgutil import iter_modules +from types import ModuleType +from typing import Iterator from .iodata import IOData from .utils import LineIterator - __all__ = ["load_one", "load_many", "dump_one", "dump_many", "write_input"] diff --git a/iodata/attrutils.py b/iodata/attrutils.py index b3f0b3fd6..4ecf26f1f 100644 --- a/iodata/attrutils.py +++ b/iodata/attrutils.py @@ -20,7 +20,6 @@ import numpy as np - __all__ = ["convert_array_to", "validate_shape"] diff --git a/iodata/basis.py b/iodata/basis.py index 117efa2ca..7b71a0716 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -26,14 +26,13 @@ from functools import wraps from numbers import Integral -from typing import List, Dict, Tuple, Union +from typing import Dict, List, Tuple, Union import attr import numpy as np from .attrutils import validate_shape - __all__ = [ "angmom_sti", "angmom_its", diff --git a/iodata/docstrings.py b/iodata/docstrings.py index e972c0fbe..b2554d8a9 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -19,8 +19,7 @@ # pylint: disable=dangerous-default-value """Docstring decorators for file format implementations.""" -from typing import List, Dict - +from typing import Dict, List __all__ = [ "document_load_one", diff --git a/iodata/formats/charmm.py b/iodata/formats/charmm.py index 8ec24d23c..e4c98e3a8 100644 --- a/iodata/formats/charmm.py +++ b/iodata/formats/charmm.py @@ -34,8 +34,7 @@ import numpy as np from ..docstrings import document_load_one - -from ..utils import angstrom, amu, LineIterator +from ..utils import LineIterator, amu, angstrom __all__ = [] diff --git a/iodata/formats/chgcar.py b/iodata/formats/chgcar.py index 0e66ce1c7..05ad6dbb9 100644 --- a/iodata/formats/chgcar.py +++ b/iodata/formats/chgcar.py @@ -31,8 +31,7 @@ from ..docstrings import document_load_one from ..periodic import sym2num -from ..utils import angstrom, volume, LineIterator, Cube - +from ..utils import Cube, LineIterator, angstrom, volume __all__ = [] diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 7c46782d8..56e797a76 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -18,17 +18,16 @@ # -- """CP2K ATOM output file format.""" -from typing import Dict, Union, List, Tuple +from typing import Dict, List, Tuple, Union import numpy as np from scipy.special import factorialk -from ..basis import angmom_sti, MolecularBasis, Shell, HORTON2_CONVENTIONS +from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell, angmom_sti from ..docstrings import document_load_one from ..orbitals import MolecularOrbitals from ..utils import LineIterator - __all__ = [] diff --git a/iodata/formats/cube.py b/iodata/formats/cube.py index a66373f6e..632e71b8f 100644 --- a/iodata/formats/cube.py +++ b/iodata/formats/cube.py @@ -26,14 +26,13 @@ as the effective core charges. """ -from typing import TextIO, Dict, Tuple +from typing import Dict, TextIO, Tuple import numpy as np +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData -from ..docstrings import document_load_one, document_dump_one -from ..utils import LineIterator, Cube - +from ..utils import Cube, LineIterator __all__ = [] diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index b5b287067..1fda8b161 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -32,13 +32,11 @@ import numpy as np -from ..docstrings import document_load_one, document_load_many -from ..periodic import sym2num, num2sym -from ..utils import angstrom, amu, LineIterator, strtobool - +from ..docstrings import document_load_many, document_load_one +from ..periodic import num2sym, sym2num +from ..utils import LineIterator, amu, angstrom, strtobool from .xyz import load_one as load_one_xyz - __all__ = [] diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 72d16bdbd..6e49ab22c 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -19,18 +19,16 @@ """Gaussian FCHK file format.""" from fnmatch import fnmatch -from typing import List, Tuple, Iterator, TextIO +from typing import Iterator, List, TextIO, Tuple import numpy as np +from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell, convert_conventions +from ..docstrings import document_dump_one, document_load_many, document_load_one from ..iodata import IOData -from ..basis import MolecularBasis, Shell, HORTON2_CONVENTIONS, convert_conventions -from ..docstrings import document_load_one, document_load_many -from ..docstrings import document_dump_one from ..orbitals import MolecularOrbitals from ..utils import LineIterator, amu - __all__ = [] diff --git a/iodata/formats/fcidump.py b/iodata/formats/fcidump.py index 320161a3c..7deeb9bb9 100644 --- a/iodata/formats/fcidump.py +++ b/iodata/formats/fcidump.py @@ -32,10 +32,9 @@ import numpy as np -from ..docstrings import document_load_one, document_dump_one +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData -from ..utils import set_four_index_element, LineIterator - +from ..utils import LineIterator, set_four_index_element __all__ = [] diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index da241608e..038d9bdfe 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -21,8 +21,7 @@ import numpy as np from ..docstrings import document_load_one -from ..utils import angstrom, LineIterator - +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/gaussianinput.py b/iodata/formats/gaussianinput.py index 1ce6622e9..29cc7a26a 100644 --- a/iodata/formats/gaussianinput.py +++ b/iodata/formats/gaussianinput.py @@ -22,8 +22,7 @@ from ..docstrings import document_load_one from ..periodic import sym2num -from ..utils import angstrom, LineIterator - +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/gaussianlog.py b/iodata/formats/gaussianlog.py index 7bda7e1e1..f147ccf7a 100644 --- a/iodata/formats/gaussianlog.py +++ b/iodata/formats/gaussianlog.py @@ -30,8 +30,7 @@ import numpy as np from ..docstrings import document_load_one -from ..utils import set_four_index_element, LineIterator - +from ..utils import LineIterator, set_four_index_element __all__ = [] diff --git a/iodata/formats/gromacs.py b/iodata/formats/gromacs.py index 836a6708d..ce0be6fb1 100644 --- a/iodata/formats/gromacs.py +++ b/iodata/formats/gromacs.py @@ -25,13 +25,12 @@ """ -from typing import Tuple, Iterator +from typing import Iterator, Tuple import numpy as np -from ..docstrings import document_load_one, document_load_many -from ..utils import nanometer, picosecond, LineIterator - +from ..docstrings import document_load_many, document_load_one +from ..utils import LineIterator, nanometer, picosecond __all__ = [] diff --git a/iodata/formats/json.py b/iodata/formats/json.py index caa0484e4..dd30d7954 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -570,12 +570,11 @@ import numpy as np +from .. import __version__ from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData from ..periodic import num2sym, sym2num from ..utils import FileFormatError, FileFormatWarning, LineIterator -from .. import __version__ - __all__ = [] diff --git a/iodata/formats/locpot.py b/iodata/formats/locpot.py index df9a74f7c..24b1728b4 100644 --- a/iodata/formats/locpot.py +++ b/iodata/formats/locpot.py @@ -26,10 +26,9 @@ """ from ..docstrings import document_load_one -from ..utils import electronvolt, LineIterator +from ..utils import LineIterator, electronvolt from .chgcar import _load_vasp_grid - __all__ = [] diff --git a/iodata/formats/mol2.py b/iodata/formats/mol2.py index 6d8b02e63..a80d593f7 100644 --- a/iodata/formats/mol2.py +++ b/iodata/formats/mol2.py @@ -22,20 +22,19 @@ was the main objective to write out files with atomic charges used by antechamber. """ -from typing import TextIO, Iterator, Tuple +from typing import Iterator, TextIO, Tuple import numpy as np from ..docstrings import ( - document_load_one, - document_load_many, - document_dump_one, document_dump_many, + document_dump_one, + document_load_many, + document_load_one, ) from ..iodata import IOData -from ..periodic import sym2num, num2sym, bond2num, num2bond -from ..utils import angstrom, LineIterator - +from ..periodic import bond2num, num2bond, num2sym, sym2num +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index af19f8aad..9e78d8458 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -25,27 +25,26 @@ errors are corrected when loading them with IOData. """ -from typing import Tuple, Union, TextIO import copy +from typing import TextIO, Tuple, Union import attr import numpy as np from ..basis import ( - angmom_its, - angmom_sti, + HORTON2_CONVENTIONS, MolecularBasis, Shell, + angmom_its, + angmom_sti, convert_conventions, - HORTON2_CONVENTIONS, ) -from ..docstrings import document_load_one, document_dump_one +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData -from ..periodic import sym2num, num2sym from ..orbitals import MolecularOrbitals from ..overlap import compute_overlap, gob_cart_normalization -from ..utils import angstrom, LineIterator - +from ..periodic import num2sym, sym2num +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index b81daf926..6848a8ad7 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -23,17 +23,16 @@ `Orca <https://sites.google.com/site/orcainputlibrary/>`_. """ -from typing import Tuple, List, TextIO +from typing import List, TextIO, Tuple import numpy as np -from .molden import CONVENTIONS, _fix_molden_from_buggy_codes -from ..basis import angmom_sti, angmom_its, convert_conventions, MolecularBasis, Shell -from ..docstrings import document_load_one, document_dump_one +from ..basis import MolecularBasis, Shell, angmom_its, angmom_sti, convert_conventions +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData from ..orbitals import MolecularOrbitals -from ..utils import angstrom, LineIterator - +from ..utils import LineIterator, angstrom +from .molden import CONVENTIONS, _fix_molden_from_buggy_codes __all__ = [] diff --git a/iodata/formats/mwfn.py b/iodata/formats/mwfn.py index 21e0a425a..f625a24f7 100644 --- a/iodata/formats/mwfn.py +++ b/iodata/formats/mwfn.py @@ -25,7 +25,6 @@ from ..orbitals import MolecularOrbitals from ..utils import LineIterator, angstrom - __all__ = [] diff --git a/iodata/formats/orcalog.py b/iodata/formats/orcalog.py index e88fa4a43..7630ba10e 100644 --- a/iodata/formats/orcalog.py +++ b/iodata/formats/orcalog.py @@ -25,7 +25,6 @@ from ..docstrings import document_load_one from ..utils import LineIterator - __all__ = [] diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 928001a55..d33fc078c 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -23,20 +23,19 @@ http://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html """ -from typing import TextIO, Iterator +from typing import Iterator, TextIO import numpy as np from ..docstrings import ( - document_load_one, - document_load_many, - document_dump_one, document_dump_many, + document_dump_one, + document_load_many, + document_load_one, ) from ..iodata import IOData -from ..periodic import sym2num, num2sym, bond2num -from ..utils import angstrom, LineIterator - +from ..periodic import bond2num, num2sym, sym2num +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/poscar.py b/iodata/formats/poscar.py index 014553aa8..c411c71ee 100644 --- a/iodata/formats/poscar.py +++ b/iodata/formats/poscar.py @@ -26,13 +26,12 @@ import numpy as np -from ..docstrings import document_load_one, document_dump_one +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData from ..periodic import num2sym -from ..utils import angstrom, LineIterator +from ..utils import LineIterator, angstrom from .chgcar import _load_vasp_header - __all__ = [] diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 0ced6c793..3ebca6cc3 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -28,7 +28,7 @@ from ..docstrings import document_load_one from ..orbitals import MolecularOrbitals from ..periodic import sym2num -from ..utils import LineIterator, angstrom, kcalmol, calmol, kjmol, strtobool +from ..utils import LineIterator, angstrom, calmol, kcalmol, kjmol, strtobool __all__ = [] diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index 9eb5e2a92..75bb19de1 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -29,20 +29,19 @@ https://en.wikipedia.org/wiki/Chemical_table_file """ -from typing import TextIO, Iterator +from typing import Iterator, TextIO import numpy as np from ..docstrings import ( - document_load_one, - document_load_many, - document_dump_one, document_dump_many, + document_dump_one, + document_load_many, + document_load_one, ) from ..iodata import IOData -from ..periodic import sym2num, num2sym -from ..utils import angstrom, LineIterator - +from ..periodic import num2sym, sym2num +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 2a5036eac..4c71a47b6 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -30,14 +30,13 @@ import numpy as np from ..basis import MolecularBasis, Shell, convert_conventions -from ..docstrings import document_load_one, document_dump_one +from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData -from ..overlap import gob_cart_normalization from ..orbitals import MolecularOrbitals +from ..overlap import gob_cart_normalization from ..periodic import num2sym, sym2num from ..utils import LineIterator - __all__ = [] diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 360a0cc6f..778e33f34 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -21,19 +21,18 @@ See http://aim.tkgristmill.com/wfxformat.html """ -from typing import TextIO, Iterator import warnings +from typing import Iterator, TextIO import numpy as np -from ..docstrings import document_load_one, document_dump_one +from ..basis import MolecularBasis, Shell, convert_conventions +from ..docstrings import document_dump_one, document_load_one +from ..iodata import IOData from ..orbitals import MolecularOrbitals from ..periodic import num2sym -from ..iodata import IOData from ..utils import LineIterator -from ..basis import MolecularBasis, Shell, convert_conventions - -from .wfn import build_obasis, get_mocoeff_scales, CONVENTIONS +from .wfn import CONVENTIONS, build_obasis, get_mocoeff_scales __all__ = [] diff --git a/iodata/formats/xyz.py b/iodata/formats/xyz.py index 8cba51ebe..de10aaecd 100644 --- a/iodata/formats/xyz.py +++ b/iodata/formats/xyz.py @@ -53,20 +53,19 @@ """ -from typing import TextIO, Iterator +from typing import Iterator, TextIO import numpy as np from ..docstrings import ( - document_load_one, - document_load_many, - document_dump_one, document_dump_many, + document_dump_one, + document_load_many, + document_load_one, ) from ..iodata import IOData -from ..periodic import sym2num, num2sym -from ..utils import angstrom, LineIterator - +from ..periodic import num2sym, sym2num +from ..utils import LineIterator, angstrom __all__ = [] diff --git a/iodata/inputs/common.py b/iodata/inputs/common.py index 25308a2f2..b95e39479 100644 --- a/iodata/inputs/common.py +++ b/iodata/inputs/common.py @@ -19,7 +19,6 @@ """Utilities for writing input files.""" import attr - import numpy as np from ..iodata import IOData diff --git a/iodata/inputs/gaussian.py b/iodata/inputs/gaussian.py index 1c6a6e3cc..67fac3e04 100644 --- a/iodata/inputs/gaussian.py +++ b/iodata/inputs/gaussian.py @@ -20,12 +20,10 @@ from typing import TextIO -from .common import populate_fields - from ..docstrings import document_write_input from ..iodata import IOData from ..periodic import num2sym - +from .common import populate_fields __all__ = [] diff --git a/iodata/inputs/orca.py b/iodata/inputs/orca.py index 25f96fa3d..8a90c7db7 100644 --- a/iodata/inputs/orca.py +++ b/iodata/inputs/orca.py @@ -20,11 +20,10 @@ from typing import TextIO -from .common import populate_fields - from ..docstrings import document_write_input from ..iodata import IOData from ..periodic import num2sym +from .common import populate_fields __all__ = [] diff --git a/iodata/iodata.py b/iodata/iodata.py index 876bab6bc..3d85fc779 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -26,7 +26,6 @@ from .orbitals import MolecularOrbitals from .utils import Cube - __all__ = ["IOData"] diff --git a/iodata/orbitals.py b/iodata/orbitals.py index decd9741e..0efbfec80 100644 --- a/iodata/orbitals.py +++ b/iodata/orbitals.py @@ -23,7 +23,6 @@ from .attrutils import convert_array_to, validate_shape - __all__ = ["MolecularOrbitals"] diff --git a/iodata/overlap.py b/iodata/overlap.py index b1e93b5e5..2f92aba6d 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -24,10 +24,9 @@ import numpy as np import scipy.special -from .overlap_cartpure import tfs -from .basis import convert_conventions, iter_cart_alphabet, MolecularBasis, Shell from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS - +from .basis import MolecularBasis, Shell, convert_conventions, iter_cart_alphabet +from .overlap_cartpure import tfs __all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] diff --git a/iodata/periodic.py b/iodata/periodic.py index 13a86a010..e73228d72 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -20,7 +20,6 @@ from typing import Dict - __all__ = ["num2sym", "sym2num"] diff --git a/iodata/test/common.py b/iodata/test/common.py index 86fd3ba9f..82fafaab8 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -20,13 +20,14 @@ import os from contextlib import contextmanager + import numpy as np -from numpy.testing import assert_equal, assert_allclose import pytest +from numpy.testing import assert_allclose, assert_equal from ..api import load_one -from ..overlap import compute_overlap from ..basis import convert_conventions +from ..overlap import compute_overlap from ..utils import FileFormatWarning try: diff --git a/iodata/test/test_attrutils.py b/iodata/test/test_attrutils.py index b36699347..cdf5cdba2 100644 --- a/iodata/test/test_attrutils.py +++ b/iodata/test/test_attrutils.py @@ -20,8 +20,8 @@ import attr import numpy as np -from numpy.testing import assert_allclose import pytest +from numpy.testing import assert_allclose from ..attrutils import convert_array_to, validate_shape diff --git a/iodata/test/test_basis.py b/iodata/test/test_basis.py index 12afe4e7e..d2c127f15 100644 --- a/iodata/test/test_basis.py +++ b/iodata/test/test_basis.py @@ -20,19 +20,19 @@ import attr import numpy as np -from numpy.testing import assert_equal import pytest +from numpy.testing import assert_equal from ..basis import ( - angmom_sti, - angmom_its, - Shell, + CCA_CONVENTIONS, + HORTON2_CONVENTIONS, MolecularBasis, + Shell, + angmom_its, + angmom_sti, convert_convention_shell, convert_conventions, iter_cart_alphabet, - HORTON2_CONVENTIONS, - CCA_CONVENTIONS, ) from ..formats.cp2klog import CONVENTIONS as CP2K_CONVENTIONS diff --git a/iodata/test/test_charmm.py b/iodata/test/test_charmm.py index cef174cf0..f852d805e 100644 --- a/iodata/test/test_charmm.py +++ b/iodata/test/test_charmm.py @@ -19,11 +19,10 @@ # pylint: disable=unsubscriptable-object """Test iodata.formats.orcalog module.""" -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one - -from ..utils import angstrom, amu +from ..utils import amu, angstrom try: from importlib_resources import as_file, files diff --git a/iodata/test/test_chgcar.py b/iodata/test/test_chgcar.py index b569e2d50..da3fbc670 100644 --- a/iodata/test/test_chgcar.py +++ b/iodata/test/test_chgcar.py @@ -20,7 +20,7 @@ """Test iodata.formats.chgcar module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..utils import angstrom, volume diff --git a/iodata/test/test_cli.py b/iodata/test/test_cli.py index c2baa372f..b647561ba 100644 --- a/iodata/test/test_cli.py +++ b/iodata/test/test_cli.py @@ -18,13 +18,14 @@ # -- """Unit tests for iodata.__main__.""" -import os import functools +import os import subprocess -from numpy.testing import assert_equal, assert_allclose + +from numpy.testing import assert_allclose, assert_equal from ..__main__ import convert -from ..api import load_one, load_many +from ..api import load_many, load_one try: from importlib_resources import as_file, files diff --git a/iodata/test/test_cp2klog.py b/iodata/test/test_cp2klog.py index bb1f4854f..2701e076e 100644 --- a/iodata/test/test_cp2klog.py +++ b/iodata/test/test_cp2klog.py @@ -19,12 +19,11 @@ """Test iodata.formats.cp2klog module.""" import pytest -from numpy.testing import assert_equal, assert_allclose - -from .common import truncated_file, check_orthonormal +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..overlap import compute_overlap +from .common import check_orthonormal, truncated_file try: from importlib_resources import as_file, files diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index b18a8967f..780a4ecec 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -20,9 +20,9 @@ """Test iodata.formats.cube module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, dump_one +from ..api import dump_one, load_one try: from importlib_resources import as_file, files diff --git a/iodata/test/test_extxyz.py b/iodata/test/test_extxyz.py index d72269580..9362c5fd7 100644 --- a/iodata/test/test_extxyz.py +++ b/iodata/test/test_extxyz.py @@ -19,9 +19,9 @@ """Test iodata.formats.extxyz module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, load_many +from ..api import load_many, load_one from ..utils import angstrom try: diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index a8c363fbb..b3bba98c6 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -20,16 +20,15 @@ """Test iodata.formats.fchk module.""" import os -import numpy as np -from numpy.testing import assert_equal, assert_allclose +import numpy as np import pytest +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, load_many, dump_one +from ..api import dump_one, load_many, load_one from ..overlap import compute_overlap from ..utils import check_dm - -from .common import check_orthonormal, compare_mols, load_one_warning, compute_1rdm +from .common import check_orthonormal, compare_mols, compute_1rdm, load_one_warning from .test_molekel import compare_mols_diff_formats try: diff --git a/iodata/test/test_fcidump.py b/iodata/test/test_fcidump.py index 537bae6af..c06545a45 100644 --- a/iodata/test/test_fcidump.py +++ b/iodata/test/test_fcidump.py @@ -19,10 +19,11 @@ """Test iodata.formats.fcidump module.""" import os + import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, dump_one +from ..api import dump_one, load_one try: from importlib_resources import as_file, files diff --git a/iodata/test/test_gamess.py b/iodata/test/test_gamess.py index 2e5e64d6c..0bc84168c 100644 --- a/iodata/test/test_gamess.py +++ b/iodata/test/test_gamess.py @@ -18,7 +18,7 @@ # -- """Test iodata.formats.gamess module.""" -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..utils import angstrom diff --git a/iodata/test/test_gaussianinput.py b/iodata/test/test_gaussianinput.py index 866747bc7..7d10f85be 100644 --- a/iodata/test/test_gaussianinput.py +++ b/iodata/test/test_gaussianinput.py @@ -19,7 +19,7 @@ """Test iodata.formats.gaussianinput module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose, assert_raises +from numpy.testing import assert_allclose, assert_equal, assert_raises from ..api import load_one from ..utils import angstrom diff --git a/iodata/test/test_gaussianlog.py b/iodata/test/test_gaussianlog.py index 5cd8e3789..fe29aaea5 100644 --- a/iodata/test/test_gaussianlog.py +++ b/iodata/test/test_gaussianlog.py @@ -18,7 +18,7 @@ # -- """Test iodata.formats.log module.""" -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one diff --git a/iodata/test/test_gromacs.py b/iodata/test/test_gromacs.py index 4274af09c..dd7848aa1 100644 --- a/iodata/test/test_gromacs.py +++ b/iodata/test/test_gromacs.py @@ -19,9 +19,9 @@ # pylint: disable=unsubscriptable-object """Test iodata.formats.gromacs module.""" -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, load_many +from ..api import load_many, load_one from ..utils import nanometer, picosecond try: diff --git a/iodata/test/test_inputs.py b/iodata/test/test_inputs.py index 137850e21..79395672d 100644 --- a/iodata/test/test_inputs.py +++ b/iodata/test/test_inputs.py @@ -19,11 +19,12 @@ """Test iodata.inputs module.""" import os + import numpy as np +from ..api import load_one, write_input from ..iodata import IOData from ..utils import angstrom -from ..api import load_one, write_input try: from importlib_resources import as_file, files diff --git a/iodata/test/test_iodata.py b/iodata/test/test_iodata.py index 18e297a47..dbf12ea23 100644 --- a/iodata/test/test_iodata.py +++ b/iodata/test/test_iodata.py @@ -19,12 +19,12 @@ """Test iodata.iodata module.""" import numpy as np -from numpy.testing import assert_allclose, assert_equal import pytest +from numpy.testing import assert_allclose, assert_equal -from .common import compute_1rdm -from ..api import load_one, IOData +from ..api import IOData, load_one from ..overlap import compute_overlap +from .common import compute_1rdm try: from importlib_resources import as_file, files diff --git a/iodata/test/test_locpot.py b/iodata/test/test_locpot.py index 06f197cc7..fbc7e8056 100644 --- a/iodata/test/test_locpot.py +++ b/iodata/test/test_locpot.py @@ -19,7 +19,7 @@ # pylint: disable=unsubscriptable-object """Test iodata.formats.locpot module.""" -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..utils import angstrom, electronvolt, volume diff --git a/iodata/test/test_mol2.py b/iodata/test/test_mol2.py index 12116e150..f2690e752 100644 --- a/iodata/test/test_mol2.py +++ b/iodata/test/test_mol2.py @@ -19,13 +19,14 @@ """Test iodata.formats.mol2 module.""" import os + import pytest -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from .common import truncated_file -from ..api import load_one, load_many, dump_one, dump_many -from ..utils import angstrom +from ..api import dump_many, dump_one, load_many, load_one from ..periodic import bond2num +from ..utils import angstrom +from .common import truncated_file try: from importlib_resources import as_file, files diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 0df15abba..7bcbe3684 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -24,15 +24,15 @@ import attr import numpy as np -from numpy.testing import assert_allclose, assert_equal import pytest +from numpy.testing import assert_allclose, assert_equal -from .common import compute_mulliken_charges, compare_mols, check_orthonormal -from ..api import load_one, dump_one +from ..api import dump_one, load_one from ..basis import convert_conventions from ..formats.molden import _load_low -from ..overlap import compute_overlap, OVERLAP_CONVENTIONS -from ..utils import LineIterator, angstrom, FileFormatWarning +from ..overlap import OVERLAP_CONVENTIONS, compute_overlap +from ..utils import FileFormatWarning, LineIterator, angstrom +from .common import check_orthonormal, compare_mols, compute_mulliken_charges try: from importlib_resources import as_file, files diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 1b90e024f..4a69f5a86 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -22,13 +22,13 @@ import os import warnings -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from .common import check_orthonormal, compare_mols, compute_mulliken_charges, load_one_warning +from ..api import dump_one, load_one from ..basis import convert_conventions -from ..api import load_one, dump_one from ..overlap import compute_overlap from ..utils import angstrom +from .common import check_orthonormal, compare_mols, compute_mulliken_charges, load_one_warning try: from importlib_resources import as_file, files diff --git a/iodata/test/test_mwfn.py b/iodata/test/test_mwfn.py index d6e53bae3..71ccc6add 100644 --- a/iodata/test/test_mwfn.py +++ b/iodata/test/test_mwfn.py @@ -19,7 +19,7 @@ """Test iodata.formats.mwfn module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..overlap import compute_overlap diff --git a/iodata/test/test_orbitals.py b/iodata/test/test_orbitals.py index bbb627b6e..fb2dc781f 100644 --- a/iodata/test/test_orbitals.py +++ b/iodata/test/test_orbitals.py @@ -19,8 +19,8 @@ # pylint: disable=pointless-statement """Unit tests for iodata.orbitals.""" -import pytest import numpy as np +import pytest from numpy.testing import assert_equal from ..orbitals import MolecularOrbitals diff --git a/iodata/test/test_orcalog.py b/iodata/test/test_orcalog.py index b252dee65..0e420c699 100644 --- a/iodata/test/test_orcalog.py +++ b/iodata/test/test_orcalog.py @@ -20,7 +20,7 @@ """Test iodata.formats.orcalog module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..utils import angstrom diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index ec3a3cbee..e07f330e8 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -19,14 +19,15 @@ """Test iodata.overlap & iodata.overlap_accel modules.""" import itertools + import attr import numpy as np -from numpy.testing import assert_allclose import pytest +from numpy.testing import assert_allclose from ..api import load_one from ..basis import MolecularBasis, Shell, convert_conventions -from ..overlap import compute_overlap, OVERLAP_CONVENTIONS +from ..overlap import OVERLAP_CONVENTIONS, compute_overlap try: from importlib_resources import as_file, files diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index f4a48a507..bba61b52c 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -19,12 +19,13 @@ """Test iodata.formats.pdb module.""" import os + import numpy as np -from numpy.testing import assert_equal, assert_allclose import pytest +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, load_many, dump_one, dump_many -from ..utils import angstrom, FileFormatWarning +from ..api import dump_many, dump_one, load_many, load_one +from ..utils import FileFormatWarning, angstrom try: from importlib_resources import as_file, files diff --git a/iodata/test/test_poscar.py b/iodata/test/test_poscar.py index d66ff6506..45a1b6028 100644 --- a/iodata/test/test_poscar.py +++ b/iodata/test/test_poscar.py @@ -20,10 +20,11 @@ """Test iodata.formats.poscar module.""" import os + import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, dump_one +from ..api import dump_one, load_one from ..utils import angstrom, volume try: diff --git a/iodata/test/test_qchemlog.py b/iodata/test/test_qchemlog.py index 597c4d6d2..9964304d2 100644 --- a/iodata/test/test_qchemlog.py +++ b/iodata/test/test_qchemlog.py @@ -19,7 +19,7 @@ """Test iodata.formats.qchemlog module.""" import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal from ..api import load_one from ..formats.qchemlog import load_qchemlog_low diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 07429f9c4..59513399a 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -19,12 +19,13 @@ """Test iodata.formats.sdf module.""" import os + import pytest -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal +from ..api import dump_many, dump_one, load_many, load_one +from ..utils import FileFormatError, angstrom from .common import truncated_file -from ..api import load_one, load_many, dump_one, dump_many -from ..utils import angstrom, FileFormatError try: from importlib_resources import as_file, files diff --git a/iodata/test/test_wfn.py b/iodata/test/test_wfn.py index 306c94c87..538095c0f 100644 --- a/iodata/test/test_wfn.py +++ b/iodata/test/test_wfn.py @@ -19,14 +19,15 @@ """Test iodata.formats.wfn module.""" import os + import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from .common import compute_mulliken_charges, check_orthonormal, compare_mols -from ..api import load_one, dump_one +from ..api import dump_one, load_one from ..formats.wfn import load_wfn_low from ..overlap import compute_overlap from ..utils import LineIterator +from .common import check_orthonormal, compare_mols, compute_mulliken_charges try: from importlib_resources import as_file, files diff --git a/iodata/test/test_wfx.py b/iodata/test/test_wfx.py index 53b19f4e2..6d9e9d853 100644 --- a/iodata/test/test_wfx.py +++ b/iodata/test/test_wfx.py @@ -19,21 +19,21 @@ """Test iodata.formats.wfn module.""" import os -import pytest + import numpy as np -from numpy.testing import assert_equal, assert_allclose +import pytest +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, dump_one +from ..api import dump_one, load_one from ..formats.wfx import load_data_wfx, parse_wfx from ..overlap import compute_overlap from ..utils import LineIterator - from .common import ( check_orthonormal, - truncated_file, compare_mols, compute_mulliken_charges, load_one_warning, + truncated_file, ) try: diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index 31e4f4cfd..ad23ab679 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -19,12 +19,13 @@ """Test iodata.formats.xyz module.""" import os + import numpy as np -from numpy.testing import assert_equal, assert_allclose +from numpy.testing import assert_allclose, assert_equal -from ..api import load_one, load_many, dump_one, dump_many -from ..utils import angstrom +from ..api import dump_many, dump_one, load_many, load_one from ..formats.xyz import DEFAULT_ATOM_COLUMNS +from ..utils import angstrom try: from importlib_resources import as_file, files diff --git a/iodata/utils.py b/iodata/utils.py index fa2b93143..ab85610d0 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -18,8 +18,8 @@ # -- """Utility functions module.""" -from typing import Tuple import warnings +from typing import Tuple import attr import numpy as np @@ -28,7 +28,6 @@ from .attrutils import validate_shape - __all__ = [ "LineIterator", "Cube", diff --git a/pyproject.toml b/pyproject.toml index fc87fa8c8..113bc1964 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,3 +52,6 @@ local_scheme = "no-local-version" [tool.ruff] line-length = 100 target-version = "py311" + +[tool.ruff.lint] +select = ["E", "F", "I"] From 9a580a405686b32c17f44e4f54e9277228dbbb57 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 10:41:43 +0200 Subject: [PATCH 101/144] Automatic fixes for py39 update --- doc/gen_formats.py | 6 ++-- doc/gen_inputs.py | 4 +-- iodata/__main__.py | 2 +- iodata/api.py | 6 ++-- iodata/attrutils.py | 11 +++---- iodata/basis.py | 32 +++++++++--------- iodata/docstrings.py | 62 +++++++++++++++++------------------ iodata/formats/charmm.py | 4 +-- iodata/formats/chgcar.py | 4 +-- iodata/formats/cp2klog.py | 14 ++++---- iodata/formats/cube.py | 12 +++---- iodata/formats/extxyz.py | 10 +++--- iodata/formats/fchk.py | 43 ++++++++++++------------- iodata/formats/gromacs.py | 6 ++-- iodata/formats/json.py | 66 ++++++++++++++++---------------------- iodata/formats/mol2.py | 9 +++--- iodata/formats/molden.py | 28 ++++++++-------- iodata/formats/molekel.py | 36 ++++++++++----------- iodata/formats/orcalog.py | 6 ++-- iodata/formats/pdb.py | 8 ++--- iodata/formats/qchemlog.py | 14 ++++---- iodata/formats/sdf.py | 7 ++-- iodata/formats/wfn.py | 14 ++++---- iodata/formats/wfx.py | 27 ++++++++-------- iodata/formats/xyz.py | 7 ++-- iodata/periodic.py | 8 ++--- iodata/test/test_cube.py | 8 ++--- iodata/test/test_fchk.py | 6 ++-- iodata/test/test_inputs.py | 4 +-- iodata/test/test_json.py | 10 +++--- iodata/test/test_xyz.py | 4 +-- iodata/utils.py | 9 +++--- pyproject.toml | 4 +-- tools/harmonics.py | 14 ++++---- 34 files changed, 239 insertions(+), 266 deletions(-) diff --git a/doc/gen_formats.py b/doc/gen_formats.py index b9f63a795..ce7d0bdcd 100755 --- a/doc/gen_formats.py +++ b/doc/gen_formats.py @@ -39,7 +39,7 @@ def _format_words(words): - return ", ".join("``{}``".format(word) for word in words) + return ", ".join(f"``{word}``" for word in words) def _print_section(title, linechar): @@ -64,7 +64,7 @@ def main(): # 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(f"{lines[0][:-1]} (``{modname}``)", "=") print() for line in lines[2:]: print(line) @@ -75,7 +75,7 @@ def main(): for fnname in FNNAMES: fn = getattr(module, fnname, None) if fn is not None: - _print_section(":py:func:`iodata.formats.{}.{}`".format(modname, fnname), "-") + _print_section(f":py:func:`iodata.formats.{modname}.{fnname}`", "-") if fnname.startswith("load"): print("- Always loads", _format_words(fn.guaranteed)) if fn.ifpresent: diff --git a/doc/gen_inputs.py b/doc/gen_inputs.py index d5c0d7b4c..df9566bbe 100755 --- a/doc/gen_inputs.py +++ b/doc/gen_inputs.py @@ -57,12 +57,12 @@ def main(): # 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(f"{lines[0][:-1]} (``{modname}``)", "=") print() for line in lines[2:]: print(line) - _print_section(":py:func:`iodata.formats.{}.write_input`".format(modname), "-") + _print_section(f":py:func:`iodata.formats.{modname}.write_input`", "-") fn = getattr(module, "write_input", None) print("- Requires", _format_words(fn.required)) if fn.optional: diff --git a/iodata/__main__.py b/iodata/__main__.py index 446268d6f..e8228ffe1 100755 --- a/iodata/__main__.py +++ b/iodata/__main__.py @@ -75,7 +75,7 @@ def parse_args(): "-V", "--version", action="version", - version="%(prog)s (IOData version {})".format(__version__), + version=f"%(prog)s (IOData version {__version__})", ) parser.add_argument( "-i", "--infmt", help="Select the input format, overrides automatic detection." diff --git a/iodata/api.py b/iodata/api.py index 2ca972ed9..71767f02d 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -19,11 +19,11 @@ """Functions to be used by end users.""" import os +from collections.abc import Iterator from fnmatch import fnmatch from importlib import import_module from pkgutil import iter_modules from types import ModuleType -from typing import Iterator from .iodata import IOData from .utils import LineIterator @@ -72,9 +72,7 @@ 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(f"Could not find file format with feature {attrname} for file {filename}") def _find_input_modules(): diff --git a/iodata/attrutils.py b/iodata/attrutils.py index 4ecf26f1f..9f970735e 100644 --- a/iodata/attrutils.py +++ b/iodata/attrutils.py @@ -86,16 +86,14 @@ 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(f"Other attribute '{other_name}' is not set.") if other_axis == 0: expected_shape.append(len(other)) else: if other_axis >= other.ndim or other_axis < 0: raise TypeError( "Cannot get length along axis " - "{} of attribute {} with ndim {}.".format( - other_axis, other_name, other.ndim - ) + f"{other_axis} of attribute {other_name} with ndim {other.ndim}." ) expected_shape.append(other.shape[other_axis]) else: @@ -120,9 +118,8 @@ def validator(obj, attribute, value): # Raise TypeError if needed. if not match: raise TypeError( - "Expecting shape {} for attribute {}, got {}".format( - expected_shape, attribute.name, observed_shape - ) + f"Expecting shape {expected_shape} for attribute {attribute.name}, " + f"got {observed_shape}" ) return validator diff --git a/iodata/basis.py b/iodata/basis.py index 7b71a0716..4e34a1d58 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -26,7 +26,7 @@ from functools import wraps from numbers import Integral -from typing import Dict, List, Tuple, Union +from typing import Union import attr import numpy as np @@ -61,7 +61,7 @@ def wrapper(firsts, *args, **kwargs): @_alsolist -def angmom_sti(char: Union[str, List[str]]) -> Union[int, List[int]]: +def angmom_sti(char: Union[str, list[str]]) -> Union[int, list[int]]: """Convert an angular momentum from string to integer format. Parameters @@ -80,7 +80,7 @@ def angmom_sti(char: Union[str, List[str]]) -> Union[int, List[int]]: @_alsolist -def angmom_its(angmom: Union[int, List[int]]) -> Union[str, List[str]]: +def angmom_its(angmom: Union[int, list[int]]) -> Union[str, list[str]]: """Convert an angular momentum from integer to string representation. Parameters @@ -127,8 +127,8 @@ class Shell: """ icenter: int - angmoms: List[int] = attr.ib(validator=validate_shape(("coeffs", 1))) - kinds: List[str] = attr.ib(validator=validate_shape(("coeffs", 1))) + angmoms: list[int] = attr.ib(validator=validate_shape(("coeffs", 1))) + kinds: list[str] = attr.ib(validator=validate_shape(("coeffs", 1))) exponents: np.ndarray = attr.ib(validator=validate_shape(("coeffs", 0))) coeffs: np.ndarray = attr.ib(validator=validate_shape(("exponents", 0), ("kinds", 0))) @@ -142,7 +142,7 @@ def nbasis(self) -> int: # noqa: D401 elif kind == "p" and angmom >= 2: result += 2 * angmom + 1 else: - raise TypeError("Unknown shell kind '{}'; expected 'c' or 'p'.".format(kind)) + raise TypeError(f"Unknown shell kind '{kind}'; expected 'c' or 'p'.") return result @property @@ -205,8 +205,8 @@ class MolecularBasis: """ - shells: List[Shell] - conventions: Dict[str, str] + shells: list[Shell] + conventions: dict[str, str] primitive_normalization: str @property @@ -227,8 +227,8 @@ def get_segmented(self): def convert_convention_shell( - conv1: List[str], conv2: List[str], reverse=False -) -> Tuple[np.ndarray, np.ndarray]: + 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 @@ -276,7 +276,7 @@ def convert_convention_shell( if set(conv1) != set(conv2): raise TypeError( "Without the minus signs, conv1 and conv2 must contain " - "the same elements. Got {} and {}.".format(conv1, conv2) + f"the same elements. Got {conv1} and {conv2}." ) # Get the permutation if reverse: @@ -289,8 +289,8 @@ def convert_convention_shell( def convert_conventions( - molbasis: MolecularBasis, new_conventions: Dict[str, List[str]], reverse=False -) -> Tuple[np.ndarray, np.ndarray]: + 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 @@ -359,7 +359,7 @@ def iter_cart_alphabet(n: int) -> np.ndarray: yield np.array((nx, ny, nz), dtype=int) -def get_default_conventions() -> Tuple[Dict, Dict]: +def get_default_conventions() -> tuple[dict, dict]: """Produce conventions dictionaries compatible with HORTON2 and CCA. Do not change this! Both conventions are also used by several file formats @@ -400,8 +400,8 @@ def get_default_conventions() -> Tuple[Dict, Dict]: if angmom > 1: conv_pure = ["c0"] for absm in range(1, angmom + 1): - conv_pure.append("c{}".format(absm)) - conv_pure.append("s{}".format(absm)) + conv_pure.append(f"c{absm}") + conv_pure.append(f"s{absm}") key = (angmom, "p") horton2[key] = conv_pure cca[key] = conv_pure[:1:-2] + conv_pure[:1] + conv_pure[1::2] diff --git a/iodata/docstrings.py b/iodata/docstrings.py index b2554d8a9..fd6505b61 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -19,8 +19,6 @@ # pylint: disable=dangerous-default-value """Docstring decorators for file format implementations.""" -from typing import Dict, List - __all__ = [ "document_load_one", "document_load_many", @@ -33,9 +31,9 @@ def _document_load( template: str, fmt: str, - guaranteed: List[str], - ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, + guaranteed: list[str], + ifpresent: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): ifpresent = ifpresent or [] @@ -43,13 +41,13 @@ def _document_load( 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) + ", ".join(f"``{word}``" for word in ifpresent) ) else: ifpresent_sentence = "" func.__doc__ = template.format( fmt=fmt, - guaranteed=", ".join("``{}``".format(word) for word in guaranteed), + guaranteed=", ".join(f"``{word}``" for word in guaranteed), ifpresent=ifpresent_sentence, kwdocs="\n".join( "{}\n {}".format(name, docu.replace("\n", " ")) @@ -91,9 +89,9 @@ def decorator(func): def document_load_one( fmt: str, - guaranteed: List[str], - ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, + guaranteed: list[str], + ifpresent: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): """Decorate a load_one function to generate a docstring. @@ -146,9 +144,9 @@ def document_load_one( def document_load_many( fmt: str, - guaranteed: List[str], - ifpresent: List[str] = None, - kwdocs: Dict[str, str] = {}, + guaranteed: list[str], + ifpresent: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): """Decorate a load_many function to generate a docstring. @@ -180,9 +178,9 @@ def document_load_many( def _document_dump( template: str, fmt: str, - required: List[str], - optional: List[str] = None, - kwdocs: Dict[str, str] = {}, + required: list[str], + optional: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): optional = optional or [] @@ -192,12 +190,12 @@ 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(f"``{word}``" for word in optional)) else: optional_sentence = "" func.__doc__ = template.format( fmt=fmt, - required=", ".join("``{}``".format(word) for word in required), + required=", ".join(f"``{word}``" for word in required), optional=optional_sentence, kwdocs="\n".join( "{}\n {}".format(name, docu.replace("\n", " ")) @@ -236,9 +234,9 @@ def decorator(func): def document_dump_one( fmt: str, - required: List[str], - optional: List[str] = None, - kwdocs: Dict[str, str] = {}, + required: list[str], + optional: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): """Decorate a dump_one function to generate a docstring. @@ -288,9 +286,9 @@ def document_dump_one( def document_dump_many( fmt: str, - required: List[str], - optional: List[str] = None, - kwdocs: Dict[str, str] = {}, + required: list[str], + optional: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): """Decorate a dump_many function to generate a docstring. @@ -322,9 +320,9 @@ def document_dump_many( def _document_write( template: str, fmt: str, - required: List[str], - optional: List[str] = None, - kwdocs: Dict[str, str] = {}, + required: list[str], + optional: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): optional = optional or [] @@ -335,12 +333,12 @@ 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(f"``{word}``" for word in optional)) else: optional_sentence = "" func.__doc__ = template.format( fmt=fmt, - required=", ".join("``{}``".format(word) for word in required), + required=", ".join(f"``{word}``" for word in required), optional=optional_sentence, kwdocs="\n".join( "{}\n {}".format(name, docu.replace("\n", " ")) @@ -381,9 +379,9 @@ def decorator(func): def document_write_input( fmt: str, - required: List[str], - optional: List[str] = None, - kwdocs: Dict[str, str] = {}, + required: list[str], + optional: list[str] = None, + kwdocs: dict[str, str] = {}, notes: str = None, ): """Decorate a write_input function to generate a docstring. diff --git a/iodata/formats/charmm.py b/iodata/formats/charmm.py index e4c98e3a8..6545f5970 100644 --- a/iodata/formats/charmm.py +++ b/iodata/formats/charmm.py @@ -29,8 +29,6 @@ """ -from typing import Tuple - import numpy as np from ..docstrings import document_load_one @@ -82,7 +80,7 @@ def load_one(lit: LineIterator) -> dict: return result -def _helper_read_crd(lit: LineIterator) -> Tuple: +def _helper_read_crd(lit: LineIterator) -> tuple: """Read CHARMM crd file.""" # Read the line for number of atoms. natom = next(lit) diff --git a/iodata/formats/chgcar.py b/iodata/formats/chgcar.py index 05ad6dbb9..593eb2ad5 100644 --- a/iodata/formats/chgcar.py +++ b/iodata/formats/chgcar.py @@ -25,8 +25,6 @@ different conversions to atomic units. """ -from typing import Tuple - import numpy as np from ..docstrings import document_load_one @@ -39,7 +37,7 @@ PATTERNS = ["CHGCAR*", "AECCAR*"] -def _load_vasp_header(lit: LineIterator) -> Tuple[str, np.ndarray, np.ndarray, np.ndarray]: +def _load_vasp_header(lit: LineIterator) -> tuple[str, np.ndarray, np.ndarray, np.ndarray]: """Load the cell and atoms from a VASP file format. Parameters diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 56e797a76..1770fece8 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -18,7 +18,7 @@ # -- """CP2K ATOM output file format.""" -from typing import Dict, List, Tuple, Union +from typing import Union import numpy as np from scipy.special import factorialk @@ -194,7 +194,7 @@ def _read_cp2k_obasis(lit: LineIterator) -> dict: def _read_cp2k_occupations_energies( lit: LineIterator, restricted: bool -) -> List[Tuple[int, int, float, float]]: +) -> list[tuple[int, int, float, float]]: """Read orbital occupation numbers and energies from a CP2K ATOM file object. Parameters @@ -235,8 +235,8 @@ def _read_cp2k_occupations_energies( def _read_cp2k_orbital_coeffs( - lit: LineIterator, oe: List[Tuple[int, int, float, float]] -) -> Dict[Tuple[int, int], np.ndarray]: + 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 @@ -270,7 +270,7 @@ def _read_cp2k_orbital_coeffs( return allcoeffs -def _get_norb_nel(oe: List[Tuple[int, int, float, float]]) -> Tuple[int, float]: +def _get_norb_nel(oe: list[tuple[int, int, float, float]]) -> tuple[int, float]: """Return number of orbitals and electrons. Parameters @@ -297,8 +297,8 @@ 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], + oe: list[tuple[int, int, float, float]], + coeffs: dict[tuple[int, int], np.ndarray], obasis: MolecularBasis, restricted: bool, ): diff --git a/iodata/formats/cube.py b/iodata/formats/cube.py index 632e71b8f..0a9c38635 100644 --- a/iodata/formats/cube.py +++ b/iodata/formats/cube.py @@ -26,7 +26,7 @@ as the effective core charges. """ -from typing import Dict, TextIO, Tuple +from typing import TextIO import numpy as np @@ -42,7 +42,7 @@ def _read_cube_header( lit: LineIterator, -) -> Tuple[str, np.ndarray, np.ndarray, np.ndarray, Dict[str, np.ndarray], np.ndarray]: +) -> tuple[str, np.ndarray, np.ndarray, np.ndarray, dict[str, np.ndarray], np.ndarray]: """Load header data from a CUBE file object. Parameters @@ -62,7 +62,7 @@ def _read_cube_header( # skip the second line next(lit) - def read_grid_line(line: str) -> Tuple[int, np.ndarray]: + def read_grid_line(line: str) -> tuple[int, np.ndarray]: """Read a grid line from the cube file.""" words = line.split() return ( @@ -83,7 +83,7 @@ def read_grid_line(line: str) -> Tuple[int, np.ndarray]: cellvecs = axes * shape.reshape(-1, 1) cube = {"origin": origin, "axes": axes, "shape": shape} - def read_atom_line(line: str) -> Tuple[int, float, np.ndarray]: + 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 ( @@ -106,7 +106,7 @@ def read_atom_line(line: str) -> Tuple[int, float, np.ndarray]: return title, atcoords, atnums, cellvecs, cube, atcorenums -def _read_cube_data(lit: LineIterator, cube: Dict[str, np.ndarray]): +def _read_cube_data(lit: LineIterator, cube: dict[str, np.ndarray]): """Load cube data from a CUBE file object. Parameters @@ -152,7 +152,7 @@ def _write_cube_header( title: str, atcoords: np.ndarray, atnums: np.ndarray, - cube: Dict[str, np.ndarray], + cube: dict[str, np.ndarray], atcorenums: np.ndarray, ): print(title, file=f) diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index 1fda8b161..4ee72237a 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -28,7 +28,7 @@ """ import shlex -from typing import Iterator +from collections.abc import Iterator import numpy as np @@ -96,7 +96,7 @@ def _parse_properties(properties: str): (3,), float, (lambda word: float(word) * angstrom), - (lambda value: "{:15.10f}".format(value / angstrom)), + (lambda value: f"{value / angstrom:15.10f}"), ), "masses": ( "atmasses", @@ -104,7 +104,7 @@ def _parse_properties(properties: str): (), float, (lambda word: float(word) * amu), - (lambda value: "{:15.10f}".format(value / amu)), + (lambda value: f"{value / amu:15.10f}"), ), "force": ( "atgradient", @@ -112,7 +112,7 @@ def _parse_properties(properties: str): (3,), float, (lambda word: -float(word)), - (lambda value: "{:15.10f}".format(-value)), + (lambda value: f"{-value:15.10f}"), ), } atnum_column = ( @@ -121,7 +121,7 @@ def _parse_properties(properties: str): (), int, (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), - (lambda atnum: "{:2s}".format(num2sym[atnum])), + (lambda atnum: f"{num2sym[atnum]:2s}"), ) splitted_properties = properties.split(":") assert len(splitted_properties) % 3 == 0 diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 6e49ab22c..31bc9c08a 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -18,8 +18,9 @@ # -- """Gaussian FCHK file format.""" +from collections.abc import Iterator from fnmatch import fnmatch -from typing import Iterator, List, TextIO, Tuple +from typing import TextIO import numpy as np @@ -207,8 +208,8 @@ def load_one(lit: LineIterator) -> dict: _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") + _load_dm(f"Total {lot} Density", fchk, one_rdms, "post_scf_ao") + _load_dm(f"Spin {lot} Density", fchk, one_rdms, "post_scf_spin_ao") if one_rdms: result["one_rdms"] = one_rdms @@ -220,7 +221,7 @@ def load_one(lit: LineIterator) -> dict: if nalpha < 0 or nbeta < 0 or nalpha + nbeta <= 0: 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(f"n_alpha={nalpha} < n_beta={nbeta} is not valid!") norba = fchk["Alpha Orbital Energies"].shape[0] mo_coeffs = np.copy(fchk["Alpha MO coefficients"].reshape(norba, nbasis).T) @@ -327,15 +328,13 @@ 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)] + results_geoms = fchk[f"{prefix} {ipoint + 1:7d} Results for each geome"] 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 - ), + fchk[f"{prefix} {ipoint + 1:7d} Geometries"].reshape(-1, natom, 3), + fchk[f"{prefix} {ipoint + 1:7d} Gradient at each geome"].reshape(-1, natom, 3), ) ) assert len(trajectory) == nstep @@ -359,7 +358,7 @@ def load_many(lit: LineIterator) -> Iterator[dict]: yield data -def _load_fchk_low(lit: LineIterator, label_patterns: List[str] = None) -> dict: +def _load_fchk_low(lit: LineIterator, label_patterns: list[str] = None) -> dict: """Read selected fields from a formatted checkpoint file. Parameters @@ -397,7 +396,7 @@ def _load_fchk_low(lit: LineIterator, label_patterns: List[str] = None) -> dict: # pylint: disable=too-many-branches -def _load_fchk_field(lit: LineIterator, label_patterns: List[str]) -> Tuple[str, object]: +def _load_fchk_field(lit: LineIterator, label_patterns: list[str]) -> tuple[str, object]: """Read a single field matching one of the given label_patterns. Parameters @@ -438,7 +437,7 @@ def _load_fchk_field(lit: LineIterator, label_patterns: List[str]) -> Tuple[str, try: return label, datatype(words[1]) except ValueError: - lit.error("Could not interpret: {}".format(words[1])) + lit.error(f"Could not interpret: {words[1]}") elif len(words) == 3: if words[1] != "N=": lit.error("Expected N= not found.") @@ -453,7 +452,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(f"Could not interpret: {word}") counter += 1 return label, value @@ -508,12 +507,12 @@ def _triangle_to_dense(triangle: np.ndarray) -> np.ndarray: # theses functions, both scalars and arrays, integer and real(float) variables def _dump_integer_scalars(name: str, val: int, f: TextIO): """Dumper for a scalar integer.""" - print("{0:40} I {1:12d}".format(name, int(val)), file=f) + print(f"{name:40} I {int(val):12d}", file=f) def _dump_real_scalars(name: str, val: float, f: TextIO): """Dumper for a scalar float.""" - print("{0:40} R {1: 16.8E}".format(name, float(val)), file=f) + print(f"{name:40} R {float(val): 16.8E}", file=f) def _dump_integer_arrays(name: str, val: np.ndarray, f: TextIO): @@ -521,10 +520,10 @@ def _dump_integer_arrays(name: str, val: np.ndarray, f: TextIO): nval = val.size if nval != 0: np.reshape(val, nval) - print("{0:40} I N={1:12}".format(name, nval), file=f) + print(f"{name:40} I N={nval:12}", file=f) k = 0 for i in range(nval): - print("{0:12}".format(int(val[i])), file=f, end="") + print(f"{int(val[i]):12}", file=f, end="") k += 1 if k == 6 or i == nval - 1: print("", file=f) @@ -536,10 +535,10 @@ def _dump_real_arrays(name: str, val: np.ndarray, f: TextIO): nval = val.size if nval != 0: np.reshape(val, nval) - print("{0:40} R N={1:12}".format(name, nval), file=f) + print(f"{name:40} R N={nval:12}", file=f) k = 0 for i in range(nval): - print("{0: 16.8E}".format(val[i]), file=f, end="") + print(f"{val[i]: 16.8E}", file=f, end="") k += 1 if k == 5 or i == nval - 1: print("", file=f) @@ -569,7 +568,7 @@ def _dump_real_arrays(name: str, val: np.ndarray, f: TextIO): def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" # write title - print("{0:72}".format(data.title or "FCHK generated by IOData"), file=f) + print("{:72}".format(data.title or "FCHK generated by IOData"), file=f) # write run type, level of theory, and basis set name (all in uppercase) items = [getattr(data, item) or "NA" for item in ["run_type", "lot", "obasis_name"]] @@ -694,9 +693,9 @@ def dump_one(f: TextIO, data: IOData): elif key == "scf_spin": title = "Spin SCF Density" elif key == "post_scf_ao": - title = "Total {0} Density".format(level) + title = f"Total {level} Density" elif key == "post_scf_spin_ao": - title = "Spin {0} Density".format(level) + title = f"Spin {level} Density" else: title = "Total SCF Density" _dump_real_arrays(title, mat, f) diff --git a/iodata/formats/gromacs.py b/iodata/formats/gromacs.py index ce0be6fb1..0b26f7d6d 100644 --- a/iodata/formats/gromacs.py +++ b/iodata/formats/gromacs.py @@ -25,7 +25,7 @@ """ -from typing import Iterator, Tuple +from collections.abc import Iterator import numpy as np @@ -75,11 +75,11 @@ def load_many(lit: LineIterator) -> Iterator[dict]: while True: try: yield load_one(lit) - except IOError: + except OSError: return -def _helper_read_frame(lit: LineIterator) -> Tuple: +def _helper_read_frame(lit: LineIterator) -> tuple: """Read one frame.""" # Read the first line, get the title and try to get the time. # Time field is optional. diff --git a/iodata/formats/json.py b/iodata/formats/json.py index dd30d7954..7540b175f 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -565,7 +565,7 @@ """ import json -from typing import List, TextIO, Union +from typing import TextIO, Union from warnings import warn import numpy as np @@ -645,8 +645,8 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: if "schema_name" not in result: # Attempt to determine schema type, since some QCElemental files omit this warn( - "{}: QCSchema files should have a `schema_name` key." - "Attempting to determine schema type...".format(lit.filename), + f"{lit.filename}: QCSchema files should have a `schema_name` key." + "Attempting to determine schema type...", FileFormatWarning, 2, ) @@ -656,7 +656,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: # Check if BSE file, which is too different elif "molssi_bse_schema" in result: raise FileFormatError( - "{}: IOData does not currently support MolSSI BSE Basis JSON.".format(lit.filename) + f"{lit.filename}: IOData does not currently support MolSSI BSE Basis JSON." ) # Center_data is required in any basis schema elif "center_data" in result: @@ -667,11 +667,11 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: else: schema_name = "qcschema_input" else: - raise FileFormatError("{}: Could not determine `schema_name`.".format(lit.filename)) + raise FileFormatError(f"{lit.filename}: Could not determine `schema_name`.") if "schema_version" not in result: warn( - "{}: QCSchema files should have a `schema_version` key." - "Attempting to load without version number.".format(lit.filename), + f"{lit.filename}: QCSchema files should have a `schema_version` key." + "Attempting to load without version number.", FileFormatWarning, 2, ) @@ -761,15 +761,13 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: for key in should_be_required_keys: if key not in mol: warn( - "{}: QCSchema files should have a '{}' key.".format(lit.filename, key), + f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, 2, ) for key in topology_keys: if key not in mol: - raise FileFormatError( - "{}: QCSchema topology requires '{}' key".format(lit.filename, key) - ) + raise FileFormatError(f"{lit.filename}: QCSchema topology requires '{key}' key") topology_dict = {} extra_dict = {} @@ -1049,7 +1047,7 @@ def _load_qcschema_input(result: dict, lit: LineIterator) -> dict: extra_dict["input"] = input_dict["extra"] if "molecule" not in result: - raise FileFormatError("{}: QCSchema Input requires 'molecule' key".format(lit.filename)) + raise FileFormatError(f"{lit.filename}: QCSchema Input requires 'molecule' key") molecule_dict = _parse_topology_keys(result["molecule"], lit) input_dict.update(molecule_dict) extra_dict["molecule"] = molecule_dict["extra"] @@ -1083,14 +1081,14 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: for key in should_be_required_keys: if key not in result: warn( - "{}: QCSchema files should have a '{}' key.".format(lit.filename, key), + f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, 2, ) for key in input_keys: if key not in result: raise FileFormatError( - "{}: QCSchema `qcschema_input` file requires '{}' key".format(lit.filename, key) + f"{lit.filename}: QCSchema `qcschema_input` file requires '{key}' key" ) # Store all extra keys in extra_dict and gather at end input_dict = {} @@ -1186,8 +1184,8 @@ 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) + f"{lit.filename}: QCSchema driver must be one of `energy`, `gradient`, `hessian`, " + "or `properties`" ) return driver @@ -1213,20 +1211,16 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: extra_dict = {} if "method" not in model: - raise FileFormatError("{}: QCSchema `model` requires a `method`".format(lit.filename)) + raise FileFormatError(f"{lit.filename}: QCSchema `model` requires a `method`") model_dict["lot"] = model["method"] # QCEngineRecords doesn't give an empty string for basis-free methods, omits req'd key instead if "basis" not in model: - warn( - "{}: Model `basis` key should be given. Assuming basis-free method.".format( - lit.filename - ) - ) + warn(f"{lit.filename}: Model `basis` key should be given. Assuming basis-free method.") elif isinstance(model["basis"], str): if model["basis"] == "": warn( - "{}: QCSchema `basis` could not be read and will be omitted." - "Unless model is for a basis-free method, check input file.".format(lit.filename), + f"{lit.filename}: QCSchema `basis` could not be read and will be omitted." + "Unless model is for a basis-free method, check input file.", FileFormatWarning, 2, ) @@ -1274,9 +1268,7 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: keep_stdout = protocols["stdout"] protocols_dict = {} if wavefunction not in {"all", "orbitals_and_eigenvalues", "return_results", "none"}: - raise FileFormatError( - "{}: Invalid `protocols` `wavefunction` keyword.".format(lit.filename) - ) + raise FileFormatError(f"{lit.filename}: Invalid `protocols` `wavefunction` keyword.") protocols_dict["keep_wavefunction"] = wavefunction if not isinstance(keep_stdout, bool): raise FileFormatError("{}: `protocols` `stdout` option must be a boolean.") @@ -1308,7 +1300,7 @@ def _load_qcschema_output(result: dict, lit: LineIterator) -> dict: extra_dict["output"] = output_dict["extra"] if "molecule" not in result: - raise FileFormatError("{}: QCSchema Input requires 'molecule' key".format(lit.filename)) + raise FileFormatError(f"{lit.filename}: QCSchema Input requires 'molecule' key") molecule_dict = _parse_topology_keys(result["molecule"], lit) output_dict.update(molecule_dict) extra_dict["molecule"] = molecule_dict["extra"] @@ -1344,14 +1336,14 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: for key in should_be_required_keys: if key not in result: warn( - "{}: QCSchema files should have a '{}' key.".format(lit.filename, key), + f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, 2, ) for key in output_keys: if key not in result: raise FileFormatError( - "{}: QCSchema `qcschema_output` file requires '{}' key".format(lit.filename, key) + f"{lit.filename}: QCSchema `qcschema_output` file requires '{key}' key" ) # Store all extra keys in extra_dict and gather at end @@ -1404,8 +1396,8 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: def _parse_provenance( - provenance: Union[List[dict], dict], lit: LineIterator, source: str, append=True -) -> Union[List[dict], dict]: + provenance: Union[list[dict], dict], lit: LineIterator, source: str, append=True +) -> Union[list[dict], dict]: """Load :ref:`provenance <json_schema_provenance>` properties from QCSchema. Parameters @@ -1427,9 +1419,7 @@ def _parse_provenance( """ if isinstance(provenance, dict): if "creator" not in provenance: - raise FileFormatError( - "{}: `{}` provenance requires `creator` key".format(lit.filename, source) - ) + raise FileFormatError(f"{lit.filename}: `{source}` provenance requires `creator` key") if append: base_provenance = [provenance] else: @@ -1440,7 +1430,7 @@ def _parse_provenance( raise FileFormatError("{}: `{}` provenance requires `creator` key") base_provenance = provenance else: - raise FileFormatError("{}: Invalid `{}` provenance type".format(lit.filename, source)) + raise FileFormatError(f"{lit.filename}: Invalid `{source}` provenance type") if append: base_provenance.append( {"creator": "IOData", "version": __version__, "routine": "iodata.formats.json.load_one"} @@ -1462,7 +1452,7 @@ def dump_one(f: TextIO, data: IOData): if schema_name == "qcschema_molecule": return_dict = _dump_qcschema_molecule(data) elif schema_name == "qcschema_basis": - raise NotImplementedError("{} not yet implemented in IOData.".format(schema_name)) + raise NotImplementedError(f"{schema_name} not yet implemented in IOData.") # return_dict = _dump_qcschema_basis(data) elif schema_name == "qcschema_input": return_dict = _dump_qcschema_input(data) @@ -1567,7 +1557,7 @@ def _dump_qcschema_molecule(data: IOData) -> dict: return molecule_dict -def _dump_provenance(data: IOData, source: str) -> Union[List[dict], dict]: +def _dump_provenance(data: IOData, source: str) -> Union[list[dict], dict]: """Generate the :ref:`provenance <json_schema_provenance>` information. This is used when dumping an IOData instance to QCSchema. diff --git a/iodata/formats/mol2.py b/iodata/formats/mol2.py index a80d593f7..50fc0f3c6 100644 --- a/iodata/formats/mol2.py +++ b/iodata/formats/mol2.py @@ -22,7 +22,8 @@ was the main objective to write out files with atomic charges used by antechamber. """ -from typing import Iterator, TextIO, Tuple +from collections.abc import Iterator +from typing import TextIO import numpy as np @@ -84,7 +85,7 @@ def load_one(lit: LineIterator) -> dict: def _load_helper_atoms( lit: LineIterator, natoms: int -) -> Tuple[np.ndarray, np.ndarray, np.ndarray, tuple]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray, tuple]: """Load element numbers, coordinates and atomic charges.""" atnums = np.empty(natoms) atcoords = np.empty((natoms, 3)) @@ -111,7 +112,7 @@ def _load_helper_atoms( return atnums, atcoords, atchgs, attypes -def _load_helper_bonds(lit: LineIterator, nbonds: int) -> Tuple[np.ndarray]: +def _load_helper_bonds(lit: LineIterator, nbonds: int) -> tuple[np.ndarray]: """Load bond information. Each line in a bond definition has the following structure @@ -146,7 +147,7 @@ def load_many(lit: LineIterator) -> Iterator[dict]: while True: try: yield load_one(lit) - except IOError: + except OSError: return diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 9e78d8458..75477f728 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -26,7 +26,7 @@ """ import copy -from typing import TextIO, Tuple, Union +from typing import TextIO, Union import attr import numpy as np @@ -228,7 +228,7 @@ def _load_low(lit: LineIterator) -> dict: def _load_helper_atoms( lit: LineIterator, cunit: float -) -> Tuple[np.ndarray, np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """Load element numbers and coordinates.""" atnums = [] atcorenums = [] @@ -282,7 +282,7 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: return MolecularBasis(shells, CONVENTIONS, "L2") -def _load_helper_coeffs(lit: LineIterator) -> Tuple: +def _load_helper_coeffs(lit: LineIterator) -> tuple: """Load the orbital coefficients.""" occsa = [] coeffsa = [] @@ -665,7 +665,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold 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={} 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. @@ -757,7 +757,7 @@ def dump_one(f: TextIO, data: IOData): f.write("[Molden Format]\n") if data.title is not None: f.write("[Title]\n") - f.write(" {}\n".format(data.title)) + f.write(f" {data.title}\n") # Print the elements numbers and the coordinates f.write("[Atoms] AU\n") @@ -766,15 +766,14 @@ def dump_one(f: TextIO, data: IOData): 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"{num2sym[atnum].ljust(2):2s} {iatom + 1:3d} {atcorenum:3.0f} " + f"{x:25.18f} {y:25.18f} {z:25.18f}\n" ) 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 OSError("A Gaussian orbital basis is required to write a molden file.") obasis = data.obasis # Figure out the pure/Cartesian situation. Note that the Molden @@ -786,9 +785,8 @@ 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 " + raise OSError( + "Molden format does not support mixed pure+Cartesian functions for one " "angular momentum." ) else: @@ -824,9 +822,9 @@ def dump_one(f: TextIO, data: IOData): # 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(f" {angmom_its(angmom):1s} {shell.nprim:3d} 1.00\n") for exponent, coeff in zip(shell.exponents, shell.coeffs[:, iangmom]): - f.write("{:20.10f} {:20.10f}\n".format(exponent, coeff)) + f.write(f"{exponent:20.10f} {coeff:20.10f}\n") f.write("\n") # Get the permutation to convert the orbital coefficients to Molden conventions. @@ -885,4 +883,4 @@ def _dump_helper_orb(f, spin, occs, coeffs, energies, irreps): # 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(f"{ibasis + 1:4d} {coeffs[ibasis, ifn]:.17e}\n") diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 6848a8ad7..833088144 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -23,7 +23,7 @@ `Orca <https://sites.google.com/site/orcainputlibrary/>`_. """ -from typing import List, TextIO, Tuple +from typing import TextIO import numpy as np @@ -40,8 +40,8 @@ PATTERNS = ["*.mkl"] -def _load_helper_charge_spinpol(lit: LineIterator) -> List[int]: - charge, spinmult = [int(word) for word in next(lit).split()] +def _load_helper_charge_spinpol(lit: LineIterator) -> list[int]: + charge, spinmult = (int(word) for word in next(lit).split()) spinpol = spinmult - 1 return charge, spinpol @@ -57,7 +57,7 @@ def _load_helper_charges(lit: LineIterator) -> dict: return {"mulliken": np.array(atcharges)} -def _load_helper_atoms(lit: LineIterator) -> Tuple[np.ndarray, np.ndarray]: +def _load_helper_atoms(lit: LineIterator) -> tuple[np.ndarray, np.ndarray]: atnums = [] atcoords = [] for line in lit: @@ -92,9 +92,7 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: elif nbasis_shell == len(CONVENTIONS[(angmom, "p")]): kind = "p" else: - lit.error( - "Cannot interpret angmom={} with nbasis_shell={}".format(angmom, nbasis_shell) - ) + lit.error(f"Cannot interpret angmom={angmom} with nbasis_shell={nbasis_shell}") exponents = [] coeffs = [] for line in lit: @@ -108,7 +106,7 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: return MolecularBasis(shells, CONVENTIONS, "L2") -def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> Tuple[np.ndarray, np.ndarray]: +def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[np.ndarray, np.ndarray]: coeffs = [] energies = [] irreps = [] @@ -275,7 +273,7 @@ def dump_one(f: TextIO, data: IOData): # CHAR_MUL f.write("$CHAR_MULT\n") - f.write(" {:.0f} {:.0f}\n".format(data.charge, data.spinpol + 1)) + f.write(f" {data.charge:.0f} {data.spinpol + 1:.0f}\n") f.write("$END\n") f.write("\n") @@ -283,7 +281,7 @@ def dump_one(f: TextIO, data: IOData): atcoords = data.atcoords / angstrom 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(f" {n:d} {coord[0]: ,.6f} {coord[1]: ,.6f} {coord[2]: ,.6f}\n") f.write("$END\n") f.write("\n") @@ -291,7 +289,7 @@ def dump_one(f: TextIO, data: IOData): if "mulliken" in data.atcharges: f.write("$CHARGES\n") for charge in data.atcharges["mulliken"]: - f.write(" {: ,.6f}\n".format(charge)) + f.write(f" {charge: ,.6f}\n") f.write("$END\n") f.write("\n") @@ -305,9 +303,9 @@ def dump_one(f: TextIO, data: IOData): 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(f" {nbasis} {angmom_its(angmom).capitalize():1s} 1.00\n") for exponent, coeff in zip(shell.exponents, shell.coeffs[:, iangmom]): - f.write("{:20.10f} {:17.10f}\n".format(exponent, coeff)) + f.write(f"{exponent:20.10f} {coeff:17.10f}\n") f.write("\n") f.write("$END\n") f.write("\n") @@ -364,15 +362,15 @@ def _dump_helper_coeffs(f, data, spin=None): else: irreps = ["a1g"] * norb else: - raise IOError("A spin must be specified") + raise OSError("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]]) + en = " ".join([f" {e: ,.12f}" for e in ener[j : j + 5]]) + irre = " ".join([f"{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]) + coeffs = " ".join([f" {c: ,.12f}" for c in orb]) f.write(coeffs + "\n") f.write(" $END\n") @@ -390,9 +388,9 @@ def _dump_helper_occ(f, data, spin=None): norb = data.mo.norba occ = data.mo.occs else: - raise IOError("A spin must be specified") + raise OSError("A spin must be specified") for j in range(0, norb, 5): - occs = " ".join([" {: ,.7f}".format(o) for o in occ[j : j + 5]]) + occs = " ".join([f" {o: ,.7f}" for o in occ[j : j + 5]]) f.write(occs + "\n") f.write(" $END\n") diff --git a/iodata/formats/orcalog.py b/iodata/formats/orcalog.py index 7630ba10e..e5619f0ec 100644 --- a/iodata/formats/orcalog.py +++ b/iodata/formats/orcalog.py @@ -18,7 +18,7 @@ # -- """Orca output file format.""" -from typing import TextIO, Tuple +from typing import TextIO import numpy as np @@ -87,7 +87,7 @@ def _helper_number_atoms(lit: LineIterator) -> int: return natom -def _helper_geometry(lit: TextIO, natom: int) -> Tuple[np.ndarray, np.ndarray]: +def _helper_geometry(lit: TextIO, natom: int) -> tuple[np.ndarray, np.ndarray]: """Load coordinates form a ORCA output file format. Parameters @@ -119,7 +119,7 @@ def _helper_geometry(lit: TextIO, natom: int) -> Tuple[np.ndarray, np.ndarray]: return atnums, atcoords -def _helper_scf_energies(lit: TextIO) -> Tuple[np.ndarray, np.ndarray]: +def _helper_scf_energies(lit: TextIO) -> tuple[np.ndarray, np.ndarray]: """Load energies from each SCF cycle from a ORCA output file format. Parameters diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index d33fc078c..c7521b3a9 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -23,7 +23,8 @@ http://www.wwpdb.org/documentation/file-format-content/format33/v3.3.html """ -from typing import Iterator, TextIO +from collections.abc import Iterator +from typing import TextIO import numpy as np @@ -239,7 +240,7 @@ def load_many(lit: LineIterator) -> Iterator[dict]: while True: try: yield load_one(lit) - except IOError: + except OSError: return @@ -300,8 +301,7 @@ def dump_one(f: TextIO, data: IOData): # Write connection in groups of max 4 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] + f"{iatom1 + 1:5d}" for iatom1 in iatoms1[ichunk * 4 : ichunk * 4 + 4] ) conect_line = f"CONECT{iatom0 + 1:5d}{other_atoms_str}" print(conect_line, file=f) diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 3ebca6cc3..a15a86322 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -21,8 +21,6 @@ This module will load Q-Chem log file into IODATA. """ -from typing import Tuple - import numpy as np from ..docstrings import document_load_one @@ -229,7 +227,7 @@ def load_qchemlog_low(lit: LineIterator) -> dict: # pylint: disable=too-many-br return data -def _helper_rem_job(lit: LineIterator) -> Tuple: +def _helper_rem_job(lit: LineIterator) -> tuple: """Load job specifications from Q-Chem output file format.""" data_rem = {} for line in lit: @@ -286,7 +284,7 @@ def _helper_energy(lit: LineIterator): return energy -def _helper_orbital_energies_restricted(lit: LineIterator) -> Tuple: +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) @@ -295,7 +293,7 @@ def _helper_orbital_energies_restricted(lit: LineIterator) -> Tuple: return mo_a_occupied, mo_a_unoccupied -def _helper_orbital_energies_unrestricted(lit: LineIterator) -> Tuple: +def _helper_orbital_energies_unrestricted(lit: LineIterator) -> tuple: """Load occupied and virtual orbital energies for unrestricted calculation.""" subdata = {} # alpha occupied MOs @@ -342,7 +340,7 @@ def _helper_mulliken(lit: LineIterator) -> np.ndarray: return np.array(mulliken_charges, dtype=float) -def _helper_dipole_moments(lit: LineIterator) -> Tuple: +def _helper_dipole_moments(lit: LineIterator) -> tuple: """Load cartesian multiple moments.""" for line in lit: if line.strip().startswith("Dipole Moment (Debye)"): @@ -388,7 +386,7 @@ def _helper_hessian(lit: LineIterator, natom: int) -> np.ndarray: return hessian.astype(float) -def _helper_vibrational(lit: LineIterator) -> Tuple: +def _helper_vibrational(lit: LineIterator) -> tuple: """Load vibrational analysis.""" for line in lit: if line.strip().startswith("This Molecule has"): @@ -406,7 +404,7 @@ def _helper_vibrational(lit: LineIterator) -> Tuple: return imaginary_freq, vib_energy, atmasses -def _helper_thermo(lit: LineIterator) -> Tuple: +def _helper_thermo(lit: LineIterator) -> tuple: """Load thermodynamics properties.""" enthalpy_dict = {} entropy_dict = {} diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index 75bb19de1..d94aa5e1b 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -29,7 +29,8 @@ https://en.wikipedia.org/wiki/Chemical_table_file """ -from typing import Iterator, TextIO +from collections.abc import Iterator +from typing import TextIO import numpy as np @@ -112,14 +113,14 @@ def dump_one(f: TextIO, data: IOData): 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) + print(f"{data.natom:3d}{nbond:3d} 0 0 0 0 0 0 0999 V2000", 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) 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(f"{iatom + 1:3d}{jatom + 1:3d}{bondtype:3d} 0 0 0 0", file=f) print("M END", file=f) print("$$$$", file=f) diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 4c71a47b6..ab5e063ea 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -25,7 +25,7 @@ Gaussian functions. """ -from typing import List, TextIO, Tuple +from typing import TextIO import numpy as np @@ -121,7 +121,7 @@ PRIMITIVE_NAMES = sum([CONVENTIONS[(angmom, "c")] for angmom in range(6)], []) -def _load_helper_num(lit: LineIterator) -> List[int]: +def _load_helper_num(lit: LineIterator) -> list[int]: """Read number of orbitals, primitives and atoms.""" line = next(lit) if not line.startswith("G"): @@ -133,7 +133,7 @@ def _load_helper_num(lit: LineIterator) -> List[int]: return num_mo, nprim, num_atoms -def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> Tuple[np.ndarray, np.ndarray]: +def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> tuple[np.ndarray, np.ndarray]: """Read the coordinates of the atoms.""" atnums = np.empty(num_atoms, int) atcoords = np.empty((num_atoms, 3), float) @@ -170,7 +170,7 @@ def _load_helper_section( return np.array(section, dtype=dtype) -def _load_helper_mo(lit: LineIterator, nprim: int) -> Tuple[int, float, float, np.ndarray]: +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"): @@ -206,7 +206,7 @@ def _load_helper_multiwfn(lit: LineIterator, num_mo: int) -> np.ndarray: return np.empty((0,), dtype=int) -def load_wfn_low(lit: LineIterator) -> Tuple: +def load_wfn_low(lit: LineIterator) -> tuple: """Load data from a WFN file into arrays. Parameters @@ -256,7 +256,7 @@ def load_wfn_low(lit: LineIterator) -> Tuple: # pylint: disable=too-many-branches def build_obasis( icenters: np.ndarray, type_assignments: np.ndarray, exponents: np.ndarray, lit: LineIterator -) -> Tuple[MolecularBasis, np.ndarray]: +) -> tuple[MolecularBasis, np.ndarray]: """Construct a basis set using the arrays read from a WFN or WFX file. Parameters @@ -454,7 +454,7 @@ def load_one(lit: LineIterator) -> dict: } -def _format_helper_section(header: str, skip: int, spec: str, nline: int) -> Tuple[str, int]: +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) diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 778e33f34..790b254bb 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -22,7 +22,8 @@ """ import warnings -from typing import Iterator, TextIO +from collections.abc import Iterator +from typing import TextIO import numpy as np @@ -141,7 +142,7 @@ def load_data_wfx(lit: LineIterator) -> dict: elif key in lbs_other: result[lbs_other[key]] = value else: - warnings.warn("Not recognized section label, skip {0}".format(key)) + warnings.warn(f"Not recognized section label, skip {key}") # reshape some arrays result["atcoords"] = result["atcoords"].reshape(-1, 3) @@ -182,7 +183,7 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: # set start & end of the section and add it to data dictionary section_start = line if section_start in data.keys(): - lit.error("Section with tag={} is repeated!".format(section_start)) + lit.error(f"Section with tag={section_start} is repeated!") data[section_start] = [] section_end = line[:1] + "/" + line[1:] # special handling of <Molecular Orbital Primitive Coefficients> section @@ -192,7 +193,7 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: elif section_start is not None and line.startswith("</"): # In some cases, closing tags have a different number of spaces. 8-[ if line.replace(" ", "") != section_end.replace(" ", ""): - lit.error("Expecting line {} but got {}.".format(section_end, line)) + lit.error(f"Expecting line {section_end} but got {line}.") # reset section_start variable to signal that section ended section_start = None # handle <MO Number> line under <Molecular Orbital Primitive Coefficients> section @@ -207,7 +208,7 @@ def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: # check if last section was closed if section_start is not None: - lit.error("Section {} is not closed at end of file.".format(section_start)) + lit.error(f"Section {section_start} is not closed at end of file.") # check required section tags if required_tags is not None: for section_tag in required_tags: @@ -419,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(f"{item[0]: ,.14E} {item[1]: ,.14E} {item[2]: ,.14E}", file=f) print("</Nuclear Cartesian Coordinates>", file=f) # write net charge, number of electrons, number of alpha electrons, and number beta electrons @@ -440,7 +441,7 @@ 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([f"{c:d}" for c in prim_centers[j : j + 10]]), file=f) print("</Primitive Centers>", file=f) # write primitive types @@ -452,14 +453,14 @@ def dump_one(f: TextIO, data: IOData): 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([f"{c:d}" 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([f"{e: ,.14E}" for e in exponents[j : j + 4]]), file=f) print("</Primitive Exponents>", file=f) # write molecular orbital occupation numbers @@ -482,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([f"{c: ,.14E}" 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) @@ -496,7 +497,7 @@ def dump_one(f: TextIO, data: IOData): for atom in nuc_cart_energy_grad: print( atom[0], - "{: ,.14E} {: ,.14E} {: ,.14E}".format(atom[1][0], atom[1][1], atom[1][2]), + f"{atom[1][0]: ,.14E} {atom[1][1]: ,.14E} {atom[1][2]: ,.14E}", file=f, ) print("</Nuclear Cartesian Energy Gradients>", file=f) @@ -524,7 +525,7 @@ def _write_xml_single(tag: str, info: [str, int], file: TextIO) -> None: 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(f"{info: ,.14E}", file=file) print("</" + tag.lstrip("<"), file=file) @@ -540,5 +541,5 @@ def _write_xml_iterator_scientific(tag: str, info: Iterator, file: TextIO) -> No """Write list of arrays to file.""" print(tag, file=file) for info_line in info: - print("{: ,.14E}".format(info_line), file=file) + print(f"{info_line: ,.14E}", file=file) print("</" + tag.lstrip("<"), file=file) diff --git a/iodata/formats/xyz.py b/iodata/formats/xyz.py index de10aaecd..217156c7e 100644 --- a/iodata/formats/xyz.py +++ b/iodata/formats/xyz.py @@ -53,7 +53,8 @@ """ -from typing import Iterator, TextIO +from collections.abc import Iterator +from typing import TextIO import numpy as np @@ -80,7 +81,7 @@ (), int, (lambda word: int(word) if word.isdigit() else sym2num[word.title()]), - (lambda atnum: "{:2s}".format(num2sym[atnum])), + (lambda atnum: f"{num2sym[atnum]:2s}"), ), ( "atcoords", @@ -88,7 +89,7 @@ (3,), float, (lambda word: float(word) * angstrom), - (lambda value: "{:15.10f}".format(value / angstrom)), + (lambda value: f"{value / angstrom:15.10f}"), ), ] diff --git a/iodata/periodic.py b/iodata/periodic.py index e73228d72..40dadb7f9 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -18,12 +18,10 @@ # -- """Periodic table module.""" -from typing import Dict - __all__ = ["num2sym", "sym2num"] -num2sym: Dict[int, str] = { +num2sym: dict[int, str] = { 1: "H", 2: "He", 3: "Li", @@ -144,7 +142,7 @@ 118: "Og", } -sym2num: Dict[str, int] = dict((value, key) for key, value in num2sym.items()) +sym2num: dict[str, int] = dict((value, key) for key, value in num2sym.items()) # Labels used for bond types. @@ -168,4 +166,4 @@ 11: "nc", # not connected } -bond2num: Dict[str, int] = dict((value, key) for key, value in num2bond.items()) +bond2num: dict[str, int] = dict((value, key) for key, value in num2bond.items()) diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 780a4ecec..53295efbb 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -59,7 +59,7 @@ 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 = "{}/{}".format(tmpdir, "aelta.cube") dump_one(mol1, fn_cube2) mol2 = load_one(fn_cube2) @@ -99,7 +99,7 @@ def test_load_dump_h2o_5points(tmpdir): 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: + with open(fn_cube1) as f: content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 @@ -113,7 +113,7 @@ def test_load_dump_ch4_6points(tmpdir): 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: + with open(fn_cube1) as f: content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 @@ -127,7 +127,7 @@ def test_load_dump_nh3_7points(tmpdir): 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: + with open(fn_cube1) as f: content1 = f.read().split("\n", 2)[-1] content2 = fn_cube2.read().split("\n", 2)[-1] assert content1 == content2 diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index b3bba98c6..070c7e316 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -272,7 +272,7 @@ 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(f"2h-azirine-{key}.fchk") assert mol.obasis.nbasis == 33 dm = mol.one_rdms["post_scf_ao"] assert_equal(dm[0, 0], numbers[0]) @@ -299,7 +299,7 @@ def test_load_azirine_mp3(): 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(f"nitrogen-{key}.fchk") assert mol.obasis.nbasis == 9 dm = mol.one_rdms["post_scf_ao"] assert_equal(dm[0, 0], numbers[0]) @@ -332,7 +332,7 @@ def test_load_nitrogen_mp3(): 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(f"2h-azirine-{key}.fchk") olp = compute_overlap(mol.obasis, mol.atcoords) check_orthonormal(mol.mo.coeffs, olp) dm = mol.one_rdms["post_scf_ao"] diff --git a/iodata/test/test_inputs.py b/iodata/test/test_inputs.py index 79395672d..9bd8b7cbc 100644 --- a/iodata/test/test_inputs.py +++ b/iodata/test/test_inputs.py @@ -43,9 +43,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) as ifn: content = "".join(ifn.readlines()) - with open(fname_expected, "r") as efn: + with open(fname_expected) as efn: expected = "".join(efn.readlines()) assert content == expected diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 47c6713d9..7b1b8658a 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -181,7 +181,7 @@ def test_inout_qcschema_molecule(tmpdir, filename, nwarn): fn_tmp = os.path.join(tmpdir, "test_qcschema_mol.json") dump_one(mol, fn_tmp) - with open(fn_tmp, "r") as mol2_in: + with open(fn_tmp) as mol2_in: mol2 = json.load(mol2_in) # Check that prior provenance info is kept @@ -211,7 +211,7 @@ def test_inout_molssi_qcschema_molecule(tmpdir, filename): fn_tmp = os.path.join(tmpdir, "test_qcschema_mol.json") dump_one(mol, fn_tmp) - with open(fn_tmp, "r") as mol2_in: + with open(fn_tmp) as mol2_in: mol2 = json.load(mol2_in) # Extra processing for testing: @@ -244,7 +244,7 @@ def test_ghost(tmpdir): np.testing.assert_allclose(mol.atcorenums, [8, 1, 1, 0, 0, 0, 0, 0, 0]) fn_tmp = os.path.join(tmpdir, "test_ghost.json") dump_one(mol, fn_tmp) - with open(fn_tmp, "r") as mol2_in: + with open(fn_tmp) as mol2_in: mol2 = json.load(mol2_in) assert mol2["real"] == [True] * 3 + [False] * 6 @@ -320,7 +320,7 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): fn_tmp = os.path.join(tmpdir, "test_input_mol.json") dump_one(mol, fn_tmp) - with open(fn_tmp, "r") as mol2_in: + with open(fn_tmp) as mol2_in: mol2 = json.load(mol2_in) # Check that prior provenance info is kept @@ -392,7 +392,7 @@ def test_inout_qcschema_output(tmpdir, filename): fn_tmp = os.path.join(tmpdir, "test_input_mol.json") dump_one(mol, fn_tmp) - with open(fn_tmp, "r") as mol2_in: + with open(fn_tmp) as mol2_in: mol2 = json.load(mol2_in) # Check that prior provenance info is kept diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index ad23ab679..5a4cf4abb 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -76,7 +76,7 @@ def check_water(mol): (3,), float, (lambda word: -float(word)), - (lambda value: "{:15.10f}".format(-value)), + (lambda value: f"{-value:15.10f}"), ), ] @@ -141,7 +141,7 @@ def test_load_many(): mols = list(load_many(str(fn_xyz))) assert len(mols) == 5 for imol, mol in enumerate(mols): - assert mol.title == "Frame {}".format(imol) + assert mol.title == f"Frame {imol}" assert_equal(mol.atnums, [8, 1, 1]) assert mol.atcoords.shape == (3, 3) assert_allclose(mols[0].atcoords[2] / angstrom, [2.864329, 0.114369, 3.3635]) diff --git a/iodata/utils.py b/iodata/utils.py index ab85610d0..5e8cfe927 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -19,7 +19,6 @@ """Utility functions module.""" import warnings -from typing import Tuple import attr import numpy as np @@ -105,7 +104,7 @@ def error(self, msg: str): Message to raise alongside filename and line number. """ - raise FileFormatError("{}:{} {}".format(self.filename, self.lineno, msg)) + raise FileFormatError(f"{self.filename}:{self.lineno} {msg}") def warn(self, msg: str): """Raise a warning while reading a file. @@ -116,7 +115,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(f"{self.filename}:{self.lineno} {msg}", FileFormatWarning, 2) def back(self, line): """Go one line back and decrease the lineno attribute by one.""" @@ -205,7 +204,7 @@ def volume(cellvecs: np.ndarray) -> float: raise ValueError("Argument cellvecs should be of shape (x, 3), where x is in {1, 2, 3}") -def derive_naturals(dm: np.ndarray, overlap: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: +def derive_naturals(dm: np.ndarray, overlap: np.ndarray) -> tuple[np.ndarray, np.ndarray]: """Derive natural orbitals from a given density matrix. Parameters @@ -264,7 +263,7 @@ def check_dm(dm: np.ndarray, overlap: np.ndarray, eps: float = 1e-4, occ_max: fl if occupations.min() < -eps: raise ValueError( "The density matrix has eigenvalues considerably smaller than " - "zero. error=%e" % (occupations.min()) + f"zero. error={occupations.min():e}" ) if occupations.max() > occ_max + eps: raise ValueError( diff --git a/pyproject.toml b/pyproject.toml index 113bc1964..9af339563 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ local_scheme = "no-local-version" [tool.ruff] line-length = 100 -target-version = "py311" +target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I"] +select = ["E", "F", "I", "UP"] diff --git a/tools/harmonics.py b/tools/harmonics.py index 62b5fdc25..dd54263f4 100644 --- a/tools/harmonics.py +++ b/tools/harmonics.py @@ -215,10 +215,10 @@ def print_latex(tfs): def iter_pure_labels(ell): """Iterate over labels for pure functions.""" - yield "C_{{{}0}}".format(ell) + yield f"C_{{{ell}0}}" for m in range(1, ell + 1): - yield "C_{{{}{}}}".format(ell, m) - yield "S_{{{}{}}}".format(ell, m) + yield f"C_{{{ell}{m}}}" + yield f"S_{{{ell}{m}}}" def tostr(v): """Format an sympy expression as Latex code.""" @@ -229,7 +229,7 @@ def tostr(v): for ell, tf in enumerate(tfs): npure, ncart = tf.shape print(r"\left(\begin{array}{c}") - print(" ", r" \\ ".join(["b({})".format(label) for label in iter_pure_labels(ell)])) + print(" ", r" \\ ".join([f"b({label})" for label in iter_pure_labels(ell)])) print(r"\end{array}\right)") print(" &=") print(r"\left(\begin{array}{" + ("c" * ncart) + "}") @@ -246,7 +246,7 @@ def tostr(v): spoly = "x" * nx + "y" * ny + "z" * nz if spoly == "": spoly = "1" - els.append("b({})".format(spoly)) + els.append(f"b({spoly})") print(" ", r" \\ ".join(els)) print(r"\end{array}\right)") if ell != len(tfs) - 1: @@ -265,13 +265,13 @@ def tostr(v): for ell, tf in enumerate(tfs): npure, ncart = tf.shape - print("tf{} = np.array([".format(ell)) + print(f"tf{ell} = np.array([") for ipure in range(npure): print( " [{}],".format(", ".join([tostr(tf[ipure, icart]) for icart in range(ncart)])) ) print("])") - print("tfs = [{}]".format(", ".join("tf{}".format(ell) for ell in range(len(tfs))))) + print("tfs = [{}]".format(", ".join(f"tf{ell}" for ell in range(len(tfs))))) def test_manual(): From 2cdf8c61cfbb5c0cfac9952c9682f54f89c78d0d Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 10:56:57 +0200 Subject: [PATCH 102/144] Enable flake8-bugbear linting --- iodata/docstrings.py | 32 +++++++++---- iodata/formats/gamess.py | 2 +- iodata/formats/gaussianlog.py | 2 +- iodata/formats/json.py | 37 ++++++++------ iodata/formats/wfx.py | 4 +- iodata/test/test_orbitals.py | 90 +++++++++++++++++------------------ iodata/utils.py | 2 +- pyproject.toml | 2 +- 8 files changed, 97 insertions(+), 74 deletions(-) diff --git a/iodata/docstrings.py b/iodata/docstrings.py index fd6505b61..95f6366bc 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -33,9 +33,11 @@ def _document_load( fmt: str, guaranteed: list[str], ifpresent: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): + if kwdocs is None: + kwdocs = {} ifpresent = ifpresent or [] def decorator(func): @@ -91,7 +93,7 @@ def document_load_one( fmt: str, guaranteed: list[str], ifpresent: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): """Decorate a load_one function to generate a docstring. @@ -117,6 +119,8 @@ def document_load_one( A decorator function. """ + if kwdocs is None: + kwdocs = {} return _document_load(LOAD_ONE_DOC_TEMPLATE, fmt, guaranteed, ifpresent, kwdocs, notes) @@ -146,7 +150,7 @@ def document_load_many( fmt: str, guaranteed: list[str], ifpresent: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): """Decorate a load_many function to generate a docstring. @@ -172,6 +176,8 @@ def document_load_many( A decorator function. """ + if kwdocs is None: + kwdocs = {} return _document_load(LOAD_MANY_DOC_TEMPLATE, fmt, guaranteed, ifpresent, kwdocs, notes) @@ -180,9 +186,11 @@ def _document_dump( fmt: str, required: list[str], optional: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): + if kwdocs is None: + kwdocs = {} optional = optional or [] def decorator(func): @@ -236,7 +244,7 @@ def document_dump_one( fmt: str, required: list[str], optional: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): """Decorate a dump_one function to generate a docstring. @@ -262,6 +270,8 @@ def document_dump_one( A decorator function. """ + if kwdocs is None: + kwdocs = {} return _document_dump(DUMP_ONE_DOC_TEMPLATE, fmt, required, optional, kwdocs, notes) @@ -288,7 +298,7 @@ def document_dump_many( fmt: str, required: list[str], optional: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): """Decorate a dump_many function to generate a docstring. @@ -314,6 +324,8 @@ def document_dump_many( A decorator function. """ + if kwdocs is None: + kwdocs = {} return _document_dump(DUMP_MANY_DOC_TEMPLATE, fmt, required, optional, kwdocs, notes) @@ -322,9 +334,11 @@ def _document_write( fmt: str, required: list[str], optional: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): + if kwdocs is None: + kwdocs = {} optional = optional or [] def decorator(func): @@ -381,7 +395,7 @@ def document_write_input( fmt: str, required: list[str], optional: list[str] = None, - kwdocs: dict[str, str] = {}, + kwdocs: dict[str, str] = None, notes: str = None, ): """Decorate a write_input function to generate a docstring. @@ -407,4 +421,6 @@ def document_write_input( A decorator function. """ + if kwdocs is None: + kwdocs = {} return _document_write(WRITE_INPUT_DOC_TEMPLATE, fmt, required, optional, kwdocs, notes) diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index 038d9bdfe..0428420d7 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -48,7 +48,7 @@ def _read_data(lit: LineIterator) -> tuple: def _read_coordinates(lit: LineIterator, result: dict) -> tuple: """Extract ``numbers`` and ``coordinates`` from the punch file.""" - for i in range(2): + for _ in range(2): next(lit) natom = len(result["symbols"]) # if the data are already read before, just overwrite them diff --git a/iodata/formats/gaussianlog.py b/iodata/formats/gaussianlog.py index f147ccf7a..b9341550a 100644 --- a/iodata/formats/gaussianlog.py +++ b/iodata/formats/gaussianlog.py @@ -124,7 +124,7 @@ def _load_fourindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: """ result = np.zeros((nbasis, nbasis, nbasis, nbasis)) # Skip first six lines - for i in range(6): + for _i in range(6): next(lit) # Start reading elements until a line is encountered that does not start # with ' I=' diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 7540b175f..39837c8c6 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -648,7 +648,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: f"{lit.filename}: QCSchema files should have a `schema_name` key." "Attempting to determine schema type...", FileFormatWarning, - 2, + stacklevel=2, ) # Geometry is required in any molecule schema if "geometry" in result: @@ -673,7 +673,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: f"{lit.filename}: QCSchema files should have a `schema_version` key." "Attempting to load without version number.", FileFormatWarning, - 2, + stacklevel=2, ) if schema_name == "qcschema_molecule": @@ -763,7 +763,7 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: warn( f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, - 2, + stacklevel=2, ) for key in topology_keys: if key not in mol: @@ -789,7 +789,7 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "Some QCSchema writers omit this key for default value 0.0," "Ensure this value is correct.", FileFormatWarning, - 2, + stacklevel=2, ) formal_charge = 0.0 else: @@ -804,7 +804,7 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "Some QCSchema writers omit this key for default value 1," "Ensure this value is correct.", FileFormatWarning, - 2, + stacklevel=2, ) topology_dict["spinpol"] = 0 else: @@ -827,7 +827,7 @@ def _parse_topology_keys(mol: dict, lit: LineIterator) -> dict: "{}: Both `masses` and `mass_numbers` given. " "Both values will be written to `extra` dict.", FileFormatWarning, - 2, + stacklevel=2, ) extra_dict["mass_numbers"] = np.array(mol["mass_numbers"]) extra_dict["masses"] = np.array(mol["masses"]) @@ -940,7 +940,7 @@ def _version_check(result: dict, max_version: float, schema_name: str, lit: Line f"{lit.filename}: Unknown {schema_name} version {version}, " "loading may produce invalid results", FileFormatWarning, - 2, + stacklevel=2, ) return version @@ -1083,7 +1083,7 @@ def _parse_input_keys(result: dict, lit: LineIterator) -> dict: warn( f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, - 2, + stacklevel=2, ) for key in input_keys: if key not in result: @@ -1215,14 +1215,17 @@ def _parse_model(model: dict, lit: LineIterator) -> dict: model_dict["lot"] = model["method"] # QCEngineRecords doesn't give an empty string for basis-free methods, omits req'd key instead if "basis" not in model: - warn(f"{lit.filename}: Model `basis` key should be given. Assuming basis-free method.") + warn( + f"{lit.filename}: Model `basis` key should be given. Assuming basis-free method.", + stacklevel=2, + ) elif isinstance(model["basis"], str): if model["basis"] == "": warn( f"{lit.filename}: QCSchema `basis` could not be read and will be omitted." "Unless model is for a basis-free method, check input file.", FileFormatWarning, - 2, + stacklevel=2, ) else: model_dict["obasis_name"] = model["basis"] @@ -1256,13 +1259,17 @@ def _parse_protocols(protocols: dict, lit: LineIterator) -> dict: warn( "{}: Protocols `wavefunction` key not specified, no properties will be kept.", FileFormatWarning, - 2, + stacklevel=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, + stacklevel=2, + ) keep_stdout = True else: keep_stdout = protocols["stdout"] @@ -1338,7 +1345,7 @@ def _parse_output_keys(result: dict, lit: LineIterator) -> dict: warn( f"{lit.filename}: QCSchema files should have a '{key}' key.", FileFormatWarning, - 2, + stacklevel=2, ) for key in output_keys: if key not in result: @@ -1494,7 +1501,7 @@ def _dump_qcschema_molecule(data: IOData) -> dict: "`charge` and `spinpol` should be given to write qcschema_molecule file:" "QCSchema defaults to charge = 0 and multiplicity = 1 if no values given.", FileFormatWarning, - 2, + stacklevel=2, ) if data.charge is not None: molecule_dict["molecular_charge"] = data.charge @@ -1691,7 +1698,7 @@ def _dump_qcschema_output(data: IOData) -> dict: "No basis name given. QCSchema assumes this signifies a basis-free method; to" "avoid this warning, specify `obasis_name` as an empty string.", FileFormatWarning, - 2, + stacklevel=2, ) if "basis" in data.extra["input"]["model"]: raise NotImplementedError("qcschema_basis is not yet supported in IOData.") diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 790b254bb..eab00ab3b 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -142,7 +142,7 @@ def load_data_wfx(lit: LineIterator) -> dict: elif key in lbs_other: result[lbs_other[key]] = value else: - warnings.warn(f"Not recognized section label, skip {key}") + warnings.warn(f"Not recognized section label, skip {key}", stacklevel=2) # reshape some arrays result["atcoords"] = result["atcoords"].reshape(-1, 3) @@ -372,7 +372,7 @@ def dump_one(f: TextIO, data: IOData): 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] - for j in range(shell.nprim): + for _j in range(shell.nprim): mo_coeffs[index_mo_new : index_mo_new + n] = c index_mo_new += n index_mo_old += n diff --git a/iodata/test/test_orbitals.py b/iodata/test/test_orbitals.py index fb2dc781f..3fe5ce03d 100644 --- a/iodata/test/test_orbitals.py +++ b/iodata/test/test_orbitals.py @@ -264,23 +264,23 @@ def test_generalized_empty(): assert mo.nbasis is None assert mo.norb is None with pytest.raises(NotImplementedError): - mo.spinpol + _ = mo.spinpol with pytest.raises(NotImplementedError): - mo.occsa + _ = mo.occsa with pytest.raises(NotImplementedError): - mo.occsb + _ = mo.occsb with pytest.raises(NotImplementedError): - mo.coeffsa + _ = mo.coeffsa with pytest.raises(NotImplementedError): - mo.coeffsb + _ = mo.coeffsb with pytest.raises(NotImplementedError): - mo.energiesa + _ = mo.energiesa with pytest.raises(NotImplementedError): - mo.energiesb + _ = mo.energiesb with pytest.raises(NotImplementedError): - mo.irrepsa + _ = mo.irrepsa with pytest.raises(NotImplementedError): - mo.irrepsb + _ = mo.irrepsb def test_generalized_occs(): @@ -291,23 +291,23 @@ def test_generalized_occs(): assert mo.nbasis is None assert mo.norb == 7 with pytest.raises(NotImplementedError): - mo.spinpol + _ = mo.spinpol with pytest.raises(NotImplementedError): - mo.occsa + _ = mo.occsa with pytest.raises(NotImplementedError): - mo.occsb + _ = mo.occsb with pytest.raises(NotImplementedError): - mo.coeffsa + _ = mo.coeffsa with pytest.raises(NotImplementedError): - mo.coeffsb + _ = mo.coeffsb with pytest.raises(NotImplementedError): - mo.energiesa + _ = mo.energiesa with pytest.raises(NotImplementedError): - mo.energiesb + _ = mo.energiesb with pytest.raises(NotImplementedError): - mo.irrepsa + _ = mo.irrepsa with pytest.raises(NotImplementedError): - mo.irrepsb + _ = mo.irrepsb def test_generalized_coeffs(): @@ -319,23 +319,23 @@ def test_generalized_coeffs(): assert mo.nbasis == 5 # 5 *spatial* basis functions! assert mo.norb == 7 with pytest.raises(NotImplementedError): - mo.spinpol + _ = mo.spinpol with pytest.raises(NotImplementedError): - mo.occsa + _ = mo.occsa with pytest.raises(NotImplementedError): - mo.occsb + _ = mo.occsb with pytest.raises(NotImplementedError): - mo.coeffsa + _ = mo.coeffsa with pytest.raises(NotImplementedError): - mo.coeffsb + _ = mo.coeffsb with pytest.raises(NotImplementedError): - mo.energiesa + _ = mo.energiesa with pytest.raises(NotImplementedError): - mo.energiesb + _ = mo.energiesb with pytest.raises(NotImplementedError): - mo.irrepsa + _ = mo.irrepsa with pytest.raises(NotImplementedError): - mo.irrepsb + _ = mo.irrepsb def test_generalized_energies(): @@ -347,23 +347,23 @@ def test_generalized_energies(): assert mo.nbasis is None assert mo.norb == 7 with pytest.raises(NotImplementedError): - mo.spinpol + _ = mo.spinpol with pytest.raises(NotImplementedError): - mo.occsa + _ = mo.occsa with pytest.raises(NotImplementedError): - mo.occsb + _ = mo.occsb with pytest.raises(NotImplementedError): - mo.coeffsa + _ = mo.coeffsa with pytest.raises(NotImplementedError): - mo.coeffsb + _ = mo.coeffsb with pytest.raises(NotImplementedError): - mo.energiesa + _ = mo.energiesa with pytest.raises(NotImplementedError): - mo.energiesb + _ = mo.energiesb with pytest.raises(NotImplementedError): - mo.irrepsa + _ = mo.irrepsa with pytest.raises(NotImplementedError): - mo.irrepsb + _ = mo.irrepsb def test_generalized_irreps(): @@ -375,20 +375,20 @@ def test_generalized_irreps(): assert mo.nbasis is None assert mo.norb == 7 with pytest.raises(NotImplementedError): - mo.spinpol + _ = mo.spinpol with pytest.raises(NotImplementedError): - mo.occsa + _ = mo.occsa with pytest.raises(NotImplementedError): - mo.occsb + _ = mo.occsb with pytest.raises(NotImplementedError): - mo.coeffsa + _ = mo.coeffsa with pytest.raises(NotImplementedError): - mo.coeffsb + _ = mo.coeffsb with pytest.raises(NotImplementedError): - mo.energiesa + _ = mo.energiesa with pytest.raises(NotImplementedError): - mo.energiesb + _ = mo.energiesb with pytest.raises(NotImplementedError): - mo.irrepsa + _ = mo.irrepsa with pytest.raises(NotImplementedError): - mo.irrepsb + _ = mo.irrepsb diff --git a/iodata/utils.py b/iodata/utils.py index 5e8cfe927..3d84330e0 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -115,7 +115,7 @@ def warn(self, msg: str): Message to raise alongside filename and line number. """ - warnings.warn(f"{self.filename}:{self.lineno} {msg}", FileFormatWarning, 2) + warnings.warn(f"{self.filename}:{self.lineno} {msg}", FileFormatWarning, stacklevel=2) def back(self, line): """Go one line back and decrease the lineno attribute by one.""" diff --git a/pyproject.toml b/pyproject.toml index 9af339563..5891e324d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,4 +54,4 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I", "UP"] +select = ["E", "F", "I", "UP", "B"] From 3e862823d765c66155c39830d2dac3efe0f12fc6 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:22:20 +0200 Subject: [PATCH 103/144] Add pylint rules with some global exceptions --- iodata/attrutils.py | 1 - iodata/basis.py | 1 - iodata/docstrings.py | 1 - iodata/formats/cp2klog.py | 2 -- iodata/formats/fchk.py | 2 -- iodata/formats/fcidump.py | 2 +- iodata/formats/json.py | 1 - iodata/formats/molden.py | 8 ++------ iodata/formats/molekel.py | 10 +++------- iodata/formats/pdb.py | 2 +- iodata/formats/qchemlog.py | 31 +++++++++++++++---------------- iodata/formats/wfn.py | 1 - iodata/formats/wfx.py | 2 -- iodata/iodata.py | 5 ----- iodata/orbitals.py | 2 +- iodata/overlap.py | 4 +--- iodata/test/test_charmm.py | 1 - iodata/test/test_chgcar.py | 1 - iodata/test/test_cube.py | 1 - iodata/test/test_fchk.py | 1 - iodata/test/test_gromacs.py | 1 - iodata/test/test_iodata.py | 12 ------------ iodata/test/test_locpot.py | 1 - iodata/test/test_molden.py | 1 - iodata/test/test_molekel.py | 1 - iodata/test/test_mwfn.py | 1 - iodata/test/test_orbitals.py | 1 - iodata/test/test_orcalog.py | 1 - iodata/test/test_poscar.py | 1 - iodata/test/test_qchemlog.py | 2 -- iodata/utils.py | 2 +- pyproject.toml | 13 ++++++++++++- 32 files changed, 37 insertions(+), 79 deletions(-) diff --git a/iodata/attrutils.py b/iodata/attrutils.py index 9f970735e..3dadf5d94 100644 --- a/iodata/attrutils.py +++ b/iodata/attrutils.py @@ -34,7 +34,6 @@ def converter(array): return converter -# pylint: disable=too-many-branches def validate_shape(*shape_requirements: tuple): """Return a validator for the shape of an array or the length of an iterable. diff --git a/iodata/basis.py b/iodata/basis.py index 4e34a1d58..b826cc41b 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -222,7 +222,6 @@ def get_segmented(self): shells.append( Shell(shell.icenter, [angmom], [kind], shell.exponents, coeffs.reshape(-1, 1)) ) - # pylint: disable=no-member return attr.evolve(self, shells=shells) diff --git a/iodata/docstrings.py b/iodata/docstrings.py index 95f6366bc..299061ecf 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=dangerous-default-value """Docstring decorators for file format implementations.""" __all__ = [ diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 1770fece8..9b8b0eae4 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -163,7 +163,6 @@ def _read_cp2k_uncontracted_obasis(lit: LineIterator) -> MolecularBasis: return MolecularBasis(shells, CONVENTIONS, "L2") -# pylint: disable=inconsistent-return-statements def _read_cp2k_obasis(lit: LineIterator) -> dict: """Read atomic orbital basis set from a CP2K ATOM file object. @@ -366,7 +365,6 @@ def _fill_orbitals( """ -# pylint: disable=too-many-branches,too-many-statements @document_load_one( "CP2K ATOM outupt", ["atcoords", "atcorenums", "atnums", "energy", "mo", "obasis"], diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 31bc9c08a..8d4be258f 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -58,7 +58,6 @@ } -# pylint: disable=too-many-branches,too-many-statements @document_load_one( "Gaussian Formatted Checkpoint", [ @@ -395,7 +394,6 @@ def _load_fchk_low(lit: LineIterator, label_patterns: list[str] = None) -> dict: return result -# pylint: disable=too-many-branches def _load_fchk_field(lit: LineIterator, label_patterns: list[str]) -> tuple[str, object]: """Read a single field matching one of the given label_patterns. diff --git a/iodata/formats/fcidump.py b/iodata/formats/fcidump.py index 7deeb9bb9..9c17c3860 100644 --- a/iodata/formats/fcidump.py +++ b/iodata/formats/fcidump.py @@ -133,7 +133,7 @@ def dump_one(f: TextIO, data: IOData): # Write integrals and core energy two_mo = data.two_ints["two_mo"] - for i0 in range(nactive): # pylint: disable=too-many-nested-blocks + for i0 in range(nactive): for i1 in range(i0 + 1): for i2 in range(nactive): for i3 in range(i2 + 1): diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 39837c8c6..9237bc21a 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=too-many-branches, too-many-statements """QCSchema JSON file format. QCSchema defines four different subschema: diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 75477f728..5b3665e7a 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -110,7 +110,6 @@ def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: return result -# pylint: disable=too-many-branches,too-many-statements def _load_low(lit: LineIterator) -> dict: """Load data from a MOLDEN input file format, without trying to fix errors. @@ -472,7 +471,6 @@ def _fix_obasis_orca(obasis: MolecularBasis) -> MolecularBasis: correction = gob_cart_normalization(exponent, np.array([5, 0, 0])) if correction != 1.0: fixed_shell.coeffs[iprim, 0] /= correction - iprim += 1 return MolecularBasis(fixed_shells, orca_conventions, obasis.primitive_normalization) @@ -654,7 +652,6 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold is raised when no more corrections can be applied. """ - # pylint: disable=too-many-return-statements obasis = result["obasis"] atcoords = result["atcoords"] if result["mo"].kind == "restricted": @@ -804,9 +801,8 @@ def dump_one(f: TextIO, data: IOData): f.write("[5D]\n") else: f.write("[5D10F]\n") - else: - if angmom_kinds[3] == "p": - f.write("[7F]\n") + elif angmom_kinds[3] == "p": + f.write("[7F]\n") if angmom_kinds[4] == "p": f.write("[9G]\n") diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 833088144..ca692219b 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -49,8 +49,7 @@ def _load_helper_charge_spinpol(lit: LineIterator) -> list[int]: def _load_helper_charges(lit: LineIterator) -> dict: atcharges = [] for line in lit: - line = line.strip() - if line == "$END": + if line.strip() == "$END": break atcharges.append(float(line)) @@ -113,8 +112,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.strip() == "$END": break if in_orb == 0: # read a1g line @@ -150,15 +148,13 @@ def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[np.ndarray, np. def _load_helper_occ(lit: LineIterator) -> np.ndarray: occs = [] for line in lit: - line = line.strip() - if line == "$END": + if line.strip() == "$END": break for word in line.split(): occs.append(float(word)) return np.array(occs) -# pylint: disable=too-many-branches,too-many-statements @document_load_one( "Molekel", ["atcoords", "atnums", "mo", "obasis"], diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index c7521b3a9..fbadee7a6 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -143,7 +143,7 @@ def _parse_pdb_conect_line(line): @document_load_one("PDB", ["atcoords", "atnums", "atffparams", "extra"], ["title", "bonds"]) -def load_one(lit: LineIterator) -> dict: # pylint: disable=too-many-branches, too-many-statements +def load_one(lit: LineIterator) -> dict: """Do not edit this docstring. It will be overwritten.""" title_lines = [] compnd_lines = [] diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index a15a86322..719d480d3 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -146,7 +146,7 @@ def load_one(lit: LineIterator) -> dict: return result -def load_qchemlog_low(lit: LineIterator) -> dict: # pylint: disable=too-many-branches +def load_qchemlog_low(lit: LineIterator) -> dict: """Load the information from Q-Chem log file.""" data = {} while True: @@ -227,24 +227,24 @@ def load_qchemlog_low(lit: LineIterator) -> dict: # pylint: disable=too-many-br return data -def _helper_rem_job(lit: LineIterator) -> tuple: +def _helper_rem_job(lit: LineIterator) -> dict: """Load job specifications from Q-Chem output file format.""" data_rem = {} for line in lit: - if line.strip() == "$end": + words = line.strip().lower().split(maxsplit=1) + if words[0] == "$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 words[0] == "jobtype": + data_rem["run_type"] = words[1] + elif words[0] == "method": + data_rem["lot"] = words[1] + elif words[0] == "unrestricted": + data_rem["unrestricted"] = strtobool(words[1]) + elif words[0] == "basis": + data_rem["obasis_name"] = words[1] + elif words[0] == "symmetry": + data_rem["symm"] = strtobool(words[1]) return data_rem @@ -391,7 +391,6 @@ def _helper_vibrational(lit: LineIterator) -> tuple: for line in lit: if line.strip().startswith("This Molecule has"): break - # pylint: disable= W0631 imaginary_freq = int(line.split()[3]) vib_energy = float(next(lit).split()[-2]) next(lit) @@ -431,7 +430,7 @@ def _helper_thermo(lit: LineIterator) -> tuple: return enthalpy_dict, entropy_dict -def _helper_eda2(lit: LineIterator) -> dict: # pylint: disable=too-many-branches +def _helper_eda2(lit: LineIterator) -> dict: """Load Energy decomposition information.""" next(lit) eda2 = {} diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index ab5e063ea..d25210d69 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -253,7 +253,6 @@ def load_wfn_low(lit: LineIterator) -> tuple: ) -# pylint: disable=too-many-branches def build_obasis( icenters: np.ndarray, type_assignments: np.ndarray, exponents: np.ndarray, lit: LineIterator ) -> tuple[MolecularBasis, np.ndarray]: diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index eab00ab3b..95346e22b 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -167,7 +167,6 @@ def load_data_wfx(lit: LineIterator) -> dict: def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: """Load data in all sections existing in the given WFX file LineIterator.""" - # pylint: disable=too-many-branches data = {} mo_start = "<Molecular Orbital Primitive Coefficients>" section_start = None @@ -335,7 +334,6 @@ def load_one(lit: LineIterator) -> dict: ) def dump_one(f: TextIO, data: IOData): """Do not edit this docstring. It will be overwritten.""" - # pylint: disable=too-many-branches,too-many-statements # get all tags/labels that can be written into a WFX file lbs_str, lbs_int, lbs_float, lbs_aint, lbs_afloat, lbs_other, _ = _wfx_labels() # put all labels in one dictionary and flip key and value for easier use diff --git a/iodata/iodata.py b/iodata/iodata.py index 3d85fc779..0f2b20448 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -29,7 +29,6 @@ __all__ = ["IOData"] -# pylint: disable=too-many-instance-attributes @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. @@ -266,10 +265,6 @@ def __attrs_post_init__(self): def atcorenums(self) -> np.ndarray: """Return effective core charges.""" if self._atcorenums is None and self.atnums is not None: - # Known bug in pylint. See - # https://stackoverflow.com/questions/47972143/using-attr-with-pylint - # https://github.com/PyCQA/pylint/issues/1694 - # pylint: disable=no-member self.atcorenums = self.atnums.astype(float) return self._atcorenums diff --git a/iodata/orbitals.py b/iodata/orbitals.py index 0efbfec80..bca69d26c 100644 --- a/iodata/orbitals.py +++ b/iodata/orbitals.py @@ -133,7 +133,7 @@ def nbasis(self): return self.coeffs.shape[0] @property - def norb(self): # pylint: disable=too-many-return-statements + def norb(self): """Return the number of spatially distinct orbitals. Notes diff --git a/iodata/overlap.py b/iodata/overlap.py index 2f92aba6d..17d57172e 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -51,7 +51,6 @@ def factorial2(n, exact=False): return out -# pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches def compute_overlap( obasis0: MolecularBasis, atcoords0: np.ndarray, @@ -132,7 +131,7 @@ def compute_overlap( n_max = max(np.max(shell.angmoms) for shell in obasis0.shells) if not identical: - n_max = max(n_max, max(np.max(shell.angmoms) for shell in obasis1.shells)) + n_max = max(n_max, *(np.max(shell.angmoms) for shell in obasis1.shells)) go = GaussianOverlap(n_max) # define a python ufunc (numpy function) for broadcasted calling over angular momentums @@ -141,7 +140,6 @@ def compute_overlap( # Loop over shell0 begin0 = 0 - # pylint: disable=too-many-nested-blocks for i0, shell0 in enumerate(obasis0.shells): r0 = atcoords0[shell0.icenter] end0 = begin0 + shell0.nbasis diff --git a/iodata/test/test_charmm.py b/iodata/test/test_charmm.py index f852d805e..8a7ea6ccc 100644 --- a/iodata/test/test_charmm.py +++ b/iodata/test/test_charmm.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.orcalog module.""" from numpy.testing import assert_allclose, assert_equal diff --git a/iodata/test/test_chgcar.py b/iodata/test/test_chgcar.py index da3fbc670..e9ee2df21 100644 --- a/iodata/test/test_chgcar.py +++ b/iodata/test/test_chgcar.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.chgcar module.""" import numpy as np diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 53295efbb..2ed056de4 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.cube module.""" import numpy as np diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index 070c7e316..e542d61da 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object,no-member """Test iodata.formats.fchk module.""" import os diff --git a/iodata/test/test_gromacs.py b/iodata/test/test_gromacs.py index dd7848aa1..4ef1eb24c 100644 --- a/iodata/test/test_gromacs.py +++ b/iodata/test/test_gromacs.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.gromacs module.""" from numpy.testing import assert_allclose, assert_equal diff --git a/iodata/test/test_iodata.py b/iodata/test/test_iodata.py index dbf12ea23..e0dc714b9 100644 --- a/iodata/test/test_iodata.py +++ b/iodata/test/test_iodata.py @@ -97,7 +97,6 @@ def test_dm_ch3_rohf_g03(): def test_charge_nelec1(): - # pylint: disable=protected-access # One a blank IOData object, charge and nelec can be set independently. mol = IOData() mol.nelec = 4 @@ -110,7 +109,6 @@ def test_charge_nelec1(): def test_charge_nelec2(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.atcorenums = np.array([6.0, 1.0, 1.0, 1.0, 1.0]) @@ -124,7 +122,6 @@ def test_charge_nelec2(): def test_charge_nelec3(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.atnums = np.array([6, 1, 1, 1, 1]) @@ -142,7 +139,6 @@ def test_charge_nelec3(): def test_charge_nelec4(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.atnums = np.array([6, 1, 1, 1, 1]) @@ -155,7 +151,6 @@ def test_charge_nelec4(): def test_charge_nelec5(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.charge = 1 @@ -170,7 +165,6 @@ def test_charge_nelec5(): def test_charge_nelec6(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.nelec = 8 @@ -185,7 +179,6 @@ def test_charge_nelec6(): def test_charge_nelec7(): - # pylint: disable=protected-access # When atcorenums is set, nelec and charge become coupled. mol = IOData() mol.nelec = 8 @@ -210,7 +203,6 @@ def test_charge_nelec8(): def test_charge_nelec9(): - # pylint: disable=protected-access mol = IOData() mol.charge = 1.0 mol.atcorenums = np.array([8.0, 1.0, 1.0]) @@ -223,7 +215,6 @@ def test_charge_nelec9(): def test_charge_nelec10(): - # pylint: disable=protected-access mol = IOData() mol.charge = 1.0 mol.atnums = np.array([8, 1, 1]) @@ -264,7 +255,6 @@ def test_charge_nelec13(): def test_charge_nelec14(): - # pylint: disable=protected-access mol = IOData() mol.nelec = 8 mol.atcorenums = None @@ -314,7 +304,6 @@ def test_spinpol2(): def test_derived1(): - # pylint: disable=protected-access # When loading a file with molecular orbitals, nelec, charge and spinpol are # derived from the mo object: with as_file(files("iodata.test.data").joinpath("ch3_rohf_sto3g_g03.fchk")) as fn_fchk: @@ -334,7 +323,6 @@ def test_derived1(): def test_derived2(): - # pylint: disable=protected-access mol = IOData(atnums=[1, 1, 8], charge=1) assert mol._charge is None assert mol._nelec == mol.atcorenums.sum() - 1 diff --git a/iodata/test/test_locpot.py b/iodata/test/test_locpot.py index fbc7e8056..814a9fb4b 100644 --- a/iodata/test/test_locpot.py +++ b/iodata/test/test_locpot.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.locpot module.""" from numpy.testing import assert_allclose, assert_equal diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 7bcbe3684..c049ac55b 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.molden module.""" import os diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 4a69f5a86..92acdf92b 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object,no-member """Test iodata.formats.molekel module.""" import os diff --git a/iodata/test/test_mwfn.py b/iodata/test/test_mwfn.py index 71ccc6add..d4e08a00d 100644 --- a/iodata/test/test_mwfn.py +++ b/iodata/test/test_mwfn.py @@ -36,7 +36,6 @@ def load_helper(fn): return load_one(absfn) -# pylint: disable=too-many-statements def test_load_mwfn_ch3_rohf_g03(): mol = load_helper("ch3_rohf_sto3g_g03_fchk_multiwfn3.7.mwfn") assert_equal(mol.mo.occs.shape[0], mol.mo.coeffs.shape[1]) diff --git a/iodata/test/test_orbitals.py b/iodata/test/test_orbitals.py index 3fe5ce03d..ea809f407 100644 --- a/iodata/test/test_orbitals.py +++ b/iodata/test/test_orbitals.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=pointless-statement """Unit tests for iodata.orbitals.""" import numpy as np diff --git a/iodata/test/test_orcalog.py b/iodata/test/test_orcalog.py index 0e420c699..60c5e8192 100644 --- a/iodata/test/test_orcalog.py +++ b/iodata/test/test_orcalog.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.orcalog module.""" import numpy as np diff --git a/iodata/test/test_poscar.py b/iodata/test/test_poscar.py index 45a1b6028..13979e7e1 100644 --- a/iodata/test/test_poscar.py +++ b/iodata/test/test_poscar.py @@ -16,7 +16,6 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- -# pylint: disable=unsubscriptable-object """Test iodata.formats.poscar module.""" import os diff --git a/iodata/test/test_qchemlog.py b/iodata/test/test_qchemlog.py index 9964304d2..e7d78fd83 100644 --- a/iodata/test/test_qchemlog.py +++ b/iodata/test/test_qchemlog.py @@ -538,7 +538,6 @@ def test_load_one_qchemlog_freq(): def test_load_qchemlog_low_qchemlog_h2o_dimer_eda2(): """Test load_qchemlog_low 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 fq: data = load_qchemlog_low(LineIterator(str(fq))) @@ -748,7 +747,6 @@ def test_load_qchemlog_low_qchemlog_h2o_dimer_eda2(): 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") diff --git a/iodata/utils.py b/iodata/utils.py index 3d84330e0..2b60e9f55 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -76,7 +76,7 @@ def __init__(self, filename: str): """ self.filename = filename - self.f = open(filename) # pylint: disable=consider-using-with + self.f = open(filename) self.lineno = 0 self.stack = [] diff --git a/pyproject.toml b/pyproject.toml index 5891e324d..a3a9cd3c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,4 +54,15 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I", "UP", "B"] +select = ["E", "F", "I", "UP", "B", "PL"] +ignore = [ + "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ + "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ + "PLR0912", # https://docs.astral.sh/ruff/rules/too-many-branches/ + "PLR0913", # https://docs.astral.sh/ruff/rules/too-many-arguments/ + "PLR0914", # https://docs.astral.sh/ruff/rules/too-many-locals/ + "PLR0915", # https://docs.astral.sh/ruff/rules/too-many-statements/ + "PLR0916", # https://docs.astral.sh/ruff/rules/too-many-boolean-expressions/ + "PLR0917", # https://docs.astral.sh/ruff/rules/too-many-positional/ + "PLR2004", # https://docs.astral.sh/ruff/rules/magic-value-comparison/ +] From c8bf9db5b1b319a666c14074cd50c6a009860ab2 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:29:53 +0200 Subject: [PATCH 104/144] Add Ruff-specific rules --- iodata/api.py | 13 +++++----- iodata/basis.py | 10 ++++---- iodata/docstrings.py | 50 +++++++++++++++++++------------------ iodata/formats/fchk.py | 4 +-- iodata/formats/wfn.py | 6 ++++- iodata/formats/wfx.py | 4 +-- iodata/formats/xyz.py | 2 +- iodata/inputs/gaussian.py | 4 +-- iodata/inputs/orca.py | 4 +-- iodata/iodata.py | 16 ++++++------ iodata/test/common.py | 5 +++- iodata/test/test_fchk.py | 3 ++- iodata/test/test_molekel.py | 3 ++- iodata/test/test_wfx.py | 3 ++- iodata/test/test_xyz.py | 3 ++- iodata/utils.py | 2 +- pyproject.toml | 2 +- 17 files changed, 74 insertions(+), 60 deletions(-) diff --git a/iodata/api.py b/iodata/api.py index 71767f02d..bef654e1f 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -24,6 +24,7 @@ from importlib import import_module from pkgutil import iter_modules from types import ModuleType +from typing import Optional from .iodata import IOData from .utils import LineIterator @@ -45,7 +46,7 @@ def _find_format_modules(): FORMAT_MODULES = _find_format_modules() -def _select_format_module(filename: str, attrname: str, fmt: str = None) -> ModuleType: +def _select_format_module(filename: str, attrname: str, fmt: Optional[str] = None) -> ModuleType: """Find a file format module with the requested attribute name. Parameters @@ -110,7 +111,7 @@ def _select_input_module(fmt: str) -> ModuleType: raise ValueError(f"Could not find input format {fmt}!") -def load_one(filename: str, fmt: str = None, **kwargs) -> IOData: +def load_one(filename: str, fmt: Optional[str] = None, **kwargs) -> IOData: """Load data from a file. This function uses the extension or prefix of the filename to determine the @@ -142,7 +143,7 @@ def load_one(filename: str, fmt: str = None, **kwargs) -> IOData: return iodata -def load_many(filename: str, fmt: str = None, **kwargs) -> Iterator[IOData]: +def load_many(filename: str, fmt: Optional[str] = None, **kwargs) -> Iterator[IOData]: """Load multiple IOData instances from a file. This function uses the extension or prefix of the filename to determine the @@ -174,7 +175,7 @@ def load_many(filename: str, fmt: str = None, **kwargs) -> Iterator[IOData]: return -def dump_one(iodata: IOData, filename: str, fmt: str = None, **kwargs): +def dump_one(iodata: IOData, filename: str, fmt: Optional[str] = None, **kwargs): """Write data to a file. This routine uses the extension or prefix of the filename to determine @@ -199,7 +200,7 @@ def dump_one(iodata: IOData, filename: str, fmt: str = None, **kwargs): format_module.dump_one(f, iodata, **kwargs) -def dump_many(iodatas: Iterator[IOData], filename: str, fmt: str = None, **kwargs): +def dump_many(iodatas: Iterator[IOData], filename: str, fmt: Optional[str] = None, **kwargs): """Write multiple IOData instances to a file. This routine uses the extension or prefix of the filename to determine @@ -223,7 +224,7 @@ def dump_many(iodatas: Iterator[IOData], filename: str, fmt: str = None, **kwarg format_module.dump_many(f, iodatas, **kwargs) -def write_input(iodata: IOData, filename: str, fmt: str, template: str = None, **kwargs): +def write_input(iodata: IOData, filename: str, fmt: str, template: Optional[str] = None, **kwargs): """Write input file using an instance of IOData for the specified software format. Parameters diff --git a/iodata/basis.py b/iodata/basis.py index b826cc41b..746f0ced0 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -133,7 +133,7 @@ class Shell: coeffs: np.ndarray = attr.ib(validator=validate_shape(("exponents", 0), ("kinds", 0))) @property - def nbasis(self) -> int: # noqa: D401 + def nbasis(self) -> int: """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): @@ -146,12 +146,12 @@ def nbasis(self) -> int: # noqa: D401 return result @property - def nprim(self) -> int: # noqa: D401 + def nprim(self) -> int: """Number of primitives, also known as the contraction length.""" return len(self.exponents) @property - def ncon(self) -> int: # noqa: D401 + def ncon(self) -> int: """Number of contractions. This is usually 1; e.g., it would be 2 for an SP shell.""" return len(self.angmoms) @@ -210,7 +210,7 @@ class MolecularBasis: primitive_normalization: str @property - def nbasis(self) -> int: # noqa: D401 + def nbasis(self) -> int: """Number of basis functions.""" return sum(shell.nbasis for shell in self.shells) @@ -369,7 +369,7 @@ def get_default_conventions() -> tuple[dict, dict]: Kenny, J. P.; Janssen, C. L.; Valeev, E. F.; Windus, T. L. Components for Integral Evaluation in Quantum Chemistry: Components for Integral Evaluation - in Quantum Chemistry. J. Comput. Chem. 2008, 29 (4), 562–577. + in Quantum Chemistry. J. Comput. Chem. 2008, 29 (4), 562-577. https://doi.org/10.1002/jcc.20815. The ordering of the spherical harmonics within one shell is rather vague diff --git a/iodata/docstrings.py b/iodata/docstrings.py index 299061ecf..f28d2f7b3 100644 --- a/iodata/docstrings.py +++ b/iodata/docstrings.py @@ -18,6 +18,8 @@ # -- """Docstring decorators for file format implementations.""" +from typing import Optional + __all__ = [ "document_load_one", "document_load_many", @@ -31,9 +33,9 @@ def _document_load( template: str, fmt: str, guaranteed: list[str], - ifpresent: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + ifpresent: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): if kwdocs is None: kwdocs = {} @@ -91,9 +93,9 @@ def decorator(func): def document_load_one( fmt: str, guaranteed: list[str], - ifpresent: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + ifpresent: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): """Decorate a load_one function to generate a docstring. @@ -148,9 +150,9 @@ def document_load_one( def document_load_many( fmt: str, guaranteed: list[str], - ifpresent: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + ifpresent: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): """Decorate a load_many function to generate a docstring. @@ -184,9 +186,9 @@ def _document_dump( template: str, fmt: str, required: list[str], - optional: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + optional: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): if kwdocs is None: kwdocs = {} @@ -242,9 +244,9 @@ def decorator(func): def document_dump_one( fmt: str, required: list[str], - optional: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + optional: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): """Decorate a dump_one function to generate a docstring. @@ -296,9 +298,9 @@ def document_dump_one( def document_dump_many( fmt: str, required: list[str], - optional: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + optional: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): """Decorate a dump_many function to generate a docstring. @@ -332,9 +334,9 @@ def _document_write( template: str, fmt: str, required: list[str], - optional: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + optional: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): if kwdocs is None: kwdocs = {} @@ -393,9 +395,9 @@ def decorator(func): def document_write_input( fmt: str, required: list[str], - optional: list[str] = None, - kwdocs: dict[str, str] = None, - notes: str = None, + optional: Optional[list[str]] = None, + kwdocs: Optional[dict[str, str]] = None, + notes: Optional[str] = None, ): """Decorate a write_input function to generate a docstring. diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 8d4be258f..41358e4e1 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -20,7 +20,7 @@ from collections.abc import Iterator from fnmatch import fnmatch -from typing import TextIO +from typing import Optional, TextIO import numpy as np @@ -357,7 +357,7 @@ def load_many(lit: LineIterator) -> Iterator[dict]: yield data -def _load_fchk_low(lit: LineIterator, label_patterns: list[str] = None) -> dict: +def _load_fchk_low(lit: LineIterator, label_patterns: Optional[list[str]] = None) -> dict: """Read selected fields from a formatted checkpoint file. Parameters diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index d25210d69..6a567e72b 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -25,6 +25,8 @@ Gaussian functions. """ +import functools +import operator from typing import TextIO import numpy as np @@ -118,7 +120,9 @@ # 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 = functools.reduce( + operator.iadd, [CONVENTIONS[(angmom, "c")] for angmom in range(6)], [] +) def _load_helper_num(lit: LineIterator) -> list[int]: diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 95346e22b..54d46c884 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -23,7 +23,7 @@ import warnings from collections.abc import Iterator -from typing import TextIO +from typing import Optional, TextIO import numpy as np @@ -165,7 +165,7 @@ def load_data_wfx(lit: LineIterator) -> dict: return result -def parse_wfx(lit: LineIterator, required_tags: list = None) -> dict: +def parse_wfx(lit: LineIterator, required_tags: Optional[list] = None) -> dict: """Load data in all sections existing in the given WFX file LineIterator.""" data = {} mo_start = "<Molecular Orbital Primitive Coefficients>" diff --git a/iodata/formats/xyz.py b/iodata/formats/xyz.py index 217156c7e..5b4ed393e 100644 --- a/iodata/formats/xyz.py +++ b/iodata/formats/xyz.py @@ -115,7 +115,7 @@ def load_one(lit: LineIterator, atom_columns=None) -> dict: 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) + array = np.zeros((natom, *shapesuffix), dtype=dtype) if keyname is None: # Store the initial array as a normal attribute. data[attrname] = array diff --git a/iodata/inputs/gaussian.py b/iodata/inputs/gaussian.py index 67fac3e04..1647e4f4f 100644 --- a/iodata/inputs/gaussian.py +++ b/iodata/inputs/gaussian.py @@ -18,7 +18,7 @@ # -- """Gaussian Input Module.""" -from typing import TextIO +from typing import Optional, TextIO from ..docstrings import document_write_input from ..iodata import IOData @@ -44,7 +44,7 @@ ["atnums", "atcoords"], ["title", "run_type", "lot", "obasis_name", "spinmult", "charge"], ) -def write_input(f: TextIO, data: IOData, template: str = None, **kwargs): +def write_input(f: TextIO, data: IOData, template: Optional[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) diff --git a/iodata/inputs/orca.py b/iodata/inputs/orca.py index 8a90c7db7..f9d4b871e 100644 --- a/iodata/inputs/orca.py +++ b/iodata/inputs/orca.py @@ -18,7 +18,7 @@ # -- """Orca Input Module.""" -from typing import TextIO +from typing import Optional, TextIO from ..docstrings import document_write_input from ..iodata import IOData @@ -41,7 +41,7 @@ ["atnums", "atcoords"], ["title", "run_type", "lot", "obasis_name", "spinmult", "charge"], ) -def write_input(f: TextIO, data: IOData, template: str = None, **kwargs): +def write_input(f: TextIO, data: IOData, template: Optional[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) diff --git a/iodata/iodata.py b/iodata/iodata.py index 0f2b20448..3edabb6e5 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -172,7 +172,7 @@ class IOData: """ - atcharges: dict = {} + atcharges: dict = attr.ib(factory=dict) atcoords: np.ndarray = attr.ib( default=None, converter=convert_array_to(float), @@ -183,7 +183,7 @@ class IOData: converter=convert_array_to(float), validator=attr.validators.optional(validate_shape("natom")), ) - atffparams: dict = {} + atffparams: dict = attr.ib(factory=dict) atfrozen: np.ndarray = attr.ib( default=None, converter=convert_array_to(bool), @@ -229,21 +229,21 @@ class IOData: converter=convert_array_to(float), validator=attr.validators.optional(validate_shape(None, 4)), ) - extra: dict = {} + extra: dict = attr.ib(factory=dict) g_rot: float = None lot: str = None mo: MolecularOrbitals = None - moments: dict = {} + moments: dict = attr.ib(factory=dict) _nelec: float = None obasis: MolecularBasis = None obasis_name: str = None - one_ints: dict = {} - one_rdms: dict = {} + one_ints: dict = attr.ib(factory=dict) + one_rdms: dict = attr.ib(factory=dict) run_type: str = None _spinpol: float = None title: str = None - two_ints: dict = {} - two_rdms: dict = {} + two_ints: dict = attr.ib(factory=dict) + two_rdms: dict = attr.ib(factory=dict) def __attrs_post_init__(self): # Trigger setter to acchieve consistency in properties diff --git a/iodata/test/common.py b/iodata/test/common.py index 82fafaab8..424553d1e 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -20,6 +20,7 @@ import os from contextlib import contextmanager +from typing import Optional import numpy as np import pytest @@ -170,7 +171,9 @@ def check_orthonormal(mo_coeffs, ao_overlap, atol=1e-5): 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): +def load_one_warning( + filename: str, fmt: Optional[str] = None, match: Optional[str] = None, **kwargs +): """Call load_one, catching expected FileFormatWarning. Parameters diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index e542d61da..f478b6825 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -19,6 +19,7 @@ """Test iodata.formats.fchk module.""" import os +from typing import Optional import numpy as np import pytest @@ -543,7 +544,7 @@ def test_load_nbasis_indep(tmpdir): assert mol2.mo.coeffs.shape == (38, 37) -def check_load_dump_consistency(tmpdir: str, fn: str, match: str = None): +def check_load_dump_consistency(tmpdir: str, fn: str, match: Optional[str] = None): """Check if dumping and loading an FCHK file results in the same data. Parameters diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 92acdf92b..7c7fdc9bc 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -20,6 +20,7 @@ import os import warnings +from typing import Optional from numpy.testing import assert_allclose, assert_equal @@ -55,7 +56,7 @@ def compare_mols_diff_formats(mol1, mol2): assert_allclose(charges1, charges2, rtol=0.0, atol=1.0e-6) -def check_load_dump_consistency(fn: str, tmpdir: str, match: str = None): +def check_load_dump_consistency(fn: str, tmpdir: str, match: Optional[str] = None): """Check if data is preserved after dumping and loading a Molekel file. Parameters diff --git a/iodata/test/test_wfx.py b/iodata/test/test_wfx.py index 6d9e9d853..150c50fe9 100644 --- a/iodata/test/test_wfx.py +++ b/iodata/test/test_wfx.py @@ -19,6 +19,7 @@ """Test iodata.formats.wfn module.""" import os +from typing import Optional import numpy as np import pytest @@ -92,7 +93,7 @@ def test_load_dump_consistency_lih_cation_rohf(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: Optional[str] = None ): """Check if charges are computed correctly after dumping and loading WFX file format. diff --git a/iodata/test/test_xyz.py b/iodata/test/test_xyz.py index 5a4cf4abb..c0b3afe39 100644 --- a/iodata/test/test_xyz.py +++ b/iodata/test/test_xyz.py @@ -64,7 +64,8 @@ def check_water(mol): ) -FCC_ATOM_COLUMNS = DEFAULT_ATOM_COLUMNS + [ +FCC_ATOM_COLUMNS = [ + *DEFAULT_ATOM_COLUMNS, # Storing the atomic numbers as zs in the extras attribute makes sense # for testing. ("extra", "zs", (), int, int, "{:2d}".format), diff --git a/iodata/utils.py b/iodata/utils.py index 2b60e9f55..0548f0bf5 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -146,7 +146,7 @@ class Cube: @property def shape(self): - """Shape of the rectangular grid.""" # noqa: D401 + """Shape of the rectangular grid.""" return self.data.shape diff --git a/pyproject.toml b/pyproject.toml index a3a9cd3c1..50a686538 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I", "UP", "B", "PL"] +select = ["E", "F", "I", "UP", "B", "PL", "RUF"] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ From 580300f4d4bc891e364bfbd26a603f70f7791605 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:34:53 +0200 Subject: [PATCH 105/144] comprehension fixes --- iodata/basis.py | 4 +--- iodata/formats/molden.py | 2 +- iodata/periodic.py | 4 ++-- pyproject.toml | 2 +- tools/harmonics.py | 6 +++--- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/iodata/basis.py b/iodata/basis.py index 746f0ced0..26cb8baa8 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -390,9 +390,7 @@ def get_default_conventions() -> tuple[dict, dict]: 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) - ) + conv_cart = ["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 diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 5b3665e7a..f94ddf49b 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -126,7 +126,7 @@ def _load_low(lit: LineIterator) -> dict: ``title`` key and its corresponding value as well. """ - pure_angmoms = set([]) + pure_angmoms = set() atnums = None atcoords = None obasis = None diff --git a/iodata/periodic.py b/iodata/periodic.py index 40dadb7f9..defe93aa9 100644 --- a/iodata/periodic.py +++ b/iodata/periodic.py @@ -142,7 +142,7 @@ 118: "Og", } -sym2num: dict[str, int] = dict((value, key) for key, value in num2sym.items()) +sym2num: dict[str, int] = {value: key for key, value in num2sym.items()} # Labels used for bond types. @@ -166,4 +166,4 @@ 11: "nc", # not connected } -bond2num: dict[str, int] = dict((value, key) for key, value in num2bond.items()) +bond2num: dict[str, int] = {value: key for key, value in num2bond.items()} diff --git a/pyproject.toml b/pyproject.toml index 50a686538..00bac1907 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I", "UP", "B", "PL", "RUF"] +select = ["E", "F", "I", "UP", "B", "PL", "RUF", "C4"] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ diff --git a/tools/harmonics.py b/tools/harmonics.py index dd54263f4..7c4922349 100644 --- a/tools/harmonics.py +++ b/tools/harmonics.py @@ -84,10 +84,10 @@ def get_bare_transforms(ellmax: int): for _m in range(2 * ell + 1): row = [] poly = sp.Poly(rrsh.pop(0), x, y, z) - lookup = dict( - ((int(nx), int(ny), int(nz)), coeff) + lookup = { + (int(nx), int(ny), int(nz)): coeff for (nx, ny, nz), coeff in zip(poly.monoms(), poly.coeffs()) - ) + } for nx, ny, nz in iter_mononomials(ell): row.append(sp.sympify(lookup.get((nx, ny, nz), 0))) tf.append(row) From 90c3c822eca0f28549ce64c44bdbd3973c3990b7 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:35:19 +0200 Subject: [PATCH 106/144] Add pycodestyle warnings --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 00bac1907..ce4702b91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "F", "I", "UP", "B", "PL", "RUF", "C4"] +select = ["E", "W", "F", "I", "UP", "B", "PL", "RUF", "C4"] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ From 56b6b278b1cd93a62259a3a4e17c8598b7ff38fb Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:36:26 +0200 Subject: [PATCH 107/144] Naming conventions --- iodata/test/test_gamess.py | 12 ++++++------ pyproject.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/iodata/test/test_gamess.py b/iodata/test/test_gamess.py index 0bc84168c..072082557 100644 --- a/iodata/test/test_gamess.py +++ b/iodata/test/test_gamess.py @@ -32,29 +32,29 @@ def test_load_one_gamess_punch(): with as_file(files("iodata.test.data").joinpath("PCGamess_PUNCH.dat")) as f: data = load_one(str(f)) - N = len(["CL", "H", "H", "H", "H", "F", "F", "F", "F", "H", "F"]) + size = len(["CL", "H", "H", "H", "H", "F", "F", "F", "F", "H", "F"]) assert data.title == "Simple example sample optimization with Hessian output for Toon" assert data.g_rot == "C1" - assert_equal(data.atnums.shape, (N,)) + assert_equal(data.atnums.shape, (size,)) assert_equal(data.atnums[0], 17) assert_equal(data.atnums[1], 1) assert_equal(data.atnums[-1], 9) - assert_equal(data.atcoords.shape, (N, 3)) + assert_equal(data.atcoords.shape, (size, 3)) assert_allclose(data.atcoords[0, 1] / angstrom, -0.1843157808) assert_allclose(data.atcoords[3, -1] / angstrom, 1.2926708150) assert_allclose(data.atcoords[-1, 0] / angstrom, 3.8608437748) assert_allclose(data.energy, -959.9675629527) - assert_equal(data.atgradient.shape, (N, 3)) + assert_equal(data.atgradient.shape, (size, 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_equal(data.athessian.shape, (3 * N, 3 * N)) + assert_equal(data.athessian.shape, (3 * size, 3 * size)) 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_equal(data.atmasses.shape, (N,)) + assert_equal(data.atmasses.shape, (size,)) assert_allclose(data.atmasses[0], 34.96885) assert_allclose(data.atmasses[3], 1.00782) assert_allclose(data.atmasses[-1], 18.99840) diff --git a/pyproject.toml b/pyproject.toml index ce4702b91..e1f6daebf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "W", "F", "I", "UP", "B", "PL", "RUF", "C4"] +select = ["E", "W", "F", "I", "N", "UP", "B", "PL", "RUF", "C4"] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ From 5e10df2c5f7986afa76a805b81ffe0bec4aedc30 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:42:38 +0200 Subject: [PATCH 108/144] Few more minor rules --- doc/conf.py | 2 +- iodata/formats/cp2klog.py | 4 ++-- iodata/formats/molden.py | 2 +- iodata/formats/molekel.py | 2 +- iodata/formats/pdb.py | 2 +- iodata/overlap.py | 4 ++-- iodata/test/test_json.py | 20 +++++++++++--------- iodata/test/test_molden.py | 2 +- iodata/test/test_overlap.py | 2 +- iodata/test/test_pdb.py | 2 +- pyproject.toml | 6 +++++- tools/harmonics.py | 0 12 files changed, 27 insertions(+), 21 deletions(-) mode change 100644 => 100755 tools/harmonics.py diff --git a/doc/conf.py b/doc/conf.py index 18e0f4c87..ebc929935 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -85,7 +85,7 @@ # General information about the project. project = "IOData" -copyright = "2019, The IODATA Development Team" +copyright = "2019, The IODATA Development Team" # noqa: A001 author = "The IODATA Development Team" # The version info for the project yo're documenting, acts as replacement for diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 9b8b0eae4..6260daa56 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -181,11 +181,11 @@ 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" + " ********************** Contracted Gaussian Type Orbitals **********************\n" ): return _read_cp2k_contracted_obasis(lit) if line == ( - " ********************* Uncontracted Gaussian Type Orbitals " "*********************\n" + " ********************* Uncontracted Gaussian Type Orbitals *********************\n" ): return _read_cp2k_uncontracted_obasis(lit) lit.error("Could not find basis set in CP2K ATOM output.") diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index f94ddf49b..855a68538 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -153,7 +153,7 @@ 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]", "[5d7f]")): pure_angmoms.add(2) pure_angmoms.add(3) elif line.lower().startswith("[7f]"): diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index ca692219b..ea347826b 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -228,7 +228,7 @@ def load_one(lit: LineIterator, norm_threshold: float = 1e-4) -> dict: else: if occsb is None: lit.error( - "Beta occupation numbers not found in mkl file while " "beta orbitals were present." + "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())) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index fbadee7a6..40e7a27f7 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -168,7 +168,7 @@ def load_one(lit: LineIterator) -> dict: title_lines.append(line[10:].strip()) if line.startswith("COMPND"): compnd_lines.append(line[10:].strip()) - if line.startswith("ATOM") or line.startswith("HETATM"): + if line.startswith(("ATOM", "HETATM")): (atnum, attype, restype, chainid, resnum, atcoord, occupancy, bfactor) = ( _parse_pdb_atom_line(line, lit) ) diff --git a/iodata/overlap.py b/iodata/overlap.py index 17d57172e..f80363ca5 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -89,7 +89,7 @@ def compute_overlap( """ if obasis0.primitive_normalization != "L2": - raise ValueError("The overlap integrals are only implemented for L2 " "normalization.") + raise ValueError("The overlap integrals are only implemented for L2 normalization.") # Get a segmented basis, for simplicity obasis0 = obasis0.get_segmented() @@ -106,7 +106,7 @@ def compute_overlap( identical = True else: if obasis1.primitive_normalization != "L2": - raise ValueError("The overlap integrals are only implemented for L2 " "normalization.") + 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 " diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 7b1b8658a..b3dacd967 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -61,7 +61,9 @@ ] -@pytest.mark.parametrize("filename, atnums, charge, spinpol, geometry, nwarn", MOL_FILES) +@pytest.mark.parametrize( + ("filename", "atnums", "charge", "spinpol", "geometry", "nwarn"), MOL_FILES +) def test_qcschema_molecule(filename, atnums, charge, spinpol, geometry, nwarn): """Test qcschema_molecule parsing using manually generated files.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: @@ -105,7 +107,7 @@ def test_qcschema_molecule(filename, atnums, charge, spinpol, geometry, nwarn): ] -@pytest.mark.parametrize("filename, atnums, charge, spinpol, nwarn", MOLSSI_MOL_FILES) +@pytest.mark.parametrize(("filename", "atnums", "charge", "spinpol", "nwarn"), MOLSSI_MOL_FILES) def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): """Test qcschema_molecule parsing using MolSSI-sourced files.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: @@ -133,7 +135,7 @@ def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): ] -@pytest.mark.parametrize("filename, unparsed_dict", PASSTHROUGH_MOL_FILES) +@pytest.mark.parametrize(("filename", "unparsed_dict"), PASSTHROUGH_MOL_FILES) def test_passthrough_qcschema_molecule(filename, unparsed_dict): """Test qcschema_molecule parsing for passthrough of unparsed keys.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: @@ -166,7 +168,7 @@ def _check_provenance(mol1, mol2): ] -@pytest.mark.parametrize("filename, nwarn", INOUT_MOL_FILES) +@pytest.mark.parametrize(("filename", "nwarn"), INOUT_MOL_FILES) def test_inout_qcschema_molecule(tmpdir, filename, nwarn): """Test that loading and dumping qcschema_molecule files retains all data.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: @@ -260,7 +262,7 @@ def test_ghost(tmpdir): @pytest.mark.parametrize( - "filename, explicit_basis, lot, obasis_name, run_type, geometry", INPUT_FILES + ("filename", "explicit_basis", "lot", "obasis_name", "run_type", "geometry"), INPUT_FILES ) def test_qcschema_input(filename, explicit_basis, lot, obasis_name, run_type, geometry): with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: @@ -286,7 +288,7 @@ def test_qcschema_input(filename, explicit_basis, lot, obasis_name, run_type, ge ] -@pytest.mark.parametrize("filename, unparsed_dict, location", PASSTHROUGH_INPUT_FILES) +@pytest.mark.parametrize(("filename", "unparsed_dict", "location"), PASSTHROUGH_INPUT_FILES) def test_passthrough_qcschema_input(filename, unparsed_dict, location): """Test qcschema_molecule parsing for passthrough of unparsed keys.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: @@ -305,7 +307,7 @@ def test_passthrough_qcschema_input(filename, unparsed_dict, location): ] -@pytest.mark.parametrize("filename, nwarn", INOUT_INPUT_FILES) +@pytest.mark.parametrize(("filename", "nwarn"), INOUT_INPUT_FILES) def test_inout_qcschema_input(tmpdir, filename, nwarn): """Test that loading and dumping qcschema_molecule files retains all data.""" with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: @@ -344,7 +346,7 @@ def test_inout_qcschema_input(tmpdir, filename, nwarn): ] -@pytest.mark.parametrize("filename, lot, obasis_name, run_type, nwarn", OUTPUT_FILES) +@pytest.mark.parametrize(("filename", "lot", "obasis_name", "run_type", "nwarn"), OUTPUT_FILES) def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_output: if nwarn == 0: @@ -368,7 +370,7 @@ def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): ] -@pytest.mark.parametrize("filename, error", BAD_OUTPUT_FILES) +@pytest.mark.parametrize(("filename", "error"), BAD_OUTPUT_FILES) def test_bad_qcschema_files(filename, error): # FIXME: these will move with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index c049ac55b..16461eeb5 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -516,7 +516,7 @@ def test_load_molden_f(): @pytest.mark.parametrize( - "fn,match", + ("fn", "match"), [ ("h2o.molden.input", "ORCA"), ("li2.molden.input", "ORCA"), diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index e07f330e8..64a76fe66 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -118,7 +118,7 @@ def test_overlap_two_basis_same(fn): assert_allclose(olp_a, olp_b, rtol=0, atol=1e-14) -@pytest.mark.parametrize("fn0,fn1", itertools.combinations_with_replacement(FNS_TWO_BASIS, 2)) +@pytest.mark.parametrize(("fn0", "fn1"), itertools.combinations_with_replacement(FNS_TWO_BASIS, 2)) def test_overlap_two_basis_different(fn0, fn1): with as_file(files("iodata.test.data").joinpath(fn0)) as pth0: mol0 = load_one(pth0) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index bba61b52c..714500db9 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -67,7 +67,7 @@ def check_water(mol): @pytest.mark.parametrize( - "fn_base,should_warn", + ("fn_base", "should_warn"), [ ("water_single.pdb", False), ("water_single_model.pdb", False), diff --git a/pyproject.toml b/pyproject.toml index e1f6daebf..fe8d2dd36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,10 @@ line-length = 100 target-version = "py39" [tool.ruff.lint] -select = ["E", "W", "F", "I", "N", "UP", "B", "PL", "RUF", "C4"] +select = [ + "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", + "PL", "RUF", "C4", "RSE" +] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ "PLR0911", # https://docs.astral.sh/ruff/rules/too-many-return-statements/ @@ -65,4 +68,5 @@ ignore = [ "PLR0916", # https://docs.astral.sh/ruff/rules/too-many-boolean-expressions/ "PLR0917", # https://docs.astral.sh/ruff/rules/too-many-positional/ "PLR2004", # https://docs.astral.sh/ruff/rules/magic-value-comparison/ + "PT011", # https://docs.astral.sh/ruff/rules/pytest-raises-too-broad/ ] diff --git a/tools/harmonics.py b/tools/harmonics.py old mode 100644 new mode 100755 From f7ee2285ba3c9f55bdd07bb40215bc6da7838710 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:49:39 +0200 Subject: [PATCH 109/144] Add flake8-return checks --- iodata/formats/charmm.py | 3 +-- iodata/formats/cp2klog.py | 4 ++-- iodata/formats/gromacs.py | 4 ++-- iodata/formats/json.py | 3 +-- iodata/overlap.py | 3 +-- iodata/test/common.py | 3 +-- pyproject.toml | 2 +- 7 files changed, 9 insertions(+), 13 deletions(-) diff --git a/iodata/formats/charmm.py b/iodata/formats/charmm.py index 6545f5970..ae9f8149a 100644 --- a/iodata/formats/charmm.py +++ b/iodata/formats/charmm.py @@ -70,14 +70,13 @@ def load_one(lit: LineIterator) -> dict: "segid": segid, "resid": resid, } - result = { + return { "atcoords": atcoords, "atffparams": atffparams, "atmasses": atmasses, "extra": extra, "title": title, } - return result def _helper_read_crd(lit: LineIterator) -> tuple: diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 6260daa56..a1ed43da3 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -189,6 +189,7 @@ def _read_cp2k_obasis(lit: LineIterator) -> dict: ): return _read_cp2k_uncontracted_obasis(lit) lit.error("Could not find basis set in CP2K ATOM output.") + return None def _read_cp2k_occupations_energies( @@ -506,7 +507,7 @@ def load_one(lit: LineIterator) -> dict: np.concatenate((orb_alpha_energies, orb_beta_energies), axis=0), ) - result = { + return { "obasis": obasis, "mo": mo, "atcoords": np.zeros((1, 3), float), @@ -514,4 +515,3 @@ def load_one(lit: LineIterator) -> dict: "energy": energy, "atcorenums": np.array([atcorenum]), } - return result diff --git a/iodata/formats/gromacs.py b/iodata/formats/gromacs.py index 0b26f7d6d..e976d1022 100644 --- a/iodata/formats/gromacs.py +++ b/iodata/formats/gromacs.py @@ -56,15 +56,15 @@ def load_one(lit: LineIterator) -> dict: cellvecs = data[7] atffparams = {"attypes": attypes, "resnames": resnames, "resnums": resnums} extra = {"time": time, "velocities": velocities} - result = { + return { "atcoords": atcoords, "atffparams": atffparams, "cellvecs": cellvecs, "extra": extra, "title": title, } - return result lit.error("Gromacs gro file could not be read.") + return None @document_load_many("GRO", ["atcoords", "atffparams", "cellvecs", "extra", "title"]) diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 9237bc21a..0645d526d 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -590,8 +590,7 @@ 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 json_in = json.load(lit.f) - result = _parse_json(json_in, lit) - return result + return _parse_json(json_in, lit) def _parse_json(json_in: dict, lit: LineIterator) -> dict: diff --git a/iodata/overlap.py b/iodata/overlap.py index f80363ca5..dfa621d8f 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -221,8 +221,7 @@ def compute_overlap( permutation1, signs1 = permutation0, signs0 else: permutation1, signs1 = convert_conventions(obasis1, OVERLAP_CONVENTIONS, reverse=True) - overlap = overlap[:, permutation1] * signs1 - return overlap + return overlap[:, permutation1] * signs1 class GaussianOverlap: diff --git a/iodata/test/common.py b/iodata/test/common.py index 424553d1e..12b4f9f37 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -48,8 +48,7 @@ def compute_1rdm(iodata): """Compute 1-RDM.""" coeffs, occs = iodata.mo.coeffs, iodata.mo.occs - dm = np.dot(coeffs * occs, coeffs.T) - return dm + return np.dot(coeffs * occs, coeffs.T) def compute_mulliken_charges(iodata): diff --git a/pyproject.toml b/pyproject.toml index fe8d2dd36..94d461785 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE" + "PL", "RUF", "C4", "RSE", "RET" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From cd95df1896514417eef80b747f42ddb4ecfdbbf8 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 11:51:11 +0200 Subject: [PATCH 110/144] Add flake8-self rule --- iodata/test/test_iodata.py | 1 + pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/iodata/test/test_iodata.py b/iodata/test/test_iodata.py index e0dc714b9..a71240742 100644 --- a/iodata/test/test_iodata.py +++ b/iodata/test/test_iodata.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, see <http://www.gnu.org/licenses/> # -- +# ruff: noqa: SLF001 """Test iodata.iodata module.""" import numpy as np diff --git a/pyproject.toml b/pyproject.toml index 94d461785..9ce2a81f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET" + "PL", "RUF", "C4", "RSE", "RET", "SLF" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From 52d448e299e626d7ca182bcdb8a990bf561cb2d8 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 22 May 2024 12:02:08 +0200 Subject: [PATCH 111/144] Add the refurb rules --- iodata/formats/wfx.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 54d46c884..424dc7ce0 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -150,7 +150,7 @@ def load_data_wfx(lit: LineIterator) -> dict: # 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) - gradient_atoms = gradient_mix[:, 0].astype(np.unicode_) + gradient_atoms = gradient_mix[:, 0].astype(np.str_) 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) diff --git a/pyproject.toml b/pyproject.toml index 9ce2a81f8..d73424ff9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF" + "PL", "RUF", "C4", "RSE", "RET", "SLF", "FURB" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From 1a360a3cc836de1adfdf79968f8d24184de11993 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Thu, 23 May 2024 10:57:19 +0200 Subject: [PATCH 112/144] Add NPY and leave out FURB --- iodata/test/test_basis.py | 12 +++++------- iodata/test/test_orbitals.py | 18 ++++++++++++------ pyproject.toml | 2 +- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/iodata/test/test_basis.py b/iodata/test/test_basis.py index d2c127f15..2fe6dc248 100644 --- a/iodata/test/test_basis.py +++ b/iodata/test/test_basis.py @@ -171,14 +171,11 @@ def test_nbasis1(): def test_get_segmented(): + rng = np.random.default_rng(1) 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)) - ), + Shell(0, [0, 1], ["c", "c"], rng.uniform(0, 1, 5), rng.uniform(-1, 1, (5, 2))), + Shell(1, [2, 3], ["p", "p"], rng.uniform(0, 1, 7), rng.uniform(-1, 1, (7, 2))), ], CP2K_CONVENTIONS, "L2", @@ -279,7 +276,8 @@ def test_convert_convention_obasis(): 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]) - vec1 = np.random.uniform(-1, 1, obasis.nbasis) + rng = np.random.default_rng(1) + vec1 = rng.uniform(-1, 1, obasis.nbasis) vec2 = vec1[permutation] * signs permutation, signs = convert_conventions(obasis, new_convention, reverse=True) vec3 = vec2[permutation] * signs diff --git a/iodata/test/test_orbitals.py b/iodata/test/test_orbitals.py index ea809f407..78652d7ea 100644 --- a/iodata/test/test_orbitals.py +++ b/iodata/test/test_orbitals.py @@ -78,7 +78,8 @@ def test_restricted_occs(): def test_restricted_coeffs(): - coeffs = np.random.uniform(-1, 1, (7, 5)) + rng = np.random.default_rng(1) + coeffs = rng.uniform(-1, 1, (7, 5)) with pytest.raises(TypeError): MolecularOrbitals("restricted", 3, 3, coeffs=coeffs) mo = MolecularOrbitals("restricted", 5, 5, coeffs=coeffs) @@ -99,7 +100,8 @@ def test_restricted_coeffs(): def test_restricted_energies(): - energies = np.random.uniform(-1, 1, 5) + rng = np.random.default_rng(1) + energies = rng.uniform(-1, 1, 5) with pytest.raises(TypeError): MolecularOrbitals("restricted", 3, 3, energies=energies) mo = MolecularOrbitals("restricted", 5, 5, energies=energies) @@ -186,7 +188,8 @@ def test_unrestricted_occs(): def test_unrestricted_coeffs(): - coeffs = np.random.uniform(-1, 1, (7, 8)) + rng = np.random.default_rng(1) + coeffs = rng.uniform(-1, 1, (7, 8)) with pytest.raises(TypeError): MolecularOrbitals("unrestricted", 3, 2, coeffs=coeffs) mo = MolecularOrbitals("unrestricted", 5, 3, coeffs=coeffs) @@ -207,7 +210,8 @@ def test_unrestricted_coeffs(): def test_unrestricted_energies(): - energies = np.random.uniform(-1, 1, 8) + rng = np.random.default_rng(1) + energies = rng.uniform(-1, 1, 8) with pytest.raises(TypeError): MolecularOrbitals("unrestricted", 3, 2, energies=energies) mo = MolecularOrbitals("unrestricted", 5, 3, energies=energies) @@ -310,7 +314,8 @@ def test_generalized_occs(): def test_generalized_coeffs(): - coeffs = np.random.uniform(-1, 1, (10, 7)) + rng = np.random.default_rng(1) + coeffs = rng.uniform(-1, 1, (10, 7)) mo = MolecularOrbitals("generalized", None, None, coeffs=coeffs) assert mo.norba is None assert mo.norbb is None @@ -338,7 +343,8 @@ def test_generalized_coeffs(): def test_generalized_energies(): - energies = np.random.uniform(-1, 1, 7) + rng = np.random.default_rng(1) + energies = rng.uniform(-1, 1, 7) mo = MolecularOrbitals("generalized", None, None, energies=energies) assert mo.norba is None assert mo.norbb is None diff --git a/pyproject.toml b/pyproject.toml index d73424ff9..e6e0ce4e3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF", "FURB" + "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From 4e1825821439fb50cb3bd85321bef217056aedfd Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Fri, 24 May 2024 09:15:30 +0200 Subject: [PATCH 113/144] enable flake8-simplify --- .pre-commit-config.yaml | 2 +- iodata/api.py | 7 ++- iodata/attrutils.py | 5 +- iodata/formats/cp2klog.py | 5 +- iodata/formats/extxyz.py | 11 ++-- iodata/formats/fchk.py | 7 +-- iodata/formats/json.py | 5 +- iodata/formats/molden.py | 10 +--- iodata/formats/molekel.py | 10 +--- iodata/formats/wfn.py | 9 +-- iodata/formats/wfx.py | 6 +- iodata/overlap.py | 5 +- iodata/test/test_cp2klog.py | 30 +++++----- iodata/test/test_cube.py | 8 +-- iodata/test/test_fchk.py | 8 ++- iodata/test/test_gaussianinput.py | 8 ++- iodata/test/test_json.py | 24 +++++--- iodata/test/test_mol2.py | 10 ++-- iodata/test/test_molden.py | 93 +++++++++++++++++-------------- iodata/test/test_molekel.py | 16 +++--- iodata/test/test_pdb.py | 24 +++++--- iodata/test/test_sdf.py | 18 +++--- iodata/test/test_wfx.py | 18 +++--- iodata/utils.py | 8 +-- pyproject.toml | 2 +- 25 files changed, 174 insertions(+), 175 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee60f72b9..26b8a298d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -30,7 +30,7 @@ repos: hooks: - id: check-github-workflows - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.4.4 + rev: v0.4.5 hooks: - id: ruff-format - id: ruff diff --git a/iodata/api.py b/iodata/api.py index bef654e1f..bfce5cffb 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -68,9 +68,10 @@ def _select_format_module(filename: str, attrname: str, fmt: Optional[str] = Non basename = os.path.basename(filename) if fmt is None: for format_module in FORMAT_MODULES.values(): - if any(fnmatch(basename, pattern) for pattern in format_module.PATTERNS): - if hasattr(format_module, attrname): - return format_module + if any(fnmatch(basename, pattern) for pattern in format_module.PATTERNS) and hasattr( + format_module, attrname + ): + return format_module else: return FORMAT_MODULES[fmt] raise ValueError(f"Could not find file format with feature {attrname} for file {filename}") diff --git a/iodata/attrutils.py b/iodata/attrutils.py index 3dadf5d94..3e157aede 100644 --- a/iodata/attrutils.py +++ b/iodata/attrutils.py @@ -99,10 +99,7 @@ def validator(obj, attribute, value): raise ValueError(f"Cannot interpret item in shape_requirements: {item}") expected_shape = tuple(expected_shape) # Get the actual shape - if isinstance(value, np.ndarray): - observed_shape = value.shape - else: - observed_shape = (len(value),) + observed_shape = value.shape if isinstance(value, np.ndarray) else (len(value),) # Compare match = True if len(expected_shape) != len(observed_shape): diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index a1ed43da3..2fec908ed 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -417,10 +417,7 @@ def load_one(lit: LineIterator) -> dict: break # Select the correct basis - if atcorenum == atnum: - obasis = ae_obasis - else: - obasis = pp_obasis + obasis = ae_obasis if atcorenum == atnum else pp_obasis # Search for energy for line in lit: diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index 4ee72237a..af1922896 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -136,14 +136,11 @@ def _parse_properties(properties: str): # If 'Z' is not present, use 'species' atom_column_map["species"] = atnum_column for name, dtype, shape in zip(names, dtypes, shapes): - if name in atom_column_map.keys(): + if name in atom_column_map: atom_columns.append(atom_column_map[name]) else: # Use the 'extra' attribute to store values which are not predefined in iodata - if shape == "1": - shape_suffix = () - else: - shape_suffix = (int(shape),) + shape_suffix = () if shape == "1" else (int(shape),) atom_columns.append(("extra", name, shape_suffix, *dtype_map[dtype])) return atom_columns @@ -167,7 +164,7 @@ def load_cellvecs(word): key, value = key_value_pair.split("=", 1) if key == "Properties": atom_columns = _parse_properties(value) - elif key in iodata_attrs.keys(): + elif key in iodata_attrs: data[iodata_attrs[key][0]] = iodata_attrs[key][1](value) else: data.setdefault("extra", {})[key] = _convert_title_value(value) @@ -192,7 +189,7 @@ 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(): + if "extra" in title_data and "extra" in xyz_data: xyz_data["extra"].update(title_data["extra"]) title_data.update(xyz_data) return title_data diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 41358e4e1..1d86c09c7 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -241,10 +241,9 @@ def load_one(lit: LineIterator) -> dict: mo_occs = np.zeros(norba) mo_occs[:nalpha] = 1.0 mo_occs[:nbeta] = 2.0 - 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") + # delete dm_full_scf because it is known to be buggy + if nalpha != nbeta and "one_rdms" in result and "scf" in result["one_rdms"]: + result["one_rdms"].pop("scf") mo = MolecularOrbitals("restricted", norba, norba, mo_occs, mo_coeffs, mo_energies) result["mo"] = mo diff --git a/iodata/formats/json.py b/iodata/formats/json.py index 0645d526d..a2e5d2c73 100644 --- a/iodata/formats/json.py +++ b/iodata/formats/json.py @@ -660,10 +660,7 @@ def _parse_json(json_in: dict, lit: LineIterator) -> dict: elif "center_data" in result: schema_name = "qcschema_basis" elif "driver" in result: - if "return_result" in result: - schema_name = "qcschema_output" - else: - schema_name = "qcschema_input" + schema_name = "qcschema_output" if "return_result" in result else "qcschema_input" else: raise FileFormatError(f"{lit.filename}: Could not determine `schema_name`.") if "schema_version" not in result: diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 855a68538..964752eb0 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -697,10 +697,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold cfour_coeff_correction = _fix_mo_coeffs_cfour(obasis) if cfour_coeff_correction is not None: coeffsa_cfour = coeffsa / cfour_coeff_correction[:, np.newaxis] - if coeffsb is None: - coeffsb_cfour = None - else: - coeffsb_cfour = coeffsb / cfour_coeff_correction[:, np.newaxis] + coeffsb_cfour = None if coeffsb is None else 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 @@ -722,10 +719,7 @@ def _fix_molden_from_buggy_codes(result: dict, lit: LineIterator, norm_threshold psi4_coeff_correction = _fix_mo_coeffs_psi4(obasis) if psi4_coeff_correction is not None: coeffsa_psi4 = coeffsa / psi4_coeff_correction[:, np.newaxis] - if coeffsb is None: - coeffsb_psi4 = None - else: - coeffsb_psi4 = coeffsb / psi4_coeff_correction[:, np.newaxis] + coeffsb_psi4 = None if coeffsb is None else coeffsb / psi4_coeff_correction[:, np.newaxis] if _is_normalized_properly( normed_obasis, atcoords, coeffsa_psi4, coeffsb_psi4, norm_threshold ): diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index ea347826b..9770f020f 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -345,18 +345,12 @@ def _dump_helper_coeffs(f, data, spin=None): 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 + irreps = data.mo.irreps[:norb] if data.mo.irreps is not None else ["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 = data.mo.irreps[norb:] if data.mo.irreps is not None else ["a1g"] * norb else: raise OSError("A spin must be specified") diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 6a567e72b..7f4c9f6e4 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -284,12 +284,9 @@ def build_obasis( while ibasis < nbasis: # Determine the angular moment of the shell type_assignment = type_assignments[ibasis] - if type_assignment == 0: - angmom = 0 - else: - # multiple different type assignments (codes for individual basis - # functions) can match one angular momentum. - angmom = len(PRIMITIVE_NAMES[type_assignments[ibasis]]) + # multiple different type assignments (codes for individual basis + # functions) can match one angular momentum. + angmom = 0 if type_assignment == 0 else len(PRIMITIVE_NAMES[type_assignments[ibasis]]) # The number of cartesian functions for the current angular momentum ncart = len(CONVENTIONS[(angmom, "c")]) # Determine how many shells are to be read in one batch. E.g. for a diff --git a/iodata/formats/wfx.py b/iodata/formats/wfx.py index 424dc7ce0..e5041b863 100644 --- a/iodata/formats/wfx.py +++ b/iodata/formats/wfx.py @@ -158,7 +158,7 @@ def load_data_wfx(lit: LineIterator) -> dict: perturbation_check = {"GTO": 0, "GIAO": 3, "CGST": 6} key = result["keywords"] num = result["num_perturbations"] - if key not in perturbation_check.keys(): + if key not in perturbation_check: lit.error(f"The keywords is {key}, but it should be either GTO, GIAO or CGST") if num != perturbation_check[key]: lit.error(f"Number of perturbations of {key} is {num}, expected {perturbation_check[key]}") @@ -181,7 +181,7 @@ def parse_wfx(lit: LineIterator, required_tags: Optional[list] = None) -> dict: if section_start is None and line.startswith("<"): # set start & end of the section and add it to data dictionary section_start = line - if section_start in data.keys(): + if section_start in data: lit.error(f"Section with tag={section_start} is repeated!") data[section_start] = [] section_end = line[:1] + "/" + line[1:] @@ -211,7 +211,7 @@ def parse_wfx(lit: LineIterator, required_tags: Optional[list] = None) -> dict: # check required section tags if required_tags is not None: for section_tag in required_tags: - if section_tag not in data.keys(): + if section_tag not in data: lit.error(f"Section {section_tag} is missing from loaded WFX data.") return data diff --git a/iodata/overlap.py b/iodata/overlap.py index dfa621d8f..1d4efac4d 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -146,10 +146,7 @@ def compute_overlap( # Loop over shell1 (lower triangular only, including diagonal) begin1 = 0 - if identical: - nshell1 = i0 + 1 - else: - nshell1 = len(obasis1.shells) + nshell1 = i0 + 1 if identical else len(obasis1.shells) for i1, shell1 in enumerate(obasis1.shells[:nshell1]): r1 = atcoords1[shell1.icenter] end1 = begin1 + shell1.nbasis diff --git a/iodata/test/test_cp2klog.py b/iodata/test/test_cp2klog.py index 2701e076e..299edc735 100644 --- a/iodata/test/test_cp2klog.py +++ b/iodata/test/test_cp2klog.py @@ -206,20 +206,18 @@ def test_carbon_sc_pp_uncontracted(): def test_errors(tmpdir): source = files("iodata.test.data").joinpath("carbon_sc_pp_uncontracted.cp2k.out") with as_file(source) as fn_test: - with truncated_file(fn_test, 0, 0, tmpdir) as fn: - with pytest.raises(IOError): - load_one(fn) - with truncated_file(fn_test, 107, 10, tmpdir) as fn: - with pytest.raises(IOError): - load_one(fn) - with truncated_file(fn_test, 357, 10, tmpdir) as fn: - with pytest.raises(IOError): - load_one(fn) - with truncated_file(fn_test, 405, 10, tmpdir) as fn: - with pytest.raises(IOError): - load_one(fn) + with truncated_file(fn_test, 0, 0, tmpdir) as fn, pytest.raises(IOError): + load_one(fn) + with truncated_file(fn_test, 107, 10, tmpdir) as fn, pytest.raises(IOError): + load_one(fn) + with truncated_file(fn_test, 357, 10, tmpdir) as fn, pytest.raises(IOError): + load_one(fn) + with truncated_file(fn_test, 405, 10, tmpdir) as fn, pytest.raises(IOError): + load_one(fn) source = files("iodata.test.data").joinpath("carbon_gs_pp_uncontracted.cp2k.out") - with as_file(source) as fn_test: - with truncated_file(fn_test, 456, 10, tmpdir) as fn: - with pytest.raises(IOError): - load_one(fn) + with ( + as_file(source) as fn_test, + truncated_file(fn_test, 456, 10, tmpdir) as fn, + pytest.raises(IOError), + ): + load_one(fn) diff --git a/iodata/test/test_cube.py b/iodata/test/test_cube.py index 2ed056de4..543eedec9 100644 --- a/iodata/test/test_cube.py +++ b/iodata/test/test_cube.py @@ -63,15 +63,13 @@ def test_load_dump_load_aelta(tmpdir): mol2 = load_one(fn_cube2) with open(fn_cube2) as f: - line_counter = 0 block_counter = 0 - for line in f: - line_counter += 1 - if line_counter > 6 + len(mol2.atnums): + for iline, line in enumerate(f): + if iline > 6 + len(mol2.atnums): if mol2.cube.shape[2] % 6 == 0: assert len(line.split()) == 6 if mol2.cube.shape[2] % 6 != 0: - block_line_counter = line_counter - ( + block_line_counter = iline - ( 6 + len(mol2.atnums) + block_counter * (mol2.cube.shape[2] // 6 + 1) ) if 1 <= block_line_counter <= mol2.cube.shape[2] // 6: diff --git a/iodata/test/test_fchk.py b/iodata/test/test_fchk.py index f478b6825..54d695259 100644 --- a/iodata/test/test_fchk.py +++ b/iodata/test/test_fchk.py @@ -38,9 +38,11 @@ def test_load_fchk_nonexistent(): - with pytest.raises(IOError): - with as_file(files("iodata.test.data").joinpath("fubar_crap.fchk")) as fn: - load_one(str(fn)) + with ( + pytest.raises(IOError), + as_file(files("iodata.test.data").joinpath("fubar_crap.fchk")) as fn, + ): + load_one(str(fn)) def load_fchk_helper(fn_fchk): diff --git a/iodata/test/test_gaussianinput.py b/iodata/test/test_gaussianinput.py index 7d10f85be..330e06daf 100644 --- a/iodata/test/test_gaussianinput.py +++ b/iodata/test/test_gaussianinput.py @@ -67,9 +67,11 @@ def test_load_multi_title(): def test_load_error(): # test error raises when loading .com with z-matrix - with assert_raises(ValueError): - with as_file(files("iodata.test.data").joinpath("water_z.com")) as fn: - load_one(str(fn)) + with ( + assert_raises(ValueError), + as_file(files("iodata.test.data").joinpath("water_z.com")) as fn, + ): + load_one(str(fn)) def check_water(mol, title): diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index b3dacd967..5e70f3bee 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -110,9 +110,11 @@ def test_qcschema_molecule(filename, atnums, charge, spinpol, geometry, nwarn): @pytest.mark.parametrize(("filename", "atnums", "charge", "spinpol", "nwarn"), MOLSSI_MOL_FILES) def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): """Test qcschema_molecule parsing using MolSSI-sourced files.""" - with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(qcschema_molecule)) + with ( + as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(qcschema_molecule)) np.testing.assert_equal(mol.atnums, atnums) assert mol.charge == charge @@ -138,9 +140,11 @@ def test_molssi_qcschema_molecule(filename, atnums, charge, spinpol, nwarn): @pytest.mark.parametrize(("filename", "unparsed_dict"), PASSTHROUGH_MOL_FILES) def test_passthrough_qcschema_molecule(filename, unparsed_dict): """Test qcschema_molecule parsing for passthrough of unparsed keys.""" - with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(qcschema_molecule)) + with ( + as_file(files("iodata.test.data").joinpath(filename)) as qcschema_molecule, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(qcschema_molecule)) assert mol.extra["molecule"]["unparsed"] == unparsed_dict assert len(record) == 1 @@ -373,9 +377,11 @@ def test_qcschema_output(filename, lot, obasis_name, run_type, nwarn): @pytest.mark.parametrize(("filename", "error"), BAD_OUTPUT_FILES) def test_bad_qcschema_files(filename, error): # FIXME: these will move - with as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input: - with pytest.raises(error): - load_one(str(qcschema_input)) + with ( + as_file(files("iodata.test.data").joinpath(filename)) as qcschema_input, + pytest.raises(error), + ): + load_one(str(qcschema_input)) INOUT_OUTPUT_FILES = [ diff --git a/iodata/test/test_mol2.py b/iodata/test/test_mol2.py index f2690e752..babebe5c4 100644 --- a/iodata/test/test_mol2.py +++ b/iodata/test/test_mol2.py @@ -43,10 +43,12 @@ def test_mol2_load_one(): def test_mol2_formaterror(tmpdir): # test if mol2 file has the wrong ending - with as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_test: - with truncated_file(fn_test, 2, 0, tmpdir) as fn: - with pytest.raises(IOError): - load_one(str(fn)) + with ( + as_file(files("iodata.test.data").joinpath("caffeine.mol2")) as fn_test, + truncated_file(fn_test, 2, 0, tmpdir) as fn, + pytest.raises(IOError), + ): + load_one(str(fn)) def test_mol2_symbol(): diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index 16461eeb5..a4ffe8427 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -40,9 +40,11 @@ def test_load_molden_li2_orca(): - with as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "ORCA" in record[0].message.args[0] @@ -70,17 +72,21 @@ def test_load_molden_li2_orca(): def test_load_molden_li2_orca_huge_threshold(): - with as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden: - with warnings.catch_warnings(): - warnings.simplefilter("error") - # The threshold is set very high, which skip a correction for ORCA. - load_one(str(fn_molden), norm_threshold=1e4) + with ( + as_file(files("iodata.test.data").joinpath("li2.molden.input")) as fn_molden, + warnings.catch_warnings(), + ): + warnings.simplefilter("error") + # The threshold is set very high, which skip a correction for ORCA. + load_one(str(fn_molden), norm_threshold=1e4) def test_load_molden_h2o_orca(): - with as_file(files("iodata.test.data").joinpath("h2o.molden.input")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("h2o.molden.input")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "ORCA" in record[0].message.args[0] @@ -287,9 +293,11 @@ def test_load_molden_cfour(): def test_load_molden_nh3_orca(): # The file tested here is created with ORCA. It should be read in # properly by altering normalization and sign conventions. - with as_file(files("iodata.test.data").joinpath("nh3_orca.molden")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("nh3_orca.molden")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "ORCA" in record[0].message.args[0] @@ -307,9 +315,11 @@ def test_load_molden_nh3_orca(): def test_load_molden_nh3_psi4(): # The file tested here is created with PSI4 (pre 1.0). It should be read in # properly by altering normalization conventions. - with as_file(files("iodata.test.data").joinpath("nh3_psi4.molden")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("nh3_psi4.molden")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "PSI4 < 1.0" in record[0].message.args[0] @@ -327,9 +337,11 @@ def test_load_molden_nh3_psi4(): def test_load_molden_nh3_psi4_1(): # The file tested here is created with PSI4 (version 1.0). # It should be read in properly by renormalizing the contractions. - with as_file(files("iodata.test.data").joinpath("nh3_psi4_1.0.molden")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("nh3_psi4_1.0.molden")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "unnormalized" in record[0].message.args[0] @@ -350,9 +362,8 @@ def test_load_molden_high_am_psi4(case): # This is a special case because it contains higher angular momenta than # officially supported by the Molden format. Most virtual orbitals were removed. source = files("iodata.test.data").joinpath(f"psi4_{case}_cc_pvqz_pure.molden") - with as_file(source) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with as_file(source) as fn_molden, pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "unnormalized" in record[0].message.args[0] # Check normalization @@ -372,9 +383,8 @@ def test_load_molden_high_am_orca(case): # This is a special case because it contains higher angular momenta than # officially supported by the Molden format. Most virtual orbitals were removed. source = files("iodata.test.data").joinpath(f"orca_{case}_cc_pvqz_pure.molden") - with as_file(source) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with as_file(source) as fn_molden, pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "ORCA" in record[0].message.args[0] # Check normalization @@ -404,9 +414,8 @@ def test_load_molden_h2o_6_31g_d_cart_psi4(): # The file tested here is created with PSI4 1.3.2. It should be read in # properly after fixing for errors in AO normalization conventions. source = files("iodata.test.data").joinpath("h2o_psi4_1.3.2_6-31G_d_cart.molden") - with as_file(source) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with as_file(source) as fn_molden, pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "PSI4 <= 1.3.2" in record[0].message.args[0] @@ -425,9 +434,8 @@ def test_load_molden_nh3_aug_cc_pvqz_cart_psi4(): # The file tested here is created with PSI4 1.3.2. It should be read in # properly after fixing for errors in AO normalization conventions. source = files("iodata.test.data").joinpath("nh3_psi4_1.3.2_aug_cc_pvqz_cart.molden") - with as_file(source) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with as_file(source) as fn_molden, pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "PSI4 <= 1.3.2" in record[0].message.args[0] @@ -461,9 +469,8 @@ def test_load_molden_nh3_molpro2012(): def test_load_molden_neon_turbomole(): # The file tested here is created with Turbomole 7.1. source = files("iodata.test.data").joinpath("neon_turbomole_def2-qzvp.molden") - with as_file(source) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with as_file(source) as fn_molden, pytest.warns(FileFormatWarning) as record: + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "Turbomole" in record[0].message.args[0] @@ -478,9 +485,11 @@ def test_load_molden_neon_turbomole(): def test_load_molden_nh3_turbomole(): # The file tested here is created with Turbomole 7.1 - with as_file(files("iodata.test.data").joinpath("nh3_turbomole.molden")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("nh3_turbomole.molden")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "Turbomole" in record[0].message.args[0] @@ -498,9 +507,11 @@ def test_load_molden_nh3_turbomole(): def test_load_molden_f(): - with as_file(files("iodata.test.data").joinpath("F.molden")) as fn_molden: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_molden)) + with ( + as_file(files("iodata.test.data").joinpath("F.molden")) as fn_molden, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_molden)) assert len(record) == 1 assert "PSI4" in record[0].message.args[0] diff --git a/iodata/test/test_molekel.py b/iodata/test/test_molekel.py index 7c7fdc9bc..da4cd20b2 100644 --- a/iodata/test/test_molekel.py +++ b/iodata/test/test_molekel.py @@ -75,9 +75,7 @@ def check_load_dump_consistency(fn: str, tmpdir: str, match: Optional[str] = Non 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: + if "molden" in form or "fchk" in form: compare_mols_diff_formats(mol1, mol2) else: compare_mols(mol1, mol2) @@ -160,8 +158,10 @@ def test_load_mkl_h2(): def test_load_mkl_h2_huge_threshold(): - with as_file(files("iodata.test.data").joinpath("h2_sto3g.mkl")) as fn_molekel: - with warnings.catch_warnings(): - warnings.simplefilter("error") - # The threshold is set very high, which skip a correction for ORCA. - load_one(str(fn_molekel), norm_threshold=1e4) + with ( + as_file(files("iodata.test.data").joinpath("h2_sto3g.mkl")) as fn_molekel, + warnings.catch_warnings(), + ): + warnings.simplefilter("error") + # The threshold is set very high, which skip a correction for ORCA. + load_one(str(fn_molekel), norm_threshold=1e4) diff --git a/iodata/test/test_pdb.py b/iodata/test/test_pdb.py index 714500db9..956a1c8fd 100644 --- a/iodata/test/test_pdb.py +++ b/iodata/test/test_pdb.py @@ -43,9 +43,11 @@ def test_load_water(case): 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 pytest.warns(FileFormatWarning, match="The END is not found"): - mol = load_one(str(fn_pdb)) + with ( + as_file(files("iodata.test.data").joinpath("water_single_no_end.pdb")) as fn_pdb, + pytest.warns(FileFormatWarning, match="The END is not found"), + ): + mol = load_one(str(fn_pdb)) check_water(mol) @@ -134,9 +136,11 @@ def test_load_dump_xyz_consistency(tmpdir): def test_load_peptide_2luv(): # test pdb of small peptide - with as_file(files("iodata.test.data").joinpath("2luv.pdb")) as fn_pdb: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(str(fn_pdb)) + with ( + as_file(files("iodata.test.data").joinpath("2luv.pdb")) as fn_pdb, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(str(fn_pdb)) assert len(record) == 271 assert mol.title.startswith("INTEGRIN") assert_equal(len(mol.atnums), 547) @@ -233,9 +237,11 @@ def test_load_ch5plus_bonds(): def test_indomethacin_dimer(): - with as_file(files("iodata.test.data").joinpath("indomethacin-dimer.pdb")) as fn_pdb: - with pytest.warns(FileFormatWarning) as record: - mol = load_one(fn_pdb) + with ( + as_file(files("iodata.test.data").joinpath("indomethacin-dimer.pdb")) as fn_pdb, + pytest.warns(FileFormatWarning) as record, + ): + mol = load_one(fn_pdb) assert len(record) == 82 for item in record: assert "Using the atom name in the PDB" in item.message.args[0] diff --git a/iodata/test/test_sdf.py b/iodata/test/test_sdf.py index 59513399a..04554dde6 100644 --- a/iodata/test/test_sdf.py +++ b/iodata/test/test_sdf.py @@ -53,10 +53,12 @@ def test_sdf_load_one_formamide(): def test_sdf_formaterror(tmpdir): # test if sdf file has the wrong ending without $$$$ - with as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_test: - with truncated_file(fn_test, 36, 0, tmpdir) as fn: - with pytest.raises(IOError): - load_one(str(fn)) + with ( + as_file(files("iodata.test.data").joinpath("example.sdf")) as fn_test, + truncated_file(fn_test, 36, 0, tmpdir) as fn, + pytest.raises(IOError), + ): + load_one(str(fn)) def check_example(mol): @@ -127,6 +129,8 @@ def test_load_dump_many_consistency(tmpdir): def test_v2000_check(): - with as_file(files("iodata.test.data").joinpath("molv3000.sdf")) as fn_sdf: - with pytest.raises(FileFormatError): - load_one(fn_sdf) + with ( + as_file(files("iodata.test.data").joinpath("molv3000.sdf")) as fn_sdf, + pytest.raises(FileFormatError), + ): + load_one(fn_sdf) diff --git a/iodata/test/test_wfx.py b/iodata/test/test_wfx.py index 150c50fe9..67106034c 100644 --- a/iodata/test/test_wfx.py +++ b/iodata/test/test_wfx.py @@ -611,9 +611,11 @@ def test_parse_wfx_missing_tag_h2o(): def test_load_data_wfx_h2o_error(): """Check that sections without a closing tag result in an exception.""" - with as_file(files("iodata.test.data").joinpath("h2o_error.wfx")) as fn_wfx: - with pytest.raises(IOError) as error: - load_one(str(fn_wfx)) + with ( + as_file(files("iodata.test.data").joinpath("h2o_error.wfx")) as fn_wfx, + 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>." ) @@ -621,10 +623,12 @@ def test_load_data_wfx_h2o_error(): def test_load_truncated_h2o(tmpdir): """Check that a truncated file raises an exception.""" - with as_file(files("iodata.test.data").joinpath("water_sto3g_hf.wfx")) as fn_wfx: - with truncated_file(str(fn_wfx), 152, 0, tmpdir) as fn_truncated: - with pytest.raises(IOError) as error: - load_one(str(fn_truncated)) + with ( + as_file(files("iodata.test.data").joinpath("water_sto3g_hf.wfx")) as fn_wfx, + truncated_file(str(fn_wfx), 152, 0, tmpdir) as fn_truncated, + 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." ) diff --git a/iodata/utils.py b/iodata/utils.py index 0548f0bf5..724abc06f 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -76,7 +76,7 @@ def __init__(self, filename: str): """ self.filename = filename - self.f = open(filename) + self.f = open(filename) # noqa self.lineno = 0 self.stack = [] @@ -88,12 +88,8 @@ def __iter__(self): def __next__(self): """Return the next line and increase the lineno attribute by one.""" - if self.stack: - line = self.stack.pop() - else: - line = next(self.f) self.lineno += 1 - return line + return self.stack.pop() if self.stack else next(self.f) def error(self, msg: str): """Raise an error while reading a file. diff --git a/pyproject.toml b/pyproject.toml index e6e0ce4e3..442f6a30e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY" + "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From a44d0b48829145324ca49f84ddeaf54fac28ca38 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Fri, 24 May 2024 09:24:34 +0200 Subject: [PATCH 114/144] Enable perflint rules --- iodata/api.py | 8 ++++---- iodata/basis.py | 3 +-- iodata/formats/chgcar.py | 6 ++---- iodata/formats/extxyz.py | 8 ++++---- iodata/formats/gromacs.py | 8 ++++---- iodata/formats/mol2.py | 8 ++++---- iodata/formats/molekel.py | 9 +++------ iodata/formats/pdb.py | 8 ++++---- iodata/formats/sdf.py | 8 ++++---- iodata/formats/wfn.py | 6 ++++-- iodata/formats/xyz.py | 11 +++++------ iodata/overlap.py | 4 +--- pyproject.toml | 2 +- 13 files changed, 41 insertions(+), 48 deletions(-) diff --git a/iodata/api.py b/iodata/api.py index bfce5cffb..ef0fda119 100644 --- a/iodata/api.py +++ b/iodata/api.py @@ -169,11 +169,11 @@ def load_many(filename: str, fmt: Optional[str] = None, **kwargs) -> Iterator[IO """ format_module = _select_format_module(filename, "load_many", fmt) lit = LineIterator(filename) - for data in format_module.load_many(lit, **kwargs): - try: + try: + for data in format_module.load_many(lit, **kwargs): yield IOData(**data) - except StopIteration: - return + except StopIteration: + return def dump_one(iodata: IOData, filename: str, fmt: Optional[str] = None, **kwargs): diff --git a/iodata/basis.py b/iodata/basis.py index 26cb8baa8..5dea1a134 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -334,8 +334,7 @@ def convert_conventions( conv2 = new_conventions[key] shell_permutation, shell_signs = convert_convention_shell(conv1, conv2, reverse) offset = len(permutation) - for i in shell_permutation: - permutation.append(i + offset) + permutation.extend(i + offset for i in shell_permutation) signs.extend(shell_signs) return np.array(permutation), np.array(signs) diff --git a/iodata/formats/chgcar.py b/iodata/formats/chgcar.py index 593eb2ad5..40fc18006 100644 --- a/iodata/formats/chgcar.py +++ b/iodata/formats/chgcar.py @@ -62,10 +62,8 @@ def _load_vasp_header(lit: LineIterator) -> tuple[str, np.ndarray, np.ndarray, n # read cell parameters in angstrom, without the universal scaling factor. # each row is one cell vector - cellvecs = [] - for _i in range(3): - cellvecs.append([float(w) for w in next(lit).split()]) - cellvecs = np.array(cellvecs) * angstrom * scaling + cellvecs = np.array([[float(w) for w in next(lit).split()] for _ in range(3)]) + cellvecs *= angstrom * scaling # note that in older VASP version the following line might be absent vasp_atnums = [sym2num[w] for w in next(lit).split()] diff --git a/iodata/formats/extxyz.py b/iodata/formats/extxyz.py index af1922896..ca3546b98 100644 --- a/iodata/formats/extxyz.py +++ b/iodata/formats/extxyz.py @@ -204,13 +204,13 @@ 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,' # making it trivial to load many frames. - while True: - try: + try: + while True: # Check for and skip empty lines at the end of file line = next(lit) if line.strip() == "": return lit.back(line) yield load_one(lit) - except StopIteration: - return + except StopIteration: + return diff --git a/iodata/formats/gromacs.py b/iodata/formats/gromacs.py index e976d1022..f41905a23 100644 --- a/iodata/formats/gromacs.py +++ b/iodata/formats/gromacs.py @@ -72,11 +72,11 @@ 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, # making it trivial to load many frames. - while True: - try: + try: + while True: yield load_one(lit) - except OSError: - return + except OSError: + return def _helper_read_frame(lit: LineIterator) -> tuple: diff --git a/iodata/formats/mol2.py b/iodata/formats/mol2.py index 50fc0f3c6..d28d5dcf8 100644 --- a/iodata/formats/mol2.py +++ b/iodata/formats/mol2.py @@ -144,11 +144,11 @@ 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,' # making it trivial to load many frames. - while True: - try: + try: + while True: yield load_one(lit) - except OSError: - return + except OSError: + return @document_dump_one("MOL2", ["atcoords", "atnums"], ["atcharges", "atffparams", "title"]) diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 9770f020f..96489e1f4 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -119,16 +119,14 @@ def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[np.ndarray, np. words = line.split() ncol = len(words) assert ncol > 0 - for word in words: - irreps.append(word) + irreps.extend(words) cols = [np.zeros((nbasis, 1), float) for _ in range(ncol)] in_orb = 1 elif in_orb == 1: # read energies words = line.split() assert len(words) == ncol - for word in words: - energies.append(float(word)) + energies.extend(float(word) for word in words) in_orb = 2 ibasis = 0 elif in_orb == 2: @@ -150,8 +148,7 @@ def _load_helper_occ(lit: LineIterator) -> np.ndarray: for line in lit: if line.strip() == "$END": break - for word in line.split(): - occs.append(float(word)) + occs.extend(float(word) for word in line.split()) return np.array(occs) diff --git a/iodata/formats/pdb.py b/iodata/formats/pdb.py index 40e7a27f7..96611de9c 100644 --- a/iodata/formats/pdb.py +++ b/iodata/formats/pdb.py @@ -237,11 +237,11 @@ 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,' # making it trivial to load many frames. - while True: - try: + try: + while True: yield load_one(lit) - except OSError: - return + except OSError: + return def _dump_multiline_str(f: TextIO, key: str, value: str): diff --git a/iodata/formats/sdf.py b/iodata/formats/sdf.py index d94aa5e1b..baa6f4424 100644 --- a/iodata/formats/sdf.py +++ b/iodata/formats/sdf.py @@ -99,11 +99,11 @@ 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,' # making it travial to load many frames. - while True: - try: + try: + while True: yield load_one(lit) - except StopIteration: - return + except StopIteration: + return @document_dump_one("SDF", ["atcoords", "atnums"], ["title", "bonds"]) diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index 7f4c9f6e4..dc97b915d 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -340,8 +340,10 @@ def build_obasis( # 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.extend( + Shell(icenter, [angmom], ["c"], np.array([exponent]), np.array([[1.0]])) + for exponent in batch_exponents + ) # Move on to the next contraction ibasis += ncart * ncon obasis = MolecularBasis(shells, CONVENTIONS, "L2") diff --git a/iodata/formats/xyz.py b/iodata/formats/xyz.py index 5b4ed393e..b67a681b2 100644 --- a/iodata/formats/xyz.py +++ b/iodata/formats/xyz.py @@ -147,16 +147,16 @@ 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,' # making it trivial to load many frames. - while True: - try: + try: + while True: # Check for and skip empty lines at the end of file line = next(lit) if line.strip() == "": return lit.back(line) yield load_one(lit, atom_columns) - except StopIteration: - return + except StopIteration: + return @document_dump_one("XYZ", ["atcoords", "atnums"], ["title"], {"atom_columns": ATOM_COLUMNS_DOC}) @@ -175,8 +175,7 @@ def dump_one(f: TextIO, data: IOData, atom_columns=None): if keyname is not None: # The data to be written is a value of a dictionary attribute. values = values[keyname] - for value in values[iatom].flat: - words.append(dumpword(value)) + words.extend(dumpword(value) for value in values[iatom].flat) print(" ".join(words), file=f) diff --git a/iodata/overlap.py b/iodata/overlap.py index 1d4efac4d..b2671a97c 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -272,9 +272,7 @@ def _compute_cart_shell_normalizations(shell: Shell) -> np.ndarray: result = [] for angmom in shell.angmoms: for exponent in shell.exponents: - row = [] - for n in iter_cart_alphabet(angmom): - row.append(gob_cart_normalization(exponent, n)) + row = [gob_cart_normalization(exponent, n) for n in iter_cart_alphabet(angmom)] result.append(row) return np.array(result) diff --git a/pyproject.toml b/pyproject.toml index 442f6a30e..11e3a3c3c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM" + "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM", "PERF" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From a050e1cb9050d3f058ac97358effba74af4cbccd Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Fri, 24 May 2024 09:26:02 +0200 Subject: [PATCH 115/144] Enable tryceratops rules --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 11e3a3c3c..e01234e1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ target-version = "py39" [tool.ruff.lint] select = [ "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM", "PERF" + "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM", "PERF", "TRY" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ @@ -69,4 +69,5 @@ ignore = [ "PLR0917", # https://docs.astral.sh/ruff/rules/too-many-positional/ "PLR2004", # https://docs.astral.sh/ruff/rules/magic-value-comparison/ "PT011", # https://docs.astral.sh/ruff/rules/pytest-raises-too-broad/ + "TRY003", # https://docs.astral.sh/ruff/rules/raise-vanilla-args/ ] From 47c165ec06279fbf5771edadff69b05d2a91e8d8 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Fri, 24 May 2024 09:30:58 +0200 Subject: [PATCH 116/144] Sort rule codes --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e01234e1b..35a2e9fd8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,8 +55,8 @@ target-version = "py39" [tool.ruff.lint] select = [ - "E", "W", "F", "I", "N", "UP", "B", "BLE", "A", "EXE", "ISC", "ICN", "PIE", "PYI", "PT", - "PL", "RUF", "C4", "RSE", "RET", "SLF", "NPY", "SIM", "PERF", "TRY" + "A", "B", "BLE", "C4", "E", "EXE", "F", "I", "ICN", "ISC", "N", "NPY", "PERF", "PIE", + "PL", "PT", "PYI", "RET", "RSE", "RUF", "SIM", "SLF", "TRY", "UP", "W" ] ignore = [ "PLR0904", # https://docs.astral.sh/ruff/rules/too-many-public-methods/ From 4fc553a3e0dffb890c94705dc9d86ccca30cfefa Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 29 May 2024 10:07:51 +0200 Subject: [PATCH 117/144] Add PyPI release workflow --- .github/workflows/release.yaml | 128 +++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 000000000..6c8af7061 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,128 @@ +# This workflow is adapted from: +# https://packaging.python.org/en/latest/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ +# Changed made: +# - Remove emoticons +# - Update some actions to more recent versions + +name: release + +on: push + +env: + # This is not critical + # It is used only for showing URLs in GitHub Actions web interface. + PYPI_NAME: qc-iodata + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.9" + - name: Install pypa/build + run: >- + python -m pip install build + - name: Build package + run: >- + python -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + publish-to-pypi: + name: Publish Python distribution to PyPI + if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes + needs: + - build + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/${{ env.PYPI_NAME }} + permissions: + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + + github-release: + name: >- + Sign the Python distribution with Sigstore + and upload them to GitHub Release + needs: + - publish-to-pypi + runs-on: ubuntu-latest + + permissions: + contents: write + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v2.1.1 + with: + inputs: >- + ./dist/*.tar.gz + ./dist/*.whl + - name: Create GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + run: >- + gh release create + '${{ github.ref_name }}' + --repo '${{ github.repository }}' + --notes "" + - name: Upload artifact signatures to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + # Upload to GitHub Release using the `gh` CLI. + # `dist/` contains the built packages, and the + # sigstore-produced signatures and certificates. + run: >- + gh release upload + '${{ github.ref_name }}' dist/** + --repo '${{ github.repository }}' + + publish-to-testpypi: + name: Publish Python distribution to TestPyPI + if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'reproducible-reporting'}} + needs: + - build + runs-on: ubuntu-latest + + environment: + name: testpypi + url: https://test.pypi.org/p/${{ env.PYPI_NAME }} + + permissions: + id-token: write + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ From 99e0f94ca011592b6209bf52604e6bec4c9b7856 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 29 May 2024 17:30:59 +0200 Subject: [PATCH 118/144] Migrate from attr to attrs --- iodata/basis.py | 31 ++++++------ iodata/formats/chgcar.py | 3 +- iodata/formats/cp2klog.py | 15 +++--- iodata/formats/cube.py | 19 ++++---- iodata/formats/fchk.py | 7 +-- iodata/formats/gamess.py | 5 +- iodata/formats/gaussianlog.py | 5 +- iodata/formats/mol2.py | 7 ++- iodata/formats/molden.py | 15 +++--- iodata/formats/molekel.py | 7 +-- iodata/formats/mwfn.py | 3 +- iodata/formats/orcalog.py | 5 +- iodata/formats/qchemlog.py | 9 ++-- iodata/formats/wfn.py | 17 ++++--- iodata/inputs/common.py | 4 +- iodata/iodata.py | 91 ++++++++++++++++++----------------- iodata/orbitals.py | 29 +++++------ iodata/overlap.py | 21 ++++---- iodata/test/common.py | 4 +- iodata/test/test_attrutils.py | 41 ++++++++-------- iodata/test/test_basis.py | 4 +- iodata/test/test_json.py | 2 +- iodata/test/test_molden.py | 4 +- iodata/test/test_overlap.py | 6 +-- iodata/utils.py | 19 ++++---- 25 files changed, 193 insertions(+), 180 deletions(-) diff --git a/iodata/basis.py b/iodata/basis.py index 5dea1a134..510eec806 100644 --- a/iodata/basis.py +++ b/iodata/basis.py @@ -28,8 +28,9 @@ from numbers import Integral from typing import Union -import attr +import attrs import numpy as np +from numpy.typing import NDArray from .attrutils import validate_shape @@ -100,7 +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]) +@attrs.define class Shell: """A shell in a molecular basis representing (generalized) contractions with the same exponents. @@ -126,11 +127,11 @@ class Shell: """ - icenter: int - angmoms: list[int] = attr.ib(validator=validate_shape(("coeffs", 1))) - kinds: list[str] = attr.ib(validator=validate_shape(("coeffs", 1))) - exponents: np.ndarray = attr.ib(validator=validate_shape(("coeffs", 0))) - coeffs: np.ndarray = attr.ib(validator=validate_shape(("exponents", 0), ("kinds", 0))) + icenter: int = attrs.field() + angmoms: list[int] = attrs.field(validator=validate_shape(("coeffs", 1))) + kinds: list[str] = attrs.field(validator=validate_shape(("coeffs", 1))) + exponents: NDArray = attrs.field(validator=validate_shape(("coeffs", 0))) + coeffs: NDArray = attrs.field(validator=validate_shape(("exponents", 0), ("kinds", 0))) @property def nbasis(self) -> int: @@ -156,7 +157,7 @@ def ncon(self) -> int: return len(self.angmoms) -@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) +@attrs.define class MolecularBasis: """A complete molecular orbital or density basis set. @@ -205,9 +206,9 @@ class MolecularBasis: """ - shells: list[Shell] - conventions: dict[str, str] - primitive_normalization: str + shells: list[Shell] = attrs.field() + conventions: dict[str, str] = attrs.field() + primitive_normalization: str = attrs.field() @property def nbasis(self) -> int: @@ -222,12 +223,12 @@ def get_segmented(self): shells.append( Shell(shell.icenter, [angmom], [kind], shell.exponents, coeffs.reshape(-1, 1)) ) - return attr.evolve(self, shells=shells) + return attrs.evolve(self, shells=shells) def convert_convention_shell( conv1: list[str], conv2: list[str], reverse=False -) -> tuple[np.ndarray, np.ndarray]: +) -> tuple[NDArray, 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 @@ -289,7 +290,7 @@ def convert_convention_shell( def convert_conventions( molbasis: MolecularBasis, new_conventions: dict[str, list[str]], reverse=False -) -> tuple[np.ndarray, np.ndarray]: +) -> tuple[NDArray, 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 @@ -339,7 +340,7 @@ def convert_conventions( return np.array(permutation), np.array(signs) -def iter_cart_alphabet(n: int) -> np.ndarray: +def iter_cart_alphabet(n: int) -> NDArray: """Loop over powers of Cartesian basis functions in alphabetical order. See https://theochem.github.io/horton/2.1.1/tech_ref_gaussian_basis.html diff --git a/iodata/formats/chgcar.py b/iodata/formats/chgcar.py index 40fc18006..619e1c531 100644 --- a/iodata/formats/chgcar.py +++ b/iodata/formats/chgcar.py @@ -26,6 +26,7 @@ """ import numpy as np +from numpy.typing import NDArray from ..docstrings import document_load_one from ..periodic import sym2num @@ -37,7 +38,7 @@ PATTERNS = ["CHGCAR*", "AECCAR*"] -def _load_vasp_header(lit: LineIterator) -> tuple[str, np.ndarray, np.ndarray, np.ndarray]: +def _load_vasp_header(lit: LineIterator) -> tuple[str, NDArray, NDArray, NDArray]: """Load the cell and atoms from a VASP file format. Parameters diff --git a/iodata/formats/cp2klog.py b/iodata/formats/cp2klog.py index 2fec908ed..77d74a0bd 100644 --- a/iodata/formats/cp2klog.py +++ b/iodata/formats/cp2klog.py @@ -21,6 +21,7 @@ from typing import Union import numpy as np +from numpy.typing import NDArray from scipy.special import factorialk from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell, angmom_sti @@ -42,9 +43,7 @@ } -def _get_cp2k_norm_corrections( - ell: int, alphas: Union[float, np.ndarray] -) -> Union[float, np.ndarray]: +def _get_cp2k_norm_corrections(ell: int, alphas: Union[float, NDArray]) -> Union[float, NDArray]: """Compute the corrections for the normalization of the basis functions. This correction is needed because the CP2K atom code works with a different @@ -236,7 +235,7 @@ def _read_cp2k_occupations_energies( def _read_cp2k_orbital_coeffs( lit: LineIterator, oe: list[tuple[int, int, float, float]] -) -> dict[tuple[int, int], np.ndarray]: +) -> dict[tuple[int, int], NDArray]: """Read the expansion coefficients of the orbital from an open CP2K ATOM output. Parameters @@ -294,11 +293,11 @@ def _get_norb_nel(oe: list[tuple[int, int, float, float]]) -> tuple[int, float]: def _fill_orbitals( - orb_coeffs: np.ndarray, - orb_energies: np.ndarray, - orb_occupations: np.ndarray, + orb_coeffs: NDArray, + orb_energies: NDArray, + orb_occupations: NDArray, oe: list[tuple[int, int, float, float]], - coeffs: dict[tuple[int, int], np.ndarray], + coeffs: dict[tuple[int, int], NDArray], obasis: MolecularBasis, restricted: bool, ): diff --git a/iodata/formats/cube.py b/iodata/formats/cube.py index 0a9c38635..df190970b 100644 --- a/iodata/formats/cube.py +++ b/iodata/formats/cube.py @@ -29,6 +29,7 @@ from typing import TextIO import numpy as np +from numpy.typing import NDArray from ..docstrings import document_dump_one, document_load_one from ..iodata import IOData @@ -42,7 +43,7 @@ def _read_cube_header( lit: LineIterator, -) -> tuple[str, np.ndarray, np.ndarray, np.ndarray, dict[str, np.ndarray], np.ndarray]: +) -> tuple[str, NDArray, NDArray, NDArray, dict[str, NDArray], NDArray]: """Load header data from a CUBE file object. Parameters @@ -62,7 +63,7 @@ def _read_cube_header( # skip the second line next(lit) - def read_grid_line(line: str) -> tuple[int, np.ndarray]: + def read_grid_line(line: str) -> tuple[int, NDArray]: """Read a grid line from the cube file.""" words = line.split() return ( @@ -83,7 +84,7 @@ def read_grid_line(line: str) -> tuple[int, np.ndarray]: cellvecs = axes * shape.reshape(-1, 1) cube = {"origin": origin, "axes": axes, "shape": shape} - def read_atom_line(line: str) -> tuple[int, float, np.ndarray]: + def read_atom_line(line: str) -> tuple[int, float, NDArray]: """Read an atomic number and coordinate from the cube file.""" words = line.split() return ( @@ -106,7 +107,7 @@ def read_atom_line(line: str) -> tuple[int, float, np.ndarray]: return title, atcoords, atnums, cellvecs, cube, atcorenums -def _read_cube_data(lit: LineIterator, cube: dict[str, np.ndarray]): +def _read_cube_data(lit: LineIterator, cube: dict[str, NDArray]): """Load cube data from a CUBE file object. Parameters @@ -150,10 +151,10 @@ def load_one(lit: LineIterator) -> dict: def _write_cube_header( f: TextIO, title: str, - atcoords: np.ndarray, - atnums: np.ndarray, - cube: dict[str, np.ndarray], - atcorenums: np.ndarray, + atcoords: NDArray, + atnums: NDArray, + cube: dict[str, NDArray], + atcorenums: NDArray, ): print(title, file=f) print("OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z", file=f) @@ -169,7 +170,7 @@ def _write_cube_header( 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): +def _write_cube_data(f: TextIO, cube_data: NDArray, block_size: int): counter = 0 for value in cube_data.flat: f.write(f" {value: 12.5E}") diff --git a/iodata/formats/fchk.py b/iodata/formats/fchk.py index 1d86c09c7..62f62b00f 100644 --- a/iodata/formats/fchk.py +++ b/iodata/formats/fchk.py @@ -23,6 +23,7 @@ from typing import Optional, TextIO import numpy as np +from numpy.typing import NDArray from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell, convert_conventions from ..docstrings import document_dump_one, document_load_many, document_load_one @@ -473,7 +474,7 @@ def _load_dm(label: str, fchk: dict, result: dict, key: str): result[key] = _triangle_to_dense(fchk[label]) -def _triangle_to_dense(triangle: np.ndarray) -> np.ndarray: +def _triangle_to_dense(triangle: NDArray) -> NDArray: """Convert a symmetric matrix in triangular storage to a dense square matrix. Parameters @@ -512,7 +513,7 @@ def _dump_real_scalars(name: str, val: float, f: TextIO): print(f"{name:40} R {float(val): 16.8E}", file=f) -def _dump_integer_arrays(name: str, val: np.ndarray, f: TextIO): +def _dump_integer_arrays(name: str, val: NDArray, f: TextIO): """Dumper for a array of integers.""" nval = val.size if nval != 0: @@ -527,7 +528,7 @@ def _dump_integer_arrays(name: str, val: np.ndarray, f: TextIO): k = 0 -def _dump_real_arrays(name: str, val: np.ndarray, f: TextIO): +def _dump_real_arrays(name: str, val: NDArray, f: TextIO): """Dumper for a array of float.""" nval = val.size if nval != 0: diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index 0428420d7..790ef5e44 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -19,6 +19,7 @@ """GAMESS punch file format.""" import numpy as np +from numpy.typing import NDArray from ..docstrings import document_load_one from ..utils import LineIterator, angstrom @@ -81,7 +82,7 @@ def _read_energy(lit: LineIterator, result: dict) -> tuple: return energy, gradient -def _read_hessian(lit: LineIterator, result: dict) -> np.ndarray: +def _read_hessian(lit: LineIterator, result: dict) -> NDArray: """Extract ``hessian`` from the punch file.""" # check that $HESS is not already parsed if "athessian" in result: @@ -102,7 +103,7 @@ def _read_hessian(lit: LineIterator, result: dict) -> np.ndarray: return hessian -def _read_masses(lit: LineIterator, result: dict) -> np.ndarray: +def _read_masses(lit: LineIterator, result: dict) -> NDArray: """Extract ``masses`` from the punch file.""" natom = len(result["symbols"]) masses = np.zeros(natom, float) diff --git a/iodata/formats/gaussianlog.py b/iodata/formats/gaussianlog.py index b9341550a..6a2815fb4 100644 --- a/iodata/formats/gaussianlog.py +++ b/iodata/formats/gaussianlog.py @@ -28,6 +28,7 @@ """ import numpy as np +from numpy.typing import NDArray from ..docstrings import document_load_one from ..utils import LineIterator, set_four_index_element @@ -73,7 +74,7 @@ def load_one(lit: LineIterator) -> dict: return result -def _load_twoindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: +def _load_twoindex_g09(lit: LineIterator, nbasis: int) -> NDArray: """Load a two-index operator from a GAUSSIAN LOG file format. Parameters @@ -106,7 +107,7 @@ def _load_twoindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: return result -def _load_fourindex_g09(lit: LineIterator, nbasis: int) -> np.ndarray: +def _load_fourindex_g09(lit: LineIterator, nbasis: int) -> NDArray: """Load a four-index operator from a GAUSSIAN LOG file. Parameters diff --git a/iodata/formats/mol2.py b/iodata/formats/mol2.py index d28d5dcf8..7e481bff7 100644 --- a/iodata/formats/mol2.py +++ b/iodata/formats/mol2.py @@ -26,6 +26,7 @@ from typing import TextIO import numpy as np +from numpy.typing import NDArray from ..docstrings import ( document_dump_many, @@ -83,9 +84,7 @@ def load_one(lit: LineIterator) -> dict: 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[NDArray, NDArray, NDArray, tuple]: """Load element numbers, coordinates and atomic charges.""" atnums = np.empty(natoms) atcoords = np.empty((natoms, 3)) @@ -112,7 +111,7 @@ def _load_helper_atoms( return atnums, atcoords, atchgs, attypes -def _load_helper_bonds(lit: LineIterator, nbonds: int) -> tuple[np.ndarray]: +def _load_helper_bonds(lit: LineIterator, nbonds: int) -> NDArray: """Load bond information. Each line in a bond definition has the following structure diff --git a/iodata/formats/molden.py b/iodata/formats/molden.py index 964752eb0..9353f8365 100644 --- a/iodata/formats/molden.py +++ b/iodata/formats/molden.py @@ -28,8 +28,9 @@ import copy from typing import TextIO, Union -import attr +import attrs import numpy as np +from numpy.typing import NDArray from ..basis import ( HORTON2_CONVENTIONS, @@ -225,9 +226,7 @@ def _load_low(lit: LineIterator) -> dict: 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[NDArray, NDArray, NDArray]: """Load element numbers and coordinates.""" atnums = [] atcorenums = [] @@ -357,9 +356,9 @@ def _load_helper_coeffs(lit: LineIterator) -> tuple: def _is_normalized_properly( obasis: MolecularBasis, - atcoords: np.ndarray, - orb_alpha: np.ndarray, - orb_beta: np.ndarray, + atcoords: NDArray, + orb_alpha: NDArray, + orb_beta: NDArray, norm_threshold: float = 1e-4, ) -> bool: """Test the normalization of the occupied and virtual orbitals. @@ -551,7 +550,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 + [attrs.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] diff --git a/iodata/formats/molekel.py b/iodata/formats/molekel.py index 96489e1f4..299a6c0f0 100644 --- a/iodata/formats/molekel.py +++ b/iodata/formats/molekel.py @@ -26,6 +26,7 @@ from typing import TextIO import numpy as np +from numpy.typing import NDArray from ..basis import MolecularBasis, Shell, angmom_its, angmom_sti, convert_conventions from ..docstrings import document_dump_one, document_load_one @@ -56,7 +57,7 @@ def _load_helper_charges(lit: LineIterator) -> dict: return {"mulliken": np.array(atcharges)} -def _load_helper_atoms(lit: LineIterator) -> tuple[np.ndarray, np.ndarray]: +def _load_helper_atoms(lit: LineIterator) -> tuple[NDArray, NDArray]: atnums = [] atcoords = [] for line in lit: @@ -105,7 +106,7 @@ def _load_helper_obasis(lit: LineIterator) -> MolecularBasis: return MolecularBasis(shells, CONVENTIONS, "L2") -def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[np.ndarray, np.ndarray]: +def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[NDArray, NDArray]: coeffs = [] energies = [] irreps = [] @@ -143,7 +144,7 @@ def _load_helper_coeffs(lit: LineIterator, nbasis: int) -> tuple[np.ndarray, np. return np.hstack(coeffs), np.array(energies), irreps -def _load_helper_occ(lit: LineIterator) -> np.ndarray: +def _load_helper_occ(lit: LineIterator) -> NDArray: occs = [] for line in lit: if line.strip() == "$END": diff --git a/iodata/formats/mwfn.py b/iodata/formats/mwfn.py index f625a24f7..6ede364ff 100644 --- a/iodata/formats/mwfn.py +++ b/iodata/formats/mwfn.py @@ -19,6 +19,7 @@ """Multiwfn MWFN file format.""" import numpy as np +from numpy.typing import NDArray from ..basis import HORTON2_CONVENTIONS, MolecularBasis, Shell from ..docstrings import document_load_one @@ -175,7 +176,7 @@ def _load_helper_shells(lit: LineIterator, nshell: int) -> dict: def _load_helper_section( lit: LineIterator, nprim: int, start: str, skip: int, dtype: np.dtype -) -> np.ndarray: +) -> NDArray: """Read single or multiple line(s) section.""" section = [] while len(section) < nprim: diff --git a/iodata/formats/orcalog.py b/iodata/formats/orcalog.py index e5619f0ec..d6c385131 100644 --- a/iodata/formats/orcalog.py +++ b/iodata/formats/orcalog.py @@ -21,6 +21,7 @@ from typing import TextIO import numpy as np +from numpy.typing import NDArray from ..docstrings import document_load_one from ..utils import LineIterator @@ -87,7 +88,7 @@ def _helper_number_atoms(lit: LineIterator) -> int: return natom -def _helper_geometry(lit: TextIO, natom: int) -> tuple[np.ndarray, np.ndarray]: +def _helper_geometry(lit: TextIO, natom: int) -> tuple[NDArray, NDArray]: """Load coordinates form a ORCA output file format. Parameters @@ -119,7 +120,7 @@ def _helper_geometry(lit: TextIO, natom: int) -> tuple[np.ndarray, np.ndarray]: return atnums, atcoords -def _helper_scf_energies(lit: TextIO) -> tuple[np.ndarray, np.ndarray]: +def _helper_scf_energies(lit: TextIO) -> tuple[NDArray, NDArray]: """Load energies from each SCF cycle from a ORCA output file format. Parameters diff --git a/iodata/formats/qchemlog.py b/iodata/formats/qchemlog.py index 719d480d3..320f9d466 100644 --- a/iodata/formats/qchemlog.py +++ b/iodata/formats/qchemlog.py @@ -22,6 +22,7 @@ """ import numpy as np +from numpy.typing import NDArray from ..docstrings import document_load_one from ..orbitals import MolecularOrbitals @@ -307,7 +308,7 @@ def _helper_orbital_energies_unrestricted(lit: LineIterator) -> tuple: return subdata -def _helper_section(start: str, end: str, lit: LineIterator, backward: bool = False) -> np.ndarray: +def _helper_section(start: str, end: str, lit: LineIterator, backward: bool = False) -> NDArray: """Load data between starting and ending strings.""" data = [] for line in lit: @@ -324,7 +325,7 @@ def _helper_section(start: str, end: str, lit: LineIterator, backward: bool = Fa return np.array(data, dtype=float) -def _helper_mulliken(lit: LineIterator) -> np.ndarray: +def _helper_mulliken(lit: LineIterator) -> NDArray: """Load mulliken net atomic charges.""" # skip line between 'Ground-State Mulliken Net Atomic Charges' line & atomic charge entries while True: @@ -358,7 +359,7 @@ def _helper_dipole_moments(lit: LineIterator) -> tuple: return dipole, quadrupole, dipole_tol -def _helper_polar(lit: LineIterator) -> np.ndarray: +def _helper_polar(lit: LineIterator) -> NDArray: """Load polarizability matrix.""" next(lit) polarizability_tensor = [] @@ -369,7 +370,7 @@ def _helper_polar(lit: LineIterator) -> np.ndarray: return np.array(polarizability_tensor, dtype=float) -def _helper_hessian(lit: LineIterator, natom: int) -> np.ndarray: +def _helper_hessian(lit: LineIterator, natom: int) -> NDArray: """Load hessian matrix.""" # hessian in Cartesian coordinates, shape(3 * natom, 3 * natom) col_idx = [int(i) for i in next(lit).split()] diff --git a/iodata/formats/wfn.py b/iodata/formats/wfn.py index dc97b915d..aada13da2 100644 --- a/iodata/formats/wfn.py +++ b/iodata/formats/wfn.py @@ -30,6 +30,7 @@ from typing import TextIO import numpy as np +from numpy.typing import NDArray from ..basis import MolecularBasis, Shell, convert_conventions from ..docstrings import document_dump_one, document_load_one @@ -137,7 +138,7 @@ def _load_helper_num(lit: LineIterator) -> list[int]: return num_mo, nprim, num_atoms -def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> tuple[np.ndarray, np.ndarray]: +def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> tuple[NDArray, NDArray]: """Read the coordinates of the atoms.""" atnums = np.empty(num_atoms, int) atcoords = np.empty((num_atoms, 3), float) @@ -158,7 +159,7 @@ def _load_helper_atoms(lit: LineIterator, num_atoms: int) -> tuple[np.ndarray, n def _load_helper_section( lit: LineIterator, n: int, start: str, skip: int, step: int, dtype: np.dtype -) -> np.ndarray: +) -> NDArray: """Read CENTRE ASSIGNMENTS, TYPE ASSIGNMENTS, and EXPONENTS sections.""" section = [] while len(section) < n: @@ -174,7 +175,7 @@ def _load_helper_section( return np.array(section, dtype=dtype) -def _load_helper_mo(lit: LineIterator, nprim: int) -> tuple[int, float, float, np.ndarray]: +def _load_helper_mo(lit: LineIterator, nprim: int) -> tuple[int, float, float, NDArray]: """Read one section of MO information.""" line = next(lit) if not line.startswith("MO"): @@ -201,7 +202,7 @@ def _load_helper_energy(lit: LineIterator) -> float: return energy, virial -def _load_helper_multiwfn(lit: LineIterator, num_mo: int) -> np.ndarray: +def _load_helper_multiwfn(lit: LineIterator, num_mo: int) -> NDArray: """Read MO spin information from MULTIWFN extension.""" for line in lit: if "$MOSPIN $END" in line: @@ -258,8 +259,8 @@ def load_wfn_low(lit: LineIterator) -> tuple: def build_obasis( - icenters: np.ndarray, type_assignments: np.ndarray, exponents: np.ndarray, lit: LineIterator -) -> tuple[MolecularBasis, np.ndarray]: + icenters: NDArray, type_assignments: NDArray, exponents: NDArray, lit: LineIterator +) -> tuple[MolecularBasis, NDArray]: """Construct a basis set using the arrays read from a WFN or WFX file. Parameters @@ -351,7 +352,7 @@ def build_obasis( return obasis, permutation -def get_mocoeff_scales(obasis: MolecularBasis) -> np.ndarray: +def get_mocoeff_scales(obasis: MolecularBasis) -> NDArray: """Get the L2-normalization of the un-normalized Cartesian basis functions. Parameters @@ -461,7 +462,7 @@ def _format_helper_section(header: str, skip: int, spec: str, nline: int) -> tup 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): +def _dump_helper_section(f: TextIO, data: NDArray, fmt: str, skip: int, step: int, nline: int): """Write a CENTRE_ASSIGNMENTS, TYPE_ASSIGNMENTS, or EXPONENTS section to file ``f``.""" while len(data) > 0: chunk = data[:nline] diff --git a/iodata/inputs/common.py b/iodata/inputs/common.py index b95e39479..2955e9561 100644 --- a/iodata/inputs/common.py +++ b/iodata/inputs/common.py @@ -18,7 +18,7 @@ # -- """Utilities for writing input files.""" -import attr +import attrs import numpy as np from ..iodata import IOData @@ -30,7 +30,7 @@ def populate_fields(data: IOData) -> dict: """Generate a dictionary with fields to replace in the template.""" # load IOData dict using attr.asdict because the IOData class uses __slots__ - fields = attr.asdict(data, recurse=False) + fields = attrs.asdict(data, recurse=False) # store atomic coordinates in angstrom fields["atcoords"] = data.atcoords / angstrom # set general defaults diff --git a/iodata/iodata.py b/iodata/iodata.py index 3edabb6e5..5da8d1501 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -18,8 +18,9 @@ # -- """Module for handling input/output from different file formats.""" -import attr +import attrs import numpy as np +from numpy.typing import NDArray from .attrutils import convert_array_to, validate_shape from .basis import MolecularBasis @@ -29,7 +30,7 @@ __all__ = ["IOData"] -@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) +@attrs.define class IOData: """A container class for data loaded from (or to be written to) a file. @@ -172,78 +173,78 @@ class IOData: """ - atcharges: dict = attr.ib(factory=dict) - atcoords: np.ndarray = attr.ib( + atcharges: dict = attrs.field(factory=dict) + atcoords: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("natom", 3)), + validator=attrs.validators.optional(validate_shape("natom", 3)), ) - _atcorenums: np.ndarray = attr.ib( + _atcorenums: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("natom")), + validator=attrs.validators.optional(validate_shape("natom")), ) - atffparams: dict = attr.ib(factory=dict) - atfrozen: np.ndarray = attr.ib( + atffparams: dict = attrs.field(factory=dict) + atfrozen: NDArray | None = attrs.field( default=None, converter=convert_array_to(bool), - validator=attr.validators.optional(validate_shape("natom")), + validator=attrs.validators.optional(validate_shape("natom")), ) - atgradient: np.ndarray = attr.ib( + atgradient: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("natom", 3)), + validator=attrs.validators.optional(validate_shape("natom", 3)), ) - athessian: np.ndarray = attr.ib( + athessian: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, None)), + validator=attrs.validators.optional(validate_shape(None, None)), ) - atmasses: np.ndarray = attr.ib( + atmasses: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("natom")), + validator=attrs.validators.optional(validate_shape("natom")), ) - atnums: np.ndarray = attr.ib( + atnums: NDArray | None = attrs.field( default=None, converter=convert_array_to(int), - validator=attr.validators.optional(validate_shape("natom")), + validator=attrs.validators.optional(validate_shape("natom")), ) - basisdef: str = None - bonds: np.ndarray = attr.ib( + basisdef: str | None = attrs.field(default=None) + bonds: NDArray | None = attrs.field( default=None, converter=convert_array_to(int), - validator=attr.validators.optional(validate_shape(None, 3)), + validator=attrs.validators.optional(validate_shape(None, 3)), ) - cellvecs: np.ndarray = attr.ib( + cellvecs: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, 3)), + validator=attrs.validators.optional(validate_shape(None, 3)), ) - _charge: float = None - core_energy: float = None - cube: Cube = None - energy: float = None - extcharges: np.ndarray = attr.ib( + _charge: float | None = attrs.field(default=None) + core_energy: float | None = attrs.field(default=None) + cube: Cube | None = attrs.field(default=None) + energy: float | None = attrs.field(default=None) + extcharges: NDArray = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, 4)), + validator=attrs.validators.optional(validate_shape(None, 4)), ) - extra: dict = attr.ib(factory=dict) - g_rot: float = None - lot: str = None - mo: MolecularOrbitals = None - moments: dict = attr.ib(factory=dict) - _nelec: float = None - obasis: MolecularBasis = None - obasis_name: str = None - one_ints: dict = attr.ib(factory=dict) - one_rdms: dict = attr.ib(factory=dict) - run_type: str = None - _spinpol: float = None - title: str = None - two_ints: dict = attr.ib(factory=dict) - two_rdms: dict = attr.ib(factory=dict) + extra: dict = attrs.field(factory=dict) + g_rot: float | None = attrs.field(default=None) + lot: str | None = attrs.field(default=None) + mo: MolecularOrbitals | None = attrs.field(default=None) + moments: dict = attrs.field(factory=dict) + _nelec: float | None = attrs.field(default=None) + obasis: MolecularBasis | None = attrs.field(default=None) + obasis_name: str | None = attrs.field(default=None) + one_ints: dict = attrs.field(factory=dict) + one_rdms: dict = attrs.field(factory=dict) + run_type: str | None = attrs.field(default=None) + _spinpol: float | None = attrs.field(default=None) + title: str | None = attrs.field(default=None) + two_ints: dict = attrs.field(factory=dict) + two_rdms: dict = attrs.field(factory=dict) def __attrs_post_init__(self): # Trigger setter to acchieve consistency in properties @@ -262,7 +263,7 @@ def __attrs_post_init__(self): # Public interfaces to private attributes @property - def atcorenums(self) -> np.ndarray: + def atcorenums(self) -> NDArray: """Return effective core charges.""" if self._atcorenums is None and self.atnums is not None: self.atcorenums = self.atnums.astype(float) diff --git a/iodata/orbitals.py b/iodata/orbitals.py index bca69d26c..18adc6b5d 100644 --- a/iodata/orbitals.py +++ b/iodata/orbitals.py @@ -18,8 +18,9 @@ # -- """Data structure for molecular orbitals.""" -import attr +import attrs import numpy as np +from numpy.typing import NDArray from .attrutils import convert_array_to, validate_shape @@ -55,7 +56,7 @@ def validate_norbab(mo, attribute, value): 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]) +@attrs.define class MolecularOrbitals: """Class of Orthonormal Molecular Orbitals. @@ -92,28 +93,28 @@ class MolecularOrbitals: """ - kind: str = attr.ib( - validator=attr.validators.in_(["restricted", "unrestricted", "generalized"]) + kind: str = attrs.field( + validator=attrs.validators.in_(["restricted", "unrestricted", "generalized"]) ) - norba: int = attr.ib(validator=validate_norbab) - norbb: int = attr.ib(validator=validate_norbab) - occs: np.ndarray = attr.ib( + norba: int = attrs.field(validator=validate_norbab) + norbb: int = attrs.field(validator=validate_norbab) + occs: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("norb")), + validator=attrs.validators.optional(validate_shape("norb")), ) - coeffs: np.ndarray = attr.ib( + coeffs: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape(None, "norb")), + validator=attrs.validators.optional(validate_shape(None, "norb")), ) - energies: np.ndarray = attr.ib( + energies: NDArray | None = attrs.field( default=None, converter=convert_array_to(float), - validator=attr.validators.optional(validate_shape("norb")), + validator=attrs.validators.optional(validate_shape("norb")), ) - irreps: np.ndarray = attr.ib( - default=None, validator=attr.validators.optional(validate_shape("norb")) + irreps: NDArray | None = attrs.field( + default=None, validator=attrs.validators.optional(validate_shape("norb")) ) @property diff --git a/iodata/overlap.py b/iodata/overlap.py index b2671a97c..ea9a5fb5f 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -20,9 +20,10 @@ from typing import Optional -import attr +import attrs import numpy as np import scipy.special +from numpy.typing import NDArray from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS from .basis import MolecularBasis, Shell, convert_conventions, iter_cart_alphabet @@ -39,7 +40,7 @@ def factorial2(n, exact=False): Parameters ---------- - n : int or np.ndarray + n : int or NDArray Values to calculate n!! for. If n={0, -1}, the return value is 1. For n < -1, the return value is 0. """ @@ -53,10 +54,10 @@ def factorial2(n, exact=False): def compute_overlap( obasis0: MolecularBasis, - atcoords0: np.ndarray, + atcoords0: NDArray, obasis1: Optional[MolecularBasis] = None, - atcoords1: Optional[np.ndarray] = None, -) -> np.ndarray: + atcoords1: Optional[NDArray] = None, +) -> NDArray: r"""Compute overlap matrix for the given molecular basis set(s). .. math:: @@ -253,7 +254,7 @@ def compute_overlap_gaussian_1d(self, x1, x2, n1, n2, two_at): return value -def _compute_cart_shell_normalizations(shell: Shell) -> np.ndarray: +def _compute_cart_shell_normalizations(shell: Shell) -> NDArray: """Return normalization constants for the primitives in a given shell. Parameters @@ -263,12 +264,12 @@ def _compute_cart_shell_normalizations(shell: Shell) -> np.ndarray: Returns ------- - np.ndarray + NDArray The normalization constants, always for Cartesian functions, even when shell is pure. """ - shell = attr.evolve(shell, kinds=["c"] * shell.ncon) + shell = attrs.evolve(shell, kinds=["c"] * shell.ncon) result = [] for angmom in shell.angmoms: for exponent in shell.exponents: @@ -277,7 +278,7 @@ def _compute_cart_shell_normalizations(shell: Shell) -> np.ndarray: return np.array(result) -def gob_cart_normalization(alpha: np.ndarray, n: np.ndarray) -> np.ndarray: +def gob_cart_normalization(alpha: NDArray, n: NDArray) -> NDArray: """Compute normalization of exponent. Parameters @@ -289,7 +290,7 @@ def gob_cart_normalization(alpha: np.ndarray, n: np.ndarray) -> np.ndarray: Returns ------- - np.ndarray + NDArray The normalization constant for the gaussian cartesian basis. """ diff --git a/iodata/test/common.py b/iodata/test/common.py index 12b4f9f37..680804954 100644 --- a/iodata/test/common.py +++ b/iodata/test/common.py @@ -155,9 +155,9 @@ def check_orthonormal(mo_coeffs, ao_overlap, atol=1e-5): Parameters ---------- - mo_coeffs : np.ndarray, shape=(nbasis, mo_count) + mo_coeffs : NDArray, shape=(nbasis, mo_count) Molecular orbital coefficients. - ao_overlap : np.ndarray, shape=(nbasis, nbasis) + ao_overlap : NDArray, shape=(nbasis, nbasis) Atomic orbital overlap matrix. atol : float Absolute tolerance in deviation from identity matrix. diff --git a/iodata/test/test_attrutils.py b/iodata/test/test_attrutils.py index cdf5cdba2..d4e999356 100644 --- a/iodata/test/test_attrutils.py +++ b/iodata/test/test_attrutils.py @@ -18,19 +18,20 @@ # -- """Unit tests for iodata.attrutils.""" -import attr +import attrs import numpy as np import pytest from numpy.testing import assert_allclose +from numpy.typing import NDArray from ..attrutils import convert_array_to, validate_shape -@attr.s(auto_attribs=True, slots=True, on_setattr=attr.setters.convert) +@attrs.define class FooBar: """Just a silly class for testing convert_array_to.""" - spam: np.ndarray = attr.ib(converter=convert_array_to(float)) + spam: NDArray = attrs.field(converter=convert_array_to(float)) def test_convert_array_to_init(): @@ -59,22 +60,22 @@ def test_convert_array_to_assign(): assert fb.spam is None -@attr.s(auto_attribs=True, slots=True, on_setattr=attr.setters.validate) +@attrs.define class Spam: """Just a silly class for testing validate_shape.""" - egg0: np.ndarray = attr.ib(validator=validate_shape(1, None, None)) - egg1: np.ndarray = attr.ib(validator=validate_shape(("egg0", 2), ("egg2", 1))) - egg2: np.ndarray = attr.ib(validator=validate_shape(2, ("egg1", 1))) - egg3: np.ndarray = attr.ib(validator=validate_shape(("leg", 0))) - leg: str = attr.ib(validator=validate_shape(("egg3", 0))) + egg0: NDArray = attrs.field(validator=validate_shape(1, None, None)) + egg1: NDArray = attrs.field(validator=validate_shape(("egg0", 2), ("egg2", 1))) + egg2: NDArray = attrs.field(validator=validate_shape(2, ("egg1", 1))) + egg3: NDArray = attrs.field(validator=validate_shape(("leg", 0))) + leg: str = attrs.field(validator=validate_shape(("egg3", 0))) 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") # Double check - attr.validate(spam) + attrs.validate(spam) # Call constructor with invalid arguments with pytest.raises(TypeError): _ = Spam( @@ -131,7 +132,7 @@ 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") # Double check - attr.validate(spam) + attrs.validate(spam) # assign invalid attributes with pytest.raises(TypeError): spam.egg0 = np.zeros((2, 7, 4)) @@ -145,33 +146,33 @@ def test_validate_shape_assign(): spam.leg = "abcd" -@attr.s(slots=True) +@attrs.define class NoName0: """Test exception in validate_shape: unsupported item in shape_requirements.""" - xxx: str = attr.ib(validator=validate_shape(["asdfsa", 3])) + xxx: str = attrs.field(validator=validate_shape(["asdfsa", 3])) -@attr.s(slots=True) +@attrs.define class NoName1: """Test exception in validate_shape: unsupported item in shape_requirements.""" - xxx: str = attr.ib(validator=validate_shape(("asdfsa",))) + xxx: str = attrs.field(validator=validate_shape(("asdfsa",))) -@attr.s(slots=True) +@attrs.define class NoName2: """Test exception in validate_shape: other doest not exist.""" - xxx: str = attr.ib(validator=validate_shape("other")) + xxx: str = attrs.field(validator=validate_shape("other")) -@attr.s(slots=True) +@attrs.define class NoName3: """Test exception in validate_shape: other is not an array.""" - xxx: str = attr.ib(validator=validate_shape(("other", 1))) - other = attr.ib() + xxx: str = attrs.field(validator=validate_shape(("other", 1))) + other = attrs.field() def test_validate_shape_exceptions(): diff --git a/iodata/test/test_basis.py b/iodata/test/test_basis.py index 2fe6dc248..e076e77cd 100644 --- a/iodata/test/test_basis.py +++ b/iodata/test/test_basis.py @@ -18,7 +18,7 @@ # -- """Unit tests for iodata.obasis.""" -import attr +import attrs import numpy as np import pytest from numpy.testing import assert_equal @@ -121,7 +121,7 @@ def test_shell_validators(): # It should not raise a TypeError. shell = Shell(0, [0, 0], ["c", "c"], np.zeros(6), np.zeros((6, 2))) # Rerun the validators as a double check. - attr.validate(shell) + attrs.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))) diff --git a/iodata/test/test_json.py b/iodata/test/test_json.py index 5e70f3bee..4b205c20e 100644 --- a/iodata/test/test_json.py +++ b/iodata/test/test_json.py @@ -34,7 +34,7 @@ # Tests for qcschema_molecule -# GEOMS: dict of str: np.ndarray(N, 3) +# GEOMS: dict of str: NDArray(N, 3) GEOMS = { "LiCl": np.array([[0.000000, 0.000000, -1.631761], [0.000000, 0.000000, 0.287958]]), "OHr": np.array([[0.0, 0.0, -0.12947694], [0.0, -1.49418734, 1.02744651]]), diff --git a/iodata/test/test_molden.py b/iodata/test/test_molden.py index a4ffe8427..bab0e8df9 100644 --- a/iodata/test/test_molden.py +++ b/iodata/test/test_molden.py @@ -21,7 +21,7 @@ import os import warnings -import attr +import attrs import numpy as np import pytest from numpy.testing import assert_allclose, assert_equal @@ -557,7 +557,7 @@ def test_load_dump_consistency(tmpdir, fn, match): 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 = attrs.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_overlap.py b/iodata/test/test_overlap.py index 64a76fe66..e2ff0bc55 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -20,7 +20,7 @@ import itertools -import attr +import attrs import numpy as np import pytest from numpy.testing import assert_allclose @@ -78,7 +78,7 @@ def test_load_fchk_o2_cc_pvtz_cart_num(): ref = np.load(str(fn_npy)) 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) + obasis = attrs.evolve(data.obasis, conventions=OVERLAP_CONVENTIONS) assert_allclose(ref, compute_overlap(obasis, data.atcoords), rtol=1.0e-5, atol=1.0e-8) @@ -130,7 +130,7 @@ 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 + attrs.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) diff --git a/iodata/utils.py b/iodata/utils.py index 724abc06f..8cf238e85 100644 --- a/iodata/utils.py +++ b/iodata/utils.py @@ -20,9 +20,10 @@ import warnings -import attr +import attrs import numpy as np import scipy.constants as spc +from numpy.typing import NDArray from scipy.linalg import eigh from .attrutils import validate_shape @@ -119,7 +120,7 @@ def back(self, line): self.lineno -= 1 -@attr.s(auto_attribs=True, slots=True, on_setattr=[attr.setters.validate, attr.setters.convert]) +@attrs.define class Cube: """The volumetric data from a cube (or similar) file. @@ -136,9 +137,9 @@ class Cube: """ - origin: np.ndarray = attr.ib(validator=validate_shape(3)) - axes: np.ndarray = attr.ib(validator=validate_shape(3, 3)) - data: np.ndarray = attr.ib(validator=validate_shape(None, None, None)) + origin: NDArray = attrs.field(validator=validate_shape(3)) + axes: NDArray = attrs.field(validator=validate_shape(3, 3)) + data: NDArray = attrs.field(validator=validate_shape(None, None, None)) @property def shape(self): @@ -147,7 +148,7 @@ def shape(self): def set_four_index_element( - four_index_object: np.ndarray, i0: int, i1: int, i2: int, i3: int, value: float + four_index_object: NDArray, i0: int, i1: int, i2: int, i3: int, value: float ): """Assign values to a four index object, account for 8-fold index symmetry. @@ -174,7 +175,7 @@ def set_four_index_element( four_index_object[i3, i0, i1, i2] = value -def volume(cellvecs: np.ndarray) -> float: +def volume(cellvecs: NDArray) -> float: """Calculate the (generalized) cell volume. Parameters @@ -200,7 +201,7 @@ def volume(cellvecs: np.ndarray) -> float: raise ValueError("Argument cellvecs should be of shape (x, 3), where x is in {1, 2, 3}") -def derive_naturals(dm: np.ndarray, overlap: np.ndarray) -> tuple[np.ndarray, np.ndarray]: +def derive_naturals(dm: NDArray, overlap: NDArray) -> tuple[NDArray, NDArray]: """Derive natural orbitals from a given density matrix. Parameters @@ -232,7 +233,7 @@ def derive_naturals(dm: np.ndarray, overlap: np.ndarray) -> tuple[np.ndarray, np return coeffs, occs -def check_dm(dm: np.ndarray, overlap: np.ndarray, eps: float = 1e-4, occ_max: float = 1.0): +def check_dm(dm: NDArray, overlap: NDArray, eps: float = 1e-4, occ_max: float = 1.0): """Check if the density matrix has eigenvalues in the proper range. Parameters From f569d369ccb0ac6e37a5dceb50c16a4252be2083 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Wed, 29 May 2024 17:45:34 +0200 Subject: [PATCH 119/144] Compatibility with Py39 --- iodata/iodata.py | 48 ++++++++++++++++++++++++---------------------- iodata/orbitals.py | 10 ++++++---- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/iodata/iodata.py b/iodata/iodata.py index 5da8d1501..4a89d03c6 100644 --- a/iodata/iodata.py +++ b/iodata/iodata.py @@ -18,6 +18,8 @@ # -- """Module for handling input/output from different file formats.""" +from typing import Optional + import attrs import numpy as np from numpy.typing import NDArray @@ -174,75 +176,75 @@ class IOData: """ atcharges: dict = attrs.field(factory=dict) - atcoords: NDArray | None = attrs.field( + atcoords: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("natom", 3)), ) - _atcorenums: NDArray | None = attrs.field( + _atcorenums: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("natom")), ) atffparams: dict = attrs.field(factory=dict) - atfrozen: NDArray | None = attrs.field( + atfrozen: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(bool), validator=attrs.validators.optional(validate_shape("natom")), ) - atgradient: NDArray | None = attrs.field( + atgradient: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("natom", 3)), ) - athessian: NDArray | None = attrs.field( + athessian: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape(None, None)), ) - atmasses: NDArray | None = attrs.field( + atmasses: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("natom")), ) - atnums: NDArray | None = attrs.field( + atnums: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(int), validator=attrs.validators.optional(validate_shape("natom")), ) - basisdef: str | None = attrs.field(default=None) - bonds: NDArray | None = attrs.field( + basisdef: Optional[str] = attrs.field(default=None) + bonds: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(int), validator=attrs.validators.optional(validate_shape(None, 3)), ) - cellvecs: NDArray | None = attrs.field( + cellvecs: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape(None, 3)), ) - _charge: float | None = attrs.field(default=None) - core_energy: float | None = attrs.field(default=None) - cube: Cube | None = attrs.field(default=None) - energy: float | None = attrs.field(default=None) + _charge: Optional[float] = attrs.field(default=None) + core_energy: Optional[float] = attrs.field(default=None) + cube: Optional[Cube] = attrs.field(default=None) + energy: Optional[float] = attrs.field(default=None) extcharges: NDArray = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape(None, 4)), ) extra: dict = attrs.field(factory=dict) - g_rot: float | None = attrs.field(default=None) - lot: str | None = attrs.field(default=None) - mo: MolecularOrbitals | None = attrs.field(default=None) + g_rot: Optional[float] = attrs.field(default=None) + lot: Optional[str] = attrs.field(default=None) + mo: Optional[MolecularOrbitals] = attrs.field(default=None) moments: dict = attrs.field(factory=dict) - _nelec: float | None = attrs.field(default=None) - obasis: MolecularBasis | None = attrs.field(default=None) - obasis_name: str | None = attrs.field(default=None) + _nelec: Optional[float] = attrs.field(default=None) + obasis: Optional[MolecularBasis] = attrs.field(default=None) + obasis_name: Optional[str] = attrs.field(default=None) one_ints: dict = attrs.field(factory=dict) one_rdms: dict = attrs.field(factory=dict) - run_type: str | None = attrs.field(default=None) - _spinpol: float | None = attrs.field(default=None) - title: str | None = attrs.field(default=None) + run_type: Optional[str] = attrs.field(default=None) + _spinpol: Optional[float] = attrs.field(default=None) + title: Optional[str] = attrs.field(default=None) two_ints: dict = attrs.field(factory=dict) two_rdms: dict = attrs.field(factory=dict) diff --git a/iodata/orbitals.py b/iodata/orbitals.py index 18adc6b5d..9a862370c 100644 --- a/iodata/orbitals.py +++ b/iodata/orbitals.py @@ -18,6 +18,8 @@ # -- """Data structure for molecular orbitals.""" +from typing import Optional + import attrs import numpy as np from numpy.typing import NDArray @@ -98,22 +100,22 @@ class MolecularOrbitals: ) norba: int = attrs.field(validator=validate_norbab) norbb: int = attrs.field(validator=validate_norbab) - occs: NDArray | None = attrs.field( + occs: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("norb")), ) - coeffs: NDArray | None = attrs.field( + coeffs: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape(None, "norb")), ) - energies: NDArray | None = attrs.field( + energies: Optional[NDArray] = attrs.field( default=None, converter=convert_array_to(float), validator=attrs.validators.optional(validate_shape("norb")), ) - irreps: NDArray | None = attrs.field( + irreps: Optional[NDArray] = attrs.field( default=None, validator=attrs.validators.optional(validate_shape("norb")) ) From 0a48fc605be6ca8231604447749624829fcb749d Mon Sep 17 00:00:00 2001 From: D-TheProgrammer <151149998+D-TheProgrammer@users.noreply.github.com> Date: Thu, 30 May 2024 19:13:28 +0200 Subject: [PATCH 120/144] New Fcatorial2 respecting linters --- iodata/overlap.py | 63 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index b2671a97c..351ed61c8 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -25,32 +25,56 @@ import scipy.special from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS -from .basis import MolecularBasis, Shell, convert_conventions, iter_cart_alphabet +from .basis import MolecularBasis, convert_conventions, iter_cart_alphabet from .overlap_cartpure import tfs __all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] def factorial2(n, exact=False): - """Wrap scipy.special.factorial2 to return 1.0 when the input is -1. + """Wrap scipy.special.factorial2 to return 1.0 when the input is -1 and handle float arrays. This is a temporary workaround while we wait for Scipy's update. To learn more, see https://github.com/scipy/scipy/issues/18409. Parameters ---------- - n : int or np.ndarray + n : int, float, or np.ndarray Values to calculate n!! for. If n={0, -1}, the return value is 1. For n < -1, the return value is 0. """ - # Scipy 1.11.x returns an integer when n is an integer, but 1.10.x returns an array, - # so np.array(n) is passed to make sure the output is always an array. - out = scipy.special.factorial2(np.array(n), exact=exact) - out[out <= 0] = 1.0 - out[out <= -2] = 0.0 - return out - - + # Handle integer inputs + if isinstance(n, (int, np.integer)): + if n in {-1, 0}: + return 1.0 + if n < -1: + return 0.0 + return scipy.special.factorial2(n, exact=exact) + + # Handle float inputs + if isinstance(n, float): + if n in {-1.0, 0.0}: + return 1.0 + if n < -1.0: + return 0.0 + return scipy.special.factorial2(int(n), exact=exact) + + # Handle array inputs + if isinstance(n, np.ndarray): + result = np.zeros_like(n, dtype=float) + for i, val in np.ndenumerate(n): + if val in {-1.0, 0.0}: + result[i] = 1.0 + elif val < -1.0: + result[i] = 0.0 + else: + result[i] = scipy.special.factorial2(int(val), exact=exact) + return result + + return None + + +# pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches def compute_overlap( obasis0: MolecularBasis, atcoords0: np.ndarray, @@ -131,7 +155,7 @@ def compute_overlap( n_max = max(np.max(shell.angmoms) for shell in obasis0.shells) if not identical: - n_max = max(n_max, *(np.max(shell.angmoms) for shell in obasis1.shells)) + n_max = max(n_max, max(np.max(shell.angmoms) for shell in obasis1.shells)) go = GaussianOverlap(n_max) # define a python ufunc (numpy function) for broadcasted calling over angular momentums @@ -140,13 +164,17 @@ def compute_overlap( # Loop over shell0 begin0 = 0 + # pylint: disable=too-many-nested-blocks for i0, shell0 in enumerate(obasis0.shells): r0 = atcoords0[shell0.icenter] end0 = begin0 + shell0.nbasis # Loop over shell1 (lower triangular only, including diagonal) begin1 = 0 - nshell1 = i0 + 1 if identical else len(obasis1.shells) + if identical: + nshell1 = i0 + 1 + else: + nshell1 = len(obasis1.shells) for i1, shell1 in enumerate(obasis1.shells[:nshell1]): r1 = atcoords1[shell1.icenter] end1 = begin1 + shell1.nbasis @@ -218,7 +246,8 @@ def compute_overlap( permutation1, signs1 = permutation0, signs0 else: permutation1, signs1 = convert_conventions(obasis1, OVERLAP_CONVENTIONS, reverse=True) - return overlap[:, permutation1] * signs1 + overlap = overlap[:, permutation1] * signs1 + return overlap class GaussianOverlap: @@ -253,7 +282,7 @@ def compute_overlap_gaussian_1d(self, x1, x2, n1, n2, two_at): 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 @@ -272,7 +301,9 @@ def _compute_cart_shell_normalizations(shell: Shell) -> np.ndarray: result = [] for angmom in shell.angmoms: for exponent in shell.exponents: - row = [gob_cart_normalization(exponent, n) for n in iter_cart_alphabet(angmom)] + row = [] + for n in iter_cart_alphabet(angmom): + row.append(gob_cart_normalization(exponent, n)) result.append(row) return np.array(result) From 58b65594086cf9c4cf59a7ea68ca980aae0a50a5 Mon Sep 17 00:00:00 2001 From: D-TheProgrammer <151149998+D-TheProgrammer@users.noreply.github.com> Date: Thu, 30 May 2024 19:14:50 +0200 Subject: [PATCH 121/144] Pytest for factorial2 --- iodata/test/test_factorial2.py | 42 ++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 iodata/test/test_factorial2.py diff --git a/iodata/test/test_factorial2.py b/iodata/test/test_factorial2.py new file mode 100644 index 000000000..e790b494b --- /dev/null +++ b/iodata/test/test_factorial2.py @@ -0,0 +1,42 @@ +import numpy as np +import pytest + +from ..overlap import factorial2 + + +def test_integer_arguments(): + assert factorial2(0, exact=True) == 1 + assert factorial2(1, exact=True) == 1 + assert factorial2(2, exact=True) == 2 + assert factorial2(3, exact=True) == 3 + assert factorial2(-1, exact=True) == 1 + assert factorial2(-2, exact=True) == 0 + + +def test_float_arguments(): + assert factorial2(0.0, exact=False) == pytest.approx(1.0) + assert factorial2(1.0, exact=False) == pytest.approx(1.0) + assert factorial2(2.0, exact=False) == pytest.approx(2.0) + assert factorial2(3.0, exact=False) == pytest.approx(3.0) + + +def test_integer_array_argument(): + np.testing.assert_array_equal( + factorial2(np.array([0, 1, 2, 3]), exact=True), np.array([1, 1, 2, 3]) + ) + + +def test_float_array_argument(): + np.testing.assert_array_almost_equal( + factorial2(np.array([0.0, 1.0, 2.0, 3.0]), exact=False), np.array([1.0, 1.0, 2.0, 3.0]) + ) + + +def test_special_cases_exact(): + assert factorial2(-1, exact=True) == pytest.approx(1.0) + assert factorial2(-2, exact=True) == pytest.approx(0.0) + + +def test_special_cases_not_exact(): + assert factorial2(-1.0, exact=False) == pytest.approx(1.0) + assert factorial2(-2.0, exact=False) == pytest.approx(0.0) From 0d639a781cb1a94edc962de7c2fc732a001833f2 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 10:42:36 +0200 Subject: [PATCH 122/144] Fix Ruff issues --- iodata/overlap.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 351ed61c8..8e1bf199a 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -25,7 +25,7 @@ import scipy.special from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS -from .basis import MolecularBasis, convert_conventions, iter_cart_alphabet +from .basis import MolecularBasis, Shell, convert_conventions, iter_cart_alphabet from .overlap_cartpure import tfs __all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] @@ -155,7 +155,7 @@ def compute_overlap( n_max = max(np.max(shell.angmoms) for shell in obasis0.shells) if not identical: - n_max = max(n_max, max(np.max(shell.angmoms) for shell in obasis1.shells)) + n_max = max(n_max, *(np.max(shell.angmoms) for shell in obasis1.shells)) go = GaussianOverlap(n_max) # define a python ufunc (numpy function) for broadcasted calling over angular momentums @@ -171,10 +171,7 @@ def compute_overlap( # Loop over shell1 (lower triangular only, including diagonal) begin1 = 0 - if identical: - nshell1 = i0 + 1 - else: - nshell1 = len(obasis1.shells) + nshell1 = i0 + 1 if identical else len(obasis1.shells) for i1, shell1 in enumerate(obasis1.shells[:nshell1]): r1 = atcoords1[shell1.icenter] end1 = begin1 + shell1.nbasis @@ -246,8 +243,7 @@ def compute_overlap( permutation1, signs1 = permutation0, signs0 else: permutation1, signs1 = convert_conventions(obasis1, OVERLAP_CONVENTIONS, reverse=True) - overlap = overlap[:, permutation1] * signs1 - return overlap + return overlap[:, permutation1] * signs1 class GaussianOverlap: @@ -301,9 +297,7 @@ def _compute_cart_shell_normalizations(shell: "Shell") -> np.ndarray: result = [] for angmom in shell.angmoms: for exponent in shell.exponents: - row = [] - for n in iter_cart_alphabet(angmom): - row.append(gob_cart_normalization(exponent, n)) + row = [gob_cart_normalization(exponent, n) for n in iter_cart_alphabet(angmom)] result.append(row) return np.array(result) From 2f8d75241e45e7f1ab42d26c715f0134abe95c3d Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 10:44:34 +0200 Subject: [PATCH 123/144] Fix conditionals: avoid sets --- iodata/overlap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 8e1bf199a..29a4b3864 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -45,7 +45,7 @@ def factorial2(n, exact=False): """ # Handle integer inputs if isinstance(n, (int, np.integer)): - if n in {-1, 0}: + if n == -1: return 1.0 if n < -1: return 0.0 @@ -53,7 +53,7 @@ def factorial2(n, exact=False): # Handle float inputs if isinstance(n, float): - if n in {-1.0, 0.0}: + if n == -1.0: return 1.0 if n < -1.0: return 0.0 @@ -63,7 +63,7 @@ def factorial2(n, exact=False): if isinstance(n, np.ndarray): result = np.zeros_like(n, dtype=float) for i, val in np.ndenumerate(n): - if val in {-1.0, 0.0}: + if val == -1.0: result[i] = 1.0 elif val < -1.0: result[i] = 0.0 From 10e343cdcad52c66a27a196bd7d2fea1e1ef4445 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:11:50 +0200 Subject: [PATCH 124/144] Restrict factorial2 to integer and exact use case --- iodata/overlap.py | 55 ++++++++++++++++------------------ iodata/test/test_factorial2.py | 36 ++++++++-------------- 2 files changed, 38 insertions(+), 53 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 29a4b3864..8f1de7355 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -18,11 +18,12 @@ # -- """Module for computing overlap of atomic orbital basis functions.""" -from typing import Optional +from typing import Optional, Union import attr import numpy as np import scipy.special +from numpy.typing import NDArray from .basis import HORTON2_CONVENTIONS as OVERLAP_CONVENTIONS from .basis import MolecularBasis, Shell, convert_conventions, iter_cart_alphabet @@ -31,47 +32,41 @@ __all__ = ["OVERLAP_CONVENTIONS", "compute_overlap", "gob_cart_normalization"] -def factorial2(n, exact=False): - """Wrap scipy.special.factorial2 to return 1.0 when the input is -1 and handle float arrays. +def factorial2(n: Union[int, NDArray[int]]) -> Union[int, NDArray[int]]: + """Modifcied scipy.special.factorial2 to return 1 when the input is -1. This is a temporary workaround while we wait for Scipy's update. To learn more, see https://github.com/scipy/scipy/issues/18409. + This function only supports integer (array) arguments, + because this is the only relevant use case in IOData. + Parameters ---------- - n : int, float, or np.ndarray - Values to calculate n!! for. If n={0, -1}, the return value is 1. + n + Values to calculate n!! for. If n==-1, the return value is 1. For n < -1, the return value is 0. + """ # Handle integer inputs if isinstance(n, (int, np.integer)): if n == -1: - return 1.0 - if n < -1: - return 0.0 - return scipy.special.factorial2(n, exact=exact) - - # Handle float inputs - if isinstance(n, float): - if n == -1.0: - return 1.0 - if n < -1.0: - return 0.0 - return scipy.special.factorial2(int(n), exact=exact) - - # Handle array inputs + return 1 + return scipy.special.factorial2(n, exact=True) + + # Handle integer array inputs if isinstance(n, np.ndarray): - result = np.zeros_like(n, dtype=float) - for i, val in np.ndenumerate(n): - if val == -1.0: - result[i] = 1.0 - elif val < -1.0: - result[i] = 0.0 - else: - result[i] = scipy.special.factorial2(int(val), exact=exact) - return result + if issubclass(n.dtype.type, (int, np.integer)): + result = np.zeros_like(n) + for i, val in np.ndenumerate(n): + if val == -1: + result[i] = 1 + else: + result[i] = scipy.special.factorial2(val, exact=True) + return result + raise TypeError(f"Unsupported dtype of array n: {n.dtype}") - return None + raise TypeError(f"Unsupported type of argument n: {type(n)}") # pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches @@ -261,7 +256,7 @@ def __init__(self, n_max): self.binomials = [ [scipy.special.binom(n, i) for i in range(n + 1)] for n in range(n_max + 1) ] - facts = [factorial2(m, 2) for m in range(2 * n_max)] + facts = [factorial2(m) for m in range(2 * n_max)] facts.insert(0, 1) self.facts = np.array(facts) diff --git a/iodata/test/test_factorial2.py b/iodata/test/test_factorial2.py index e790b494b..cf543d75c 100644 --- a/iodata/test/test_factorial2.py +++ b/iodata/test/test_factorial2.py @@ -5,38 +5,28 @@ def test_integer_arguments(): - assert factorial2(0, exact=True) == 1 - assert factorial2(1, exact=True) == 1 - assert factorial2(2, exact=True) == 2 - assert factorial2(3, exact=True) == 3 - assert factorial2(-1, exact=True) == 1 - assert factorial2(-2, exact=True) == 0 + assert factorial2(0) == 1 + assert factorial2(1) == 1 + assert factorial2(2) == 2 + assert factorial2(3) == 3 + assert factorial2(-1) == 1 + assert factorial2(-2) == 0 def test_float_arguments(): - assert factorial2(0.0, exact=False) == pytest.approx(1.0) - assert factorial2(1.0, exact=False) == pytest.approx(1.0) - assert factorial2(2.0, exact=False) == pytest.approx(2.0) - assert factorial2(3.0, exact=False) == pytest.approx(3.0) + with pytest.raises(TypeError): + assert factorial2(1.0) def test_integer_array_argument(): - np.testing.assert_array_equal( - factorial2(np.array([0, 1, 2, 3]), exact=True), np.array([1, 1, 2, 3]) - ) + assert (factorial2(np.array([0, 1, 2, 3])) == np.array([1, 1, 2, 3])).all() def test_float_array_argument(): - np.testing.assert_array_almost_equal( - factorial2(np.array([0.0, 1.0, 2.0, 3.0]), exact=False), np.array([1.0, 1.0, 2.0, 3.0]) - ) + with pytest.raises(TypeError): + factorial2(np.array([0.0, 1.0, 2.0, 3.0])) def test_special_cases_exact(): - assert factorial2(-1, exact=True) == pytest.approx(1.0) - assert factorial2(-2, exact=True) == pytest.approx(0.0) - - -def test_special_cases_not_exact(): - assert factorial2(-1.0, exact=False) == pytest.approx(1.0) - assert factorial2(-2.0, exact=False) == pytest.approx(0.0) + assert factorial2(-1) == pytest.approx(1) + assert factorial2(-2) == pytest.approx(0) From 0a33bf77d1b90abde25b4f0dabe5d1204073ebe0 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:12:45 +0200 Subject: [PATCH 125/144] Move factorial2 tests to test_overlap.py --- iodata/test/test_factorial2.py | 32 -------------------------------- iodata/test/test_overlap.py | 30 +++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 33 deletions(-) delete mode 100644 iodata/test/test_factorial2.py diff --git a/iodata/test/test_factorial2.py b/iodata/test/test_factorial2.py deleted file mode 100644 index cf543d75c..000000000 --- a/iodata/test/test_factorial2.py +++ /dev/null @@ -1,32 +0,0 @@ -import numpy as np -import pytest - -from ..overlap import factorial2 - - -def test_integer_arguments(): - assert factorial2(0) == 1 - assert factorial2(1) == 1 - assert factorial2(2) == 2 - assert factorial2(3) == 3 - assert factorial2(-1) == 1 - assert factorial2(-2) == 0 - - -def test_float_arguments(): - with pytest.raises(TypeError): - assert factorial2(1.0) - - -def test_integer_array_argument(): - assert (factorial2(np.array([0, 1, 2, 3])) == np.array([1, 1, 2, 3])).all() - - -def test_float_array_argument(): - with pytest.raises(TypeError): - factorial2(np.array([0.0, 1.0, 2.0, 3.0])) - - -def test_special_cases_exact(): - assert factorial2(-1) == pytest.approx(1) - assert factorial2(-2) == pytest.approx(0) diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index 64a76fe66..d775dd2ea 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -27,7 +27,7 @@ from ..api import load_one from ..basis import MolecularBasis, Shell, convert_conventions -from ..overlap import OVERLAP_CONVENTIONS, compute_overlap +from ..overlap import OVERLAP_CONVENTIONS, compute_overlap, factorial2 try: from importlib_resources import as_file, files @@ -35,6 +35,34 @@ from importlib.resources import as_file, files +def test_integer_arguments(): + assert factorial2(0) == 1 + assert factorial2(1) == 1 + assert factorial2(2) == 2 + assert factorial2(3) == 3 + assert factorial2(-1) == 1 + assert factorial2(-2) == 0 + + +def test_float_arguments(): + with pytest.raises(TypeError): + assert factorial2(1.0) + + +def test_integer_array_argument(): + assert (factorial2(np.array([0, 1, 2, 3])) == np.array([1, 1, 2, 3])).all() + + +def test_float_array_argument(): + with pytest.raises(TypeError): + factorial2(np.array([0.0, 1.0, 2.0, 3.0])) + + +def test_special_cases_exact(): + assert factorial2(-1) == pytest.approx(1) + assert factorial2(-2) == pytest.approx(0) + + def test_normalization_basics_segmented(): for angmom in range(7): shells = [Shell(0, [angmom], ["c"], np.array([0.23]), np.array([[1.0]]))] From 128edbba791152e6d145d2988db2008e44116658 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:14:20 +0200 Subject: [PATCH 126/144] Fix type test factorial2 --- iodata/test/test_overlap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index d775dd2ea..2f68a0dce 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -46,7 +46,7 @@ def test_integer_arguments(): def test_float_arguments(): with pytest.raises(TypeError): - assert factorial2(1.0) + factorial2(1.0) def test_integer_array_argument(): From ccb1d92c3311c9ac70683cd5f2df14b318f603c4 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:16:26 +0200 Subject: [PATCH 127/144] Add deepsource config --- .deepsource.toml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .deepsource.toml diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 000000000..fba5f6003 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,11 @@ +version = 1 + +[[analyzers]] +name = "shell" + +[[analyzers]] +name = "python" + + [analyzers.meta] + runtime_version = "3.x.x" + max_line_length = 100 From 99d332e710bdf7ed217f3dbede02933aff5ade47 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:19:35 +0200 Subject: [PATCH 128/144] Remove pylint comments --- iodata/overlap.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 8f1de7355..6e143de5c 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -69,7 +69,6 @@ def factorial2(n: Union[int, NDArray[int]]) -> Union[int, NDArray[int]]: raise TypeError(f"Unsupported type of argument n: {type(n)}") -# pylint: disable=too-many-nested-blocks,too-many-statements,too-many-branches def compute_overlap( obasis0: MolecularBasis, atcoords0: np.ndarray, @@ -159,7 +158,6 @@ def compute_overlap( # Loop over shell0 begin0 = 0 - # pylint: disable=too-many-nested-blocks for i0, shell0 in enumerate(obasis0.shells): r0 = atcoords0[shell0.icenter] end0 = begin0 + shell0.nbasis From b657183dc3c872d904d79a3ae3ace93de1570e2e Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:20:54 +0200 Subject: [PATCH 129/144] Improve docstring --- iodata/overlap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 6e143de5c..0ccb1350f 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -33,7 +33,7 @@ def factorial2(n: Union[int, NDArray[int]]) -> Union[int, NDArray[int]]: - """Modifcied scipy.special.factorial2 to return 1 when the input is -1. + """Modifcied scipy.special.factorial2 that returns 1 when the input is -1. This is a temporary workaround while we wait for Scipy's update. To learn more, see https://github.com/scipy/scipy/issues/18409. From 5b8ec3b95eceee08e2639ef33d3b76844936f010 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:21:21 +0200 Subject: [PATCH 130/144] Remove redundant quotes --- iodata/overlap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index 0ccb1350f..c9fc68e89 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -271,7 +271,7 @@ def compute_overlap_gaussian_1d(self, x1, x2, n1, n2, two_at): 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 From f539ff7a89ab124941430dacd6a92479a93c91eb Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 11:32:24 +0200 Subject: [PATCH 131/144] Reduce complexity and vectorize --- iodata/overlap.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/iodata/overlap.py b/iodata/overlap.py index c9fc68e89..85a9d8af1 100644 --- a/iodata/overlap.py +++ b/iodata/overlap.py @@ -50,19 +50,13 @@ def factorial2(n: Union[int, NDArray[int]]) -> Union[int, NDArray[int]]: """ # Handle integer inputs if isinstance(n, (int, np.integer)): - if n == -1: - return 1 - return scipy.special.factorial2(n, exact=True) + return 1 if n == -1 else scipy.special.factorial2(n, exact=True) # Handle integer array inputs if isinstance(n, np.ndarray): if issubclass(n.dtype.type, (int, np.integer)): - result = np.zeros_like(n) - for i, val in np.ndenumerate(n): - if val == -1: - result[i] = 1 - else: - result[i] = scipy.special.factorial2(val, exact=True) + result = scipy.special.factorial2(n, exact=True) + result[n == -1] = 1 return result raise TypeError(f"Unsupported dtype of array n: {n.dtype}") From d40b54380588757ee97a89dba4456677fd75954f Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 19:31:23 +0200 Subject: [PATCH 132/144] Factorial2 test improvements - Include type checks - Include other arrays - Include more cases --- iodata/test/test_overlap.py | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/iodata/test/test_overlap.py b/iodata/test/test_overlap.py index 2f68a0dce..9a2e5bc43 100644 --- a/iodata/test/test_overlap.py +++ b/iodata/test/test_overlap.py @@ -35,34 +35,30 @@ from importlib.resources import as_file, files -def test_integer_arguments(): - assert factorial2(0) == 1 - assert factorial2(1) == 1 - assert factorial2(2) == 2 - assert factorial2(3) == 3 - assert factorial2(-1) == 1 - assert factorial2(-2) == 0 +@pytest.mark.parametrize( + ("inp", "out"), [(0, 1), (1, 1), (2, 2), (3, 3), (4, 8), (5, 15), (-1, 1), (-2, 0)] +) +def test_factorial2_integer_arguments(inp, out): + assert factorial2(inp) == out + assert isinstance(factorial2(inp), int) -def test_float_arguments(): +def test_factorial2_float_arguments(): with pytest.raises(TypeError): factorial2(1.0) -def test_integer_array_argument(): - assert (factorial2(np.array([0, 1, 2, 3])) == np.array([1, 1, 2, 3])).all() +def test_factorial2_integer_array_argument(): + assert (factorial2(np.array([-2, -1, 4, 5])) == np.array([0, 1, 8, 15])).all() + assert (factorial2(np.array([[-2, -1], [4, 5]])) == np.array([[0, 1], [8, 15]])).all() + assert issubclass(factorial2(np.array([-2, -1, 4, 5])).dtype.type, np.integer) -def test_float_array_argument(): +def test_factorial2_float_array_argument(): with pytest.raises(TypeError): factorial2(np.array([0.0, 1.0, 2.0, 3.0])) -def test_special_cases_exact(): - assert factorial2(-1) == pytest.approx(1) - assert factorial2(-2) == pytest.approx(0) - - def test_normalization_basics_segmented(): for angmom in range(7): shells = [Shell(0, [angmom], ["c"], np.array([0.23]), np.array([[1.0]]))] From 264e4e313537b7a79681a6de24a31fff0ffb92e3 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 19:34:22 +0200 Subject: [PATCH 133/144] Remove conda recipe. If we'd ever be interested in conda releases, a package on conda-forge would be more of use. --- tools/conda.recipe/meta.yaml | 43 ------------------------------------ 1 file changed, 43 deletions(-) delete mode 100644 tools/conda.recipe/meta.yaml diff --git a/tools/conda.recipe/meta.yaml b/tools/conda.recipe/meta.yaml deleted file mode 100644 index ec4db6ea3..000000000 --- a/tools/conda.recipe/meta.yaml +++ /dev/null @@ -1,43 +0,0 @@ -package: - version: "{{ PROJECT_VERSION }}" - name: 'qc-iodata' - -source: - path: ../../ - -build: - number: 0 - noarch: python - script: "{{ PYTHON }} -m pip install . --no-deps" - entry_points: - - iodata-convert = iodata.__main__:main - -requirements: - host: - - python - - numpy >=1.0 - - setuptools - run: - - python - - scipy - - attrs >=20.1.0 - - importlib_resources # [py<37] - -test: - requires: - - python - - pytest - - pytest-xdist - imports: - - iodata - commands: - - pytest --pyargs iodata -v -n auto - -about: - description: Input and output module for quantum chemistry - home: https://iodata.readthedocs.io/en/latest - doc_url: https://iodata.readthedocs.io/en/latest - dev_url: https://github.com/theochem/iodata - license: GNU Version 3 - license_family: GPL - license_file: LICENSE From aa53c47c212c045b8169afd5659b0db0450d6b75 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 19:51:31 +0200 Subject: [PATCH 134/144] More type hinting Not crucial, but does not hurt. This is meant to trigger deepsource analysis --- iodata/formats/gamess.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/iodata/formats/gamess.py b/iodata/formats/gamess.py index 790ef5e44..608bc9160 100644 --- a/iodata/formats/gamess.py +++ b/iodata/formats/gamess.py @@ -30,7 +30,7 @@ PATTERNS = ["*.dat"] -def _read_data(lit: LineIterator) -> tuple: +def _read_data(lit: LineIterator) -> tuple[str, str, list[str]]: """Extract ``title``, ``symmetry`` and ``symbols`` from the punch file.""" title = next(lit).strip() symmetry = next(lit).split()[0] @@ -47,7 +47,7 @@ def _read_data(lit: LineIterator) -> tuple: return title, symmetry, symbols -def _read_coordinates(lit: LineIterator, result: dict) -> tuple: +def _read_coordinates(lit: LineIterator, result: dict[str]) -> tuple[NDArray, NDArray]: """Extract ``numbers`` and ``coordinates`` from the punch file.""" for _ in range(2): next(lit) @@ -68,7 +68,7 @@ def _read_coordinates(lit: LineIterator, result: dict) -> tuple: return numbers, coordinates -def _read_energy(lit: LineIterator, result: dict) -> tuple: +def _read_energy(lit: LineIterator, result: dict[str]) -> tuple[float, NDArray]: """Extract ``energy`` and ``gradient`` from the punch file.""" energy = float(next(lit).split()[1]) natom = len(result["symbols"]) @@ -82,7 +82,7 @@ def _read_energy(lit: LineIterator, result: dict) -> tuple: return energy, gradient -def _read_hessian(lit: LineIterator, result: dict) -> NDArray: +def _read_hessian(lit: LineIterator, result: dict[str]) -> NDArray: """Extract ``hessian`` from the punch file.""" # check that $HESS is not already parsed if "athessian" in result: @@ -103,7 +103,7 @@ def _read_hessian(lit: LineIterator, result: dict) -> NDArray: return hessian -def _read_masses(lit: LineIterator, result: dict) -> NDArray: +def _read_masses(lit: LineIterator, result: dict[str]) -> NDArray: """Extract ``masses`` from the punch file.""" natom = len(result["symbols"]) masses = np.zeros(natom, float) @@ -120,7 +120,7 @@ def _read_masses(lit: LineIterator, result: dict) -> NDArray: "PUNCH", ["title", "energy", "grot", "atgradient", "athessian", "atmasses", "atnums", "atcoords"], ) -def load_one(lit: LineIterator) -> dict: +def load_one(lit: LineIterator) -> dict[str]: """Do not edit this docstring. It will be overwritten.""" result = {} while True: From ddd9332d07ef57ad3e74fff33692296953cb94f5 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 19:53:51 +0200 Subject: [PATCH 135/144] Use sys.executable instead of "python". This is a silly PR meant to test some of the newly activated code analysis tools. Despite being minor, it still is useful --- iodata/test/test_cli.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/iodata/test/test_cli.py b/iodata/test/test_cli.py index b647561ba..110bef614 100644 --- a/iodata/test/test_cli.py +++ b/iodata/test/test_cli.py @@ -21,6 +21,7 @@ import functools import os import subprocess +import sys from numpy.testing import assert_allclose, assert_equal @@ -55,7 +56,7 @@ def test_convert_one_manfmt(tmpdir): def test_script_one_autofmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(["python", "-m", "iodata.__main__", infn, outfn], check=True) + subprocess.run([sys.executable, "-m", "iodata.__main__", infn, outfn], check=True) _check_convert_one(myconvert, tmpdir) @@ -63,7 +64,8 @@ def myconvert(infn, outfn): def test_script_one_manfmt(tmpdir): def myconvert(infn, outfn): subprocess.run( - ["python", "-m", "iodata.__main__", infn, outfn, "-i", "fchk", "-o", "xyz"], check=True + [sys.executable, "-m", "iodata.__main__", infn, outfn, "-i", "fchk", "-o", "xyz"], + check=True, ) _check_convert_one(myconvert, tmpdir) @@ -94,7 +96,7 @@ def test_convert_many_manfmt(tmpdir): def test_script_many_autofmt(tmpdir): def myconvert(infn, outfn): - subprocess.run(["python", "-m", "iodata.__main__", infn, outfn, "-m"], check=True) + subprocess.run([sys.executable, "-m", "iodata.__main__", infn, outfn, "-m"], check=True) _check_convert_many(myconvert, tmpdir) @@ -102,7 +104,7 @@ def myconvert(infn, outfn): def test_script_many_manfmt(tmpdir): def myconvert(infn, outfn): subprocess.run( - ["python", "-m", "iodata.__main__", infn, outfn, "-m", "-i", "fchk", "-o", "xyz"], + [sys.executable, "-m", "iodata.__main__", infn, outfn, "-m", "-i", "fchk", "-o", "xyz"], check=True, ) From c56a117b22096c9212c53359abe3a6f2a6778380 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Mon, 3 Jun 2024 21:54:50 +0200 Subject: [PATCH 136/144] Update badges --- README.rst | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 3279a8d0c..58bb7745c 100644 --- a/README.rst +++ b/README.rst @@ -22,12 +22,11 @@ IOData ====== -|GithubActions| -|Conda| -|Pypi| -|Codecov| +|pytest| +|release| +|CodeFactor| +|PyPI| |Version| -|CondaVersion| |License| @@ -85,17 +84,14 @@ environment. Note that there may be API changes between subsequent revisions. See https://iodata.readthedocs.io/en/latest/install.html for full details. - -.. |GithubActions| image:: https://github.com/theochem/iodata/actions/workflows/ci.yml/badge.svg?branch=master - :target: https://github.com/theochem/iodata/actions/workflows/ci.yml -.. |Version| image:: https://img.shields.io/pypi/pyversions/iodata.svg +.. |pytest| image:: https://github.com/theochem/iodata/actions/workflows/pytest.yml/badge.svg + :target: https://github.com/theochem/iodata/actions/workflows/pytest.yml +.. |release| image:: https://github.com/theochem/iodata/actions/workflows/release.yml/badge.svg + :target: https://github.com/theochem/iodata/actions/workflows/release.yml +.. |CodeFactor| image:: https://www.codefactor.io/repository/github/tovrstra/stepup-core/badge + :target: https://www.codefactor.io/repository/github/tovrstra/stepup-core +.. |Version| image:: https://img.shields.io/pypi/pyversions/qc-iodata.svg .. |License| image:: https://img.shields.io/github/license/theochem/iodata -.. |Pypi| image:: https://img.shields.io/pypi/v/iodata.svg - :target: https://pypi.python.org/pypi/iodata/0.1.3 -.. |Codecov| image:: https://img.shields.io/codecov/c/github/theochem/iodata/master.svg - :target: https://codecov.io/gh/theochem/iodata -.. |Conda| image:: https://img.shields.io/conda/v/theochem/iodata.svg - :target: https://anaconda.org/theochem/iodata -.. |CondaVersion| image:: https://img.shields.io/conda/pn/theochem/iodata.svg - :target: https://anaconda.org/theochem/iodata +.. |PyPI| image:: https://img.shields.io/pypi/v/qc-iodata.svg + :target: https://pypi.python.org/pypi/qc-iodata/ .. _virtual environment: https://docs.python.org/3/tutorial/venv.html From 034164e56229a6294e4e541d8e1bbeff64b7ff15 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 06:51:48 +0200 Subject: [PATCH 137/144] Fix documentation build issues --- .readthedocs.yml | 28 ++++++++++++++++++++++++++++ doc/conf.py | 6 +----- pyproject.toml | 2 +- readthedocs.yml | 5 ----- 4 files changed, 30 insertions(+), 11 deletions(-) create mode 100644 .readthedocs.yml delete mode 100644 readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 000000000..a5acee1ff --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,28 @@ +# Read the Docs configuration file for Sphinx projects +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: ubuntu-22.04 + tools: + python: "3.12" + + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs + # builder: "dirhtml" + # Fail on all warnings to avoid broken references + # fail_on_warning: true + + +# Optional but recommended, declare the Python requirements required +# to build your documentation +# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html + python: + install: + - requirements: docs/requirements.txt diff --git a/doc/conf.py b/doc/conf.py index ebc929935..9931740e1 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -102,7 +102,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = "en" # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -207,10 +207,6 @@ # A list of files that should not be packed into the epub file. epub_exclude_files = ["search.html"] -# Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"https://docs.python.org/": None} - - # -- Configuration for autodoc extensions --------------------------------- autodoc_default_options = { diff --git a/pyproject.toml b/pyproject.toml index 35a2e9fd8..a7d1eccba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ dynamic = ["version"] [project.optional-dependencies] -dev = ["pytest", "pytest-xdist"] +dev = ["pytest", "pytest-xdist", "sphinx", "sphinx_autodoc_typehints", "sphinx-rtd-theme"] [project.urls] Documentation = "https://iodata.readthedocs.io/en/latest/" diff --git a/readthedocs.yml b/readthedocs.yml deleted file mode 100644 index 57ce308c4..000000000 --- a/readthedocs.yml +++ /dev/null @@ -1,5 +0,0 @@ -build: - image: latest - -conda: - file: environment.yml From aad4ea88298e66cc0842b2861432fbda09103526 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 20:22:38 +0200 Subject: [PATCH 138/144] Fix typo in yaml file --- .pre-commit-config.yaml | 1 + .readthedocs.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 26b8a298d..3555f8152 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,6 +8,7 @@ repos: - id: check-case-conflict - id: check-executables-have-shebangs - id: check-json + - id: check-yaml - id: check-merge-conflict - id: check-symlinks - id: check-toml diff --git a/.readthedocs.yml b/.readthedocs.yml index a5acee1ff..fe730e81f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -23,6 +23,6 @@ sphinx: # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html - python: +python: install: - requirements: docs/requirements.txt From 5c5743ca466e447ff9b7e91088adc1afa39c8e27 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 20:27:27 +0200 Subject: [PATCH 139/144] Fix another typo in rtd yaml config --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index fe730e81f..b0c1fbb24 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -25,4 +25,4 @@ sphinx: # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - - requirements: docs/requirements.txt + - requirements: doc/requirements.txt From d4a0e6dfba7b22b271f87c3c3f54867af9fac514 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 20:28:41 +0200 Subject: [PATCH 140/144] Fix another typo in rtd yaml config --- .readthedocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index b0c1fbb24..24c6d5af6 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -13,7 +13,7 @@ build: # Build documentation in the "docs/" directory with Sphinx sphinx: - configuration: docs/conf.py + configuration: doc/conf.py # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs # builder: "dirhtml" # Fail on all warnings to avoid broken references From ac9a6d51656e0bef5d03fbba51da388322ebe86d Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 20:38:49 +0200 Subject: [PATCH 141/144] Another attempt to fix rtd build --- doc/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/requirements.txt b/doc/requirements.txt index 0064fa5ef..70ffff456 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,2 +1,3 @@ # Requirements for building documentation on RTD sphinx_autodoc_typehints +sphinx_rtd_theme From 417fdf938abb48ae701c9beaf3b1e3ec6a66d998 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 21:08:10 +0200 Subject: [PATCH 142/144] Fix typo on release workflow --- .github/workflows/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6c8af7061..875e1684d 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -104,7 +104,7 @@ jobs: publish-to-testpypi: name: Publish Python distribution to TestPyPI - if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'reproducible-reporting'}} + if: ${{ github.ref == 'refs/heads/main' && github.repository_owner == 'theochem'}} needs: - build runs-on: ubuntu-latest From 2ba38581ef3e234c282276e012ac33e20fc97a82 Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 21:11:43 +0200 Subject: [PATCH 143/144] Fix extension consistency and fix typo in README --- .github/workflows/{pytest.yml => pytest.yaml} | 0 README.rst | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename .github/workflows/{pytest.yml => pytest.yaml} (100%) diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yaml similarity index 100% rename from .github/workflows/pytest.yml rename to .github/workflows/pytest.yaml diff --git a/README.rst b/README.rst index 58bb7745c..82b393c9f 100644 --- a/README.rst +++ b/README.rst @@ -84,10 +84,10 @@ environment. Note that there may be API changes between subsequent revisions. See https://iodata.readthedocs.io/en/latest/install.html for full details. -.. |pytest| image:: https://github.com/theochem/iodata/actions/workflows/pytest.yml/badge.svg - :target: https://github.com/theochem/iodata/actions/workflows/pytest.yml -.. |release| image:: https://github.com/theochem/iodata/actions/workflows/release.yml/badge.svg - :target: https://github.com/theochem/iodata/actions/workflows/release.yml +.. |pytest| image:: https://github.com/theochem/iodata/actions/workflows/pytest.yaml/badge.svg + :target: https://github.com/theochem/iodata/actions/workflows/pytest.yaml +.. |release| image:: https://github.com/theochem/iodata/actions/workflows/release.yaml/badge.svg + :target: https://github.com/theochem/iodata/actions/workflows/release.yaml .. |CodeFactor| image:: https://www.codefactor.io/repository/github/tovrstra/stepup-core/badge :target: https://www.codefactor.io/repository/github/tovrstra/stepup-core .. |Version| image:: https://img.shields.io/pypi/pyversions/qc-iodata.svg From faad8fe6271ec65657660de2dcb7bc43ef0c25ed Mon Sep 17 00:00:00 2001 From: Toon Verstraelen <Toon.Verstraelen@UGent.be> Date: Tue, 4 Jun 2024 21:21:33 +0200 Subject: [PATCH 144/144] Update PyPI classifiers to better reflect current status --- pyproject.toml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a7d1eccba..c95057402 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,13 +12,21 @@ readme = "README.rst" license = {file = "LICENSE"} requires-python = ">=3.9" classifiers = [ + "Development Status :: 3 - Alpha", "Environment :: Console", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Scientific/Engineering :: Physics", "Topic :: Scientific/Engineering :: Chemistry", - "Intended Audience :: Science/Research", ] dependencies = [ "numpy>=1.0",