File: testbader.py

package info (click to toggle)
cclib 1.8-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 55,300 kB
  • sloc: python: 23,276; makefile: 84; sh: 26
file content (90 lines) | stat: -rw-r--r-- 3,491 bytes parent folder | download
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
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023, 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
import unittest

import numpy

from cclib.method import Bader, volume
from cclib.parser import Psi4
from cclib.io import ccread
from cclib.method.calculationmethod import MissingAttributeError

from numpy.testing import assert_allclose

from ..test_data import getdatafile
import pytest


class BaderTest(unittest.TestCase):
    """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)
        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)
        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