5. 磁性质数值导数 (1):RHF 的非 GIAO 磁化率#

创建时间:2020-08-27

在这篇文档中,我们会讨论使用 PySCF 以及其作为 libcint 的接口,计算非 GIAO 的 RHF 数值磁化率的程序。该文档大量参考 PySCF 的代码 magnetizability/rhf.pynmr/rhf.py。一些公式符号参考 Atkins, Friedman [1]

我们的讨论中所使用到的分子体系 mol 会是非对称的氨分子,并且取用最小基组。规范原点 (Gauge Origin) 会取在坐标原点上 coord_orig。其 RHF 计算放在实例 mf,而磁化率计算实例会放在 mf_mag

from pyscf import gto, scf
from pyscf.prop import nmr, magnetizability
import numpy as np

np.set_printoptions(precision=5, linewidth=150, suppress=True)
mol = gto.Mole()
mol.atom = """
N  0.  0.  0.
H  0.  1.  0.2
H  0.1 0.3 1.5
H  0.9 0.4 -.2
"""
mol.basis = "STO-3G"
mol.verbose = 0
mol.build()
coord_orig = np.zeros(3)

其自洽场能量为

mf = scf.RHF(mol).run()
mf.e_tot
-55.253540514686556

其磁化率张量 \(\xi_{ts}\) 为 (其中,\(t, s \in \{ x, y, z \}\) 表示三个坐标方向,需要注意这里选择了规范原点为坐标原点,若选取其它坐标则会得到非常不同的结果)

mf_mag = magnetizability.RHF(mf)
mf_mag.gauge_orig = coord_orig
mf_mag.kernel()
array([[-4.94475,  0.21773, -0.08268],
       [ 0.21773, -4.27801,  0.49885],
       [-0.08268,  0.49885, -4.15348]])

5.1. 基础概念#

5.1.1. 分子能量作为外加微扰量的函数#

我们指出,磁化率可以看作是分子处在某一恒定外加磁场 \(\boldsymbol{\mathscr{B}}\) 下 (作为三维矢量),所产生的能量变化的表征:

\[ E_\mathrm{tot} (\boldsymbol{\mathscr{B}}) = E_\mathrm{tot}^{(0)} + E_\mathrm{tot}^{(1)} \boldsymbol{\mathscr{B}} + E_\mathrm{tot}^{(2)} \boldsymbol{\mathscr{B}}^2 + \cdots \]

以一般物理的约定俗成而言,对于外加的磁场微扰 (Atkins and Friedman, eq 13.34)

\[ E_\mathrm{tot}^{(2)} = - \frac{1}{2} \boldsymbol{\mathscr{B}}^\dagger \boldsymbol{\xi} \boldsymbol{\mathscr{B}} = - \frac{1}{2} \sum_{t, s \in \{ x, y, z \}} \mathscr{B}_t \xi_{ts} \mathscr{B}_s \]

其中,\(\boldsymbol{\xi}\) 是二维对称矩阵 (或称张量,如之前代码所展示)。我们使用了 \(\boldsymbol{\xi}\) (Atkins and Friedman, eq 13.34, termed as magnetizability) 而非 \(\boldsymbol{\chi}\) (Atkins and Friedman, eq 13.3c, termed as magnetic susceptibility) 来表示磁化率。因此,磁化率本身可以表示为 (矩阵元的形式与向量形式)

\[ \xi_{ts} = - \frac{\partial^2 E_\mathrm{tot} (\boldsymbol{\mathscr{B}})}{\partial \mathscr{B}_t \partial \mathscr{B}_s}, \quad \boldsymbol{\xi} = - \boldsymbol{\nabla}_{\boldsymbol{\mathscr{B}}} \boldsymbol{\nabla}_{\boldsymbol{\mathscr{B}}}^\dagger E_\mathrm{tot} (\boldsymbol{\mathscr{B}}) \]

5.1.2. 哈密顿算符作为外加微扰量的算符#

能量可以通过波函数在哈密顿算符的变分极小值处的期望获得:

\[ E_\mathrm{tot} (\boldsymbol{\mathscr{B}}) = \langle \Psi (\boldsymbol{\mathscr{B}}) | \hat H (\boldsymbol{\mathscr{B}}) | \Psi (\boldsymbol{\mathscr{B}}) \rangle \]

