4.1.1. Electronegativity equalization methodΒΆ

The electronegativity equalization method (EEM) is a second order expansion of the molecular energy \(E_{EEM}\) in terms of the partial charges \(q_i\):

\(E_{EEM}(q_1,q_2,...,q_N) = \sum\limits_{i=1}^{N} \left[ \chi_i q_i + \frac{1}{2}\eta_i q_i^2 + \frac{1}{2}\sum\limits_{l\ne i}^{N}q_iq_lJ_{il}\right]\)

where \(N\) is the number of atoms, \(\chi_i\) are the atomic electronegativities, \(\eta_i\) the atomic hardness and \(J_{il}\) the Coulomb interaction between atoms. The factor 1/2 before the Coulomb matrix is to avoid double counting. In equilibrium, the chemical potential is the same for each atom, \(\chi_{eq}\). The EEM can be implemented by taking the derivative of \(E_{EEM}\) with respect to \(q_i\) and equating it to zero:

\(0 = \displaystyle{\frac{\partial E_{EEM}}{\partial q_i}} = \chi_{eq} = \chi_i + q_i\eta_i + \frac{1}{2}\sum\limits_{l\ne i}^{N} q_l J_{il}\)

which leads to a set of \(N+1\) equations that is linear in the \(q_i\) and that can be solved using linear algebra tools. After taking the derivative with respect to \(q_i\) a matrix equation is obtained:

\(\begin{bmatrix}\eta_1 & J_{12} & J_{13} & ... & J_{1N} & -1 \\ J_{21} & \eta_2 & J_{23} & ... & J_{2N} & -1 \\ ... & ... & ... &... & ... & ... \\ J_{N1} & J_{N2} & J_{N3} & ... & \eta_N & -1 \\ 1 & 1 & 1 & ... & 1 & 0\end{bmatrix} \begin{bmatrix}q_1\\ q_2\\ ... \\ q_N \\ \chi_{eq} \end{bmatrix} = \begin{bmatrix}-\chi_1\\-\chi_2\\...\\ -\chi_N\\ q_{tot}\end{bmatrix}\)

Note the last column in the matrix is there to make sure that the electronegativity for all atoms is the same (\(\chi_{eq}\)), while the last row is there to make sure the total charge \(q_{tot}\) is maintained. A good reference for the method is the classic paper by Rappe and Goddard.

In the code below we test the method on a methanol molecule, using parameters from Verstraelen et al. as well as experimental data from Cordero et al..

import numpy as np
from scipy.linalg import lstsq
import math
from charge_utils import *
from test_systems import *

def solveEEM(names, coords, qtotal, model, verbose=False):
    # Compute Coulomb
    J = calcJEEM(names, coords, model)
    N = coords.shape[0]

    # Right hand side of the equation
    rhs = np.zeros(N+1,dtype=float)
    chi = get_chi(model)
    for i in range(N):
        if names[i] in chi:
            rhs[i] = -chi[names[i]]
        else:
            print("No chi for %s" % names[i])
            exit(1)
    rhs[N] = qtotal
    q = np.linalg.solve(J, rhs)
    if verbose:
        print("J = \n{}".format(J))
        print("rhs = {}".format(rhs))
        print("q = {}".format(q))
        y = np.dot(J,q)
        print("y = {}".format(y))
    return q

def run_compound(molname, verbose, alexandria=False):
    mol = get_system(molname)
    if not mol:
        print("No test system %s defined" % molname)
    else:
        print("\n%s" % molname)
        q = solveEEM(mol["names"], mol["coords"], mol["qtotal"], verbose, alexandria)
        for i in range(mol["coords"].shape[0]):
            print("q[%s] = %g" % (mol["names"][i], q[i]))
        printDipole(q, mol["coords"], mol["atomnr"], False)

verbose = False

for compound in [ "carbon-monoxide", "water", "methanol", "acetate" ]:
    run_compound(compound, "ACM-g", verbose)
carbon-monoxide
q[c2] = 0.100795
q[o] = -0.100795
Dipole = 0.0580969 Debye

water
q[hw] = 0.130157
q[ow] = -0.260313
q[hw] = 0.130157
Dipole = 0.731574 Debye

methanol
q[oh] = -0.252355
q[hp] = 0.13162
q[c3] = -0.151608
q[h1] = 0.0878756
q[h1] = 0.087886
q[h1] = 0.0965812
Dipole = 1.22594 Debye

acetate
q[c3] = -0.254055
q[cm] = -0.108471
q[om] = -0.322013
q[om] = -0.322018
q[hc] = 0.00241518
q[hc] = 0.00166854
q[hc] = 0.00247338
Dipole = 0.841977 Debye

Note the positive charge on O and negative charge on one of the H with the EEM chi and eta values from Verstraelen. These charges are too small since the experimental dipole for methanol is about 1.69 Debye.