1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
|
# Copyright (c) 2024, the cclib development team
#
# This file is part of cclib (http://cclib.github.io) and is distributed under
# the terms of the BSD 3-Clause License.
"""Test the various population analyses (MPA, LPA, CSPA, Bickelhaupt) in cclib"""
import os
from cclib.io import ccread
from cclib.method import Bader, volume
from cclib.method.calculationmethod import MissingAttributeError
from cclib.parser import Psi4
from cclib.parser.utils import find_package
import numpy
import pytest
from numpy.testing import assert_allclose
from ..test_data import getdatafile
_found_pyquante = find_package("PyQuante")
class BaderTest:
"""Bader's QTAIM method tests."""
def setUp(self) -> None:
super(BaderTest, self).setUp()
self.parse()
def parse(self) -> None:
self.data, self.logfile = getdatafile(Psi4, "basicPsi4-1.2.1", ["water_mp2.out"])
self.volume = volume.Volume((-4, -4, -4), (4, 4, 4), (0.2, 0.2, 0.2))
def testmissingrequiredattributes(self) -> None:
"""Is an error raised when required attributes are missing?"""
for missing_attribute in Bader.required_attrs:
self.parse()
delattr(self.data, missing_attribute)
with pytest.raises(MissingAttributeError):
trialBader = Bader(self.data, self.volume)
def test_val(self) -> None:
"""Do the calculated values match with known values?"""
self.data, logfile = getdatafile(Psi4, "basicPsi4-1.2.1", ["water_mp2.out"])
self.volume = volume.Volume((-4, -4, -4), (4, 4, 4), (0.2, 0.2, 0.2))
self.analysis = Bader(self.data, self.volume)
if not _found_pyquante:
pytest.skip("analysis.calculate() requires PyQuante")
self.analysis.calculate()
refData = [6.9378, 0.3639, 0.3827] # values from `bader` package
assert_allclose(self.analysis.fragcharges, refData, atol=0.15)
def test_chgsum_hf(self) -> None:
"""Does the sum of charges equate to the number of electrons for a simple molecule?"""
hfpath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "hf.out")
data = ccread(hfpath)
vol = volume.Volume((-6, -6, -6), (6, 6, 6), (0.2, 0.2, 0.2))
analysis = Bader(data, vol)
if not _found_pyquante:
pytest.skip("analysis.calculate() requires PyQuante")
analysis.calculate()
assert abs(numpy.sum(analysis.fragcharges) - 10) < 1
def test_symms_benzene(self) -> None:
"""Do the carbons in benzene ring get assigned with roughly equal charges?
Discrepancy between carbons do exist in this test due to grid coarseness and limited
size of the grid. One can do a larger test, for example, using 160x170x80 size grid to
obtain [5.63728706, 5.89862956, 5.73956182, 5.63728706, 5.73963179, 5.8996759].
In comparison, `bader`, which is implemented by Henkelman group which proposed
the algorithm, reports [5.947370, 6.032509, 5.873431, 5.947370, 5.873431, 6.033485].
`bader` uses fuzzy boundaries which result in slightly higher carbon charges.
"""
benzenepath = os.path.join(os.path.dirname(os.path.realpath(__file__)), "benzene.out")
data = ccread(benzenepath)
vol = volume.read_from_cube(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "benzene.cube")
)
assert_allclose(vol.origin, numpy.array([-4.1805, -4.498006, -2.116709]), atol=1e-3)
analysis = Bader(data, vol)
analysis.calculate()
assert abs(analysis.fragcharges[0:6].max() - analysis.fragcharges[0:6].min()) < 0.5
|