其中,

\[ \hat H (\boldsymbol{\mathscr{B}}) = \sum_{i} \hat h (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) + \hat V_\mathrm{ee} + \hat V_\mathrm{NN} \]

上述算符是体系的多电子总哈密顿算符;而 \(\hat h (\boldsymbol{\mathscr{B}})\) 则是单电子的 Core Hamiltonian 算符;\(\hat V_\mathrm{ee}\) 为电子互斥算符,\(\hat V_\mathrm{NN}\) 为原子核互斥算符。需要注意,由于我们不使用 GIAO,因此 \(\hat V_\mathrm{ee}\) 就是普通的电子互斥算符,不受外场 \(\boldsymbol{\mathscr{B}}\) 干扰;但使用 GIAO 的情况下,可能需要额外考虑这部分贡献。

\[ \hat h (\boldsymbol{\mathscr{B}}) = \hat h {}^{(0)} + \hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) + \hat h {}^{(2)} (\boldsymbol{\mathscr{B}}) \]

\(\hat h {}^{(0)}\) 是没有外加场的算符 (这与自洽场计算过程所用到的算符相同)。其余的算符则为 (Atkins and Friedman, eq 13.26, eq 13.29)

\[\begin{split} \begin{align} \hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) &= \frac{1}{2} \boldsymbol{\mathscr{B}} \cdot \boldsymbol{r} \times \boldsymbol{\hat{p}} \\ \hat h {}^{(2)} (\boldsymbol{\mathscr{B}}) &= \frac{1}{8} \big( \boldsymbol{\mathscr{B}}^2 \boldsymbol{r}^2 - (\boldsymbol{\mathscr{B}} \cdot \boldsymbol{r})^2 \big) \end{align} \end{split}\]

其中,

\[\begin{split} \boldsymbol{r} = \begin{pmatrix} x \\ y \\ z \end{pmatrix}, \quad \boldsymbol{\hat p} = \begin{pmatrix} \displaystyle - i \frac{\partial}{\partial x} \\ \displaystyle - i \frac{\partial}{\partial y} \\ \displaystyle - i \frac{\partial}{\partial z} \end{pmatrix} = -i \nabla \boldsymbol{r} \end{split}\]

其中,

\[ E_\mathrm{tot}^{(2)} = 2 \langle \Psi^{(0)} (\boldsymbol{\mathscr{B}}) | \sum_i \hat h {}^{(1)} (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) | \Psi^{(1)} (\boldsymbol{\mathscr{B}}) \rangle + \langle \Psi^{(0)} | \sum_i \hat h {}^{(2)} (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) | \Psi^{(0)} \rangle \]

前一项被称为顺磁项 (Paramagnetic),后一项称为抗磁项 (Diamagnetic)。\(\Psi^{(0)}\) 是指未微扰的体系哈密顿算符 \(\hat H {}^{(0)}\) 的本征态,\(\Psi^{(1)} (\boldsymbol{\mathscr{B}})\) 则是一阶微扰的波函数;其解析的求取方法是在程序中表示为 U 矩阵,通过 CP-HF 方程求取;我们在这里不会对解析方法作说明,但了解这两项的区分是有帮助的。

5.2. Core Hamiltonian 的程序实现#

我们知道,PySCF 中,在自洽场实例中更改 Core Hamiltonian 的类方法函数 (method function) 就可以实现外场微扰下的能量计算。这在 pyxdh 偶极矩的计算 文档 中有所说明。在这里我们也要做类似的工作。

5.2.1. 顺磁项#

在 PySCF 中,顺磁项 \(\hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) = \frac{1}{2} \boldsymbol{\mathscr{B}} \cdot \boldsymbol{r} \times \boldsymbol{\hat p}\) 有其对应的积分 hcore_1 (\(h_{t \mu \nu}^{(1)}\),需要注意它不包含作为标量的 \(\mathscr{B}_t\))

\[ h_{t \mu \nu}^{(1)} \cdot \mathscr{B}_t = \langle \mu | \hat h {}^{(1)} (\mathscr{B}_t) | \nu \rangle \]
hcore_1 = - 0.5 * mol.intor("int1e_cg_irxp") * 1j
hcore_1.shape, hcore_1.dtype
((3, 8, 8), dtype('complex128'))

上述的程序看起来会有些奇怪,因为这里出现了复数。我们需要分段对其作解释。

积分字符

我们使用到了积分字符 int1e_cg_irxp。关于这段字符,其意义需要通过 auto_intor.cl 程序了解:

  '("int1e_cg_irxp"             (#C(0 1) \| rc cross p))

其右侧是积分的具体形式,说明在 README 文件中,意义为

\[ \mathtt{int1e\_cg\_irxp} = i \langle \mu | \boldsymbol{r} \times \boldsymbol{\hat p} | \nu \rangle \]

其维度是 \((t, \mu, \nu)\),但其第一个维度是通过向量叉乘给出,因此它与 \(\boldsymbol{r}\)\(\boldsymbol{p}\) 的维度不是直接相关的。如果我们令动量算符 \(\boldsymbol{\hat l} = \boldsymbol{r} \times \boldsymbol{\hat p}\),那么可以将上述积分写为

\[ \mathtt{int1e\_cg\_irxp}_{t \mu \nu} = i \langle \mu | \hat l_t | \nu \rangle \]

反对称性厄米性

我们应当留意到 \(\mathtt{int1e\_cg\_irxp}_{t \mu \nu}\) 是一个反对称矩阵 (即随 \(\mu, \nu\) 交换成相反值)

np.allclose(mol.intor("int1e_cg_irxp"), - mol.intor("int1e_cg_irxp").swapaxes(-1, -2))
True

这是由于 \(\nabla\) 算符本身是一个反对称算符。但是需要留意到,动量算符在此基础上乘以了虚数单位 \(- i\),因此,该矩阵是厄米的,即其转置后的共轭是其本身。我们所定义的 \(h_{t \mu \nu}^{(1)}\) 就具有这样的性质:

np.allclose(hcore_1, hcore_1.swapaxes(-1, -2).conj())
True

因此,我们会说 hcore_1 \(h_{t \mu \nu}^{(1)}\) 是厄米的。

5.2.2. 抗磁项#

抗磁项 \(\hat h {}^{(2)} (\boldsymbol{\mathscr{B}}) = \frac{1}{8} \big( \boldsymbol{\mathscr{B}}^2 \boldsymbol{r}^2 - (\boldsymbol{\mathscr{B}} \cdot \boldsymbol{r})^2 \big)\) 需要一些技巧生成。PySCF 中可以生成张量 int1e_rr

\[ \mathtt{int1e\_rr}_{ts \mu \nu} = \langle \mu | ts | \nu \rangle \]

我们定义 hcore_2 \(h_{ts \mu \nu}^{(2)}\)

\[ h_{ts \mu \nu}^{(2)} = \frac{1}{8} \big( \delta_{ts} \langle \mu | x^2 + y^2 + z^2 | \nu \rangle - \langle \mu | ts | \nu \rangle \big) \]
with mol.with_common_orig(coord_orig):
    int1e_rr = mol.intor("int1e_rr").reshape(3, 3, mol.nao, mol.nao)
hcore_2 = 1/8 * (np.einsum("ts, uv -> tsuv", np.eye(3), int1e_rr.diagonal(0, 0, 1).sum(-1)) - int1e_rr)

并且,上述张量具有下述性质:

\[ h_{ts \mu \nu}^{(2)} \cdot \mathscr{B}_t \mathscr{B}_s = \langle \mu | \hat h {}^{(2)} (\mathscr{B}_t, \mathscr{B}_s) | \nu \rangle \]

5.2.3. Core Hamiltonian 程序实现#

我们最后可以编写外加磁场微扰下的 Core Hamiltonian,以及在此微扰下的分子体系能量。为了加快计算速度,我们会使用为微扰的自洽场密度作为初猜 dm_guess。Core Hamiltonian 表达式为

\[ h_{\mu \nu} (\boldsymbol{\mathscr{B}}) = h_{\mu \nu} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z) = h_{\mu \nu}^{(0)} + \sum_{t} h_{t \mu \nu}^{(1)} \mathscr{B}_t + \sum_{ts} h_{ts \mu \nu}^{(2)} \mathscr{B}_t \mathscr{B}_s \]
dm_guess = mf.make_rdm1()

def hcore_mag_field(dev_xyz):
    mf = scf.RHF(mol)
    def hcore(mol_):
        hcore_total  = np.asarray(scf.rhf.get_hcore(mol_), dtype=np.complex128)
        hcore_total += np.einsum("tuv, t -> uv", hcore_1, dev_xyz)
        hcore_total += np.einsum("tsuv, t, s -> uv", hcore_2, dev_xyz, dev_xyz)
        return hcore_total
    mf.get_hcore = hcore
    return mf.kernel(dm=dm_guess)

上述函数的参数 t, s 表示坐标方向分量,dev_t, dev_s 表示外加微扰大小,单位为 a.u.。

譬如,若在 \(x\) 方向的磁场上施加 \(\mathscr{B}_x = 1 \, \mathsf{a.u.}\),而 \(y\) 方向上施加 \(\mathscr{B}_y = 2 \, \mathsf{a.u.}\) (即 \(\boldsymbol{\mathscr{B}} = (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z) = (1, 2, 0) \, \mathsf{a.u.}\)),那么下述程序会给出该自洽场能量 \(E_\mathrm{tot} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z)\)

hcore_mag_field(0, 1, 1, 2)
-57.997213868466154

5.3. 数值导数求取磁化率#

我们已经有了求取 \(E_\mathrm{tot} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z)\) 的程序了,接下来就可以进行数值导数计算。数值导数的计算公式可以简单地使用三点差分法;对于被求导量 \(x :\neq y\),有 (当 \(h\) 足够小时)

\[ \frac{\partial^2 f}{\partial x \partial y} \simeq \frac{1}{4 h^2} \big[ f(x + h, y + h) - f(x - h, y + h) - f(x + h, y - h) + f(x - h, y - h) \big] \]

而对被求导量相同的情形,有

\[ \frac{\partial^2 f}{\partial x^2} \simeq \frac{1}{h^2} \big[ f(x + h) - 2 f(x) + f(x - h) \big] \]

下面的程序就依照上述两个公式进行二阶数值导数求取。求导的原点取在 \((\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z) = (0, 0, 0)\) 即不受外磁场影响的情形的自洽场能量 eng_origin,差分大小为 interval \(h = 10^{-3} \, \mathsf{a.u.}\)。需要注意,根据约定俗成,

\[ \xi_{ts} = - \frac{\partial^2 E_\mathrm{tot} (\boldsymbol{\mathscr{B}})}{\partial \mathscr{B}_t \partial \mathscr{B}_s} \]

因此求取得到的磁化率 num_polar \(\xi_{ts}\) 需要乘以 -1。

eng_origin = hcore_mag_field((0, 0, 0))
interval = 1e-3
num_polar = np.zeros((3, 3))
for t in range(3):
    for s in range(3):
        if t != s:
            dev_xyzs = np.zeros((4, 3))
            dev_xyzs[0, t] = dev_xyzs[0, s] = dev_xyzs[1, t] = dev_xyzs[2, s] =  interval
            dev_xyzs[3, t] = dev_xyzs[3, s] = dev_xyzs[2, t] = dev_xyzs[1, s] = -interval
            num_polar[t, s] = (
                + hcore_mag_field(dev_xyzs[0])
                - hcore_mag_field(dev_xyzs[1])
                - hcore_mag_field(dev_xyzs[2])
                + hcore_mag_field(dev_xyzs[3])
            ) / (4 * interval**2)
        else:
            dev_xyzs = np.zeros((2, 3))
            dev_xyzs[0, t], dev_xyzs[1, t] = interval, -interval
            num_polar[t, t] = (
                + hcore_mag_field(dev_xyzs[0])
                + hcore_mag_field(dev_xyzs[2])
                - eng_origin * 2
            ) / (interval ** 2)
num_polar *= -1
num_polar
array([[-4.94475,  0.21773, -0.08268],
       [ 0.21773, -4.27801,  0.49885],
       [-0.08268,  0.49885, -4.15348]])

我们再与 PySCF 的解析结果作对照:

mf_mag.kernel()
array([[-4.94475,  0.21773, -0.08268],
       [ 0.21773, -4.27801,  0.49885],
       [-0.08268,  0.49885, -4.15348]])