@junpeipei000

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

CI計算における多重項の縮退数の不一致

解決したいこと

CaF2:Mn2+(d$^5$)のCI計算を行っています。
下記コードで、固有値・固有ベクトルはエラーなく出ました。
しかし、多重項の縮退数が合いません。
[本来]低エネルギー順に
$^6A_{1g}$(6重縮退)→$^4T_{1g}$(12重縮退)→$^4T_{2g}$(12重縮退)
皆様のお力を貸していただけないでしょうか。何卒よろしくお願いします。

計算手順

一電子積分および二電子積分を全ての$i,j,k,l$に対して計算・結果を保存
$\langle i | \hat{h} | j \rangle
= \int \phi_i^*(\mathbf{r}) \hat{h}(\mathbf{r})\phi_j(\mathbf{r})d\mathbf{r}$
$\langle ij | \hat{g} | kl \rangle = \iint \phi_i^{\ast}(r)\phi_j^{\ast}(\mathbf{r}_2)\hat{g}(\mathbf{r}_1,\mathbf{r}_2)\phi_k(\mathbf{r}_1)\phi_l(\mathbf{r}_2)d\mathbf{r}_1d\mathbf{r}_2$
これをもとに、CI行列要素しCI行列(252×252)を組み立てる。
$\langle \Phi_p | \hat{H} | \Phi_q \rangle = \sum _{i,j =1}^{10}\langle i | \hat{h} | j \rangle
\langle \Phi_p | a_i^\dagger a_j | \Phi_q \rangle + \tfrac{1}{2} \sum _{i,j,k,l=1}^{10} \langle ij | \hat{g} | kl \rangle \langle \Phi_p | a_i^\dagger a_j^\dagger a_l a_k | \Phi_q \rangle$
CI行列を対角化することで、固有値(多重項準位)および固有ベクトルを得る。

該当するソースコード

one_integral.pklは一電子積分の結果を保存したpandasのdf(column:[i,j,one_integral])
two_integral.pklは二電子積分の結果を保存したpandasのdf(column:[i,j,k,l,integral])

import numpy as np
import pandas as pd
import itertools
from math import comb

# ========= ユーザ設定 =========
N_ORB = 10     # 基底関数数
N_ELEC = 5     # 電子数
ONE_PKL = 'one_integral.pkl'  # columns: ['i','j','one_integral'](1始まり)
TWO_PKL = 'two_integral.pkl'  # columns: ['i','j','k','l','integral'](1始まり)

# =============================

# ---- 基底(スレーター行列式=占有0/1ベクトル)を生成 ----
def generate_slater_basis(n_orb, n_elec):
    basis_bits = []
    basis_occ_lists = []
    for occ in itertools.combinations(range(n_orb), n_elec):
        bits = np.zeros(n_orb, dtype=np.int8)
        bits[list(occ)] = 1
        basis_bits.append(bits)
        basis_occ_lists.append(list(occ))  # 0始まりの占有位置
    return basis_bits, basis_occ_lists

B_BITS, B_OCC = generate_slater_basis(N_ORB, N_ELEC)   #長さ=252

# ---- 反交換に基づく位相:左から s-1 番目までの占有数の偶奇 ----
def parity_before(r, s):
    # r: 0/1 の occupation(0始まり)
    # s: 0始まりの軌道インデックス
    return (-1) ** int(r[:s].sum())

# ---- ⟨p| a_i† a_j |q⟩(i,j は 0始まり)----
def me_1body(p_bits, q_bits, i, j):
    # a_j が q に作用できる条件
    if q_bits[j] == 0:
        return 0
    # a_i† が最終的に作用できる条件
    if p_bits[i] == 0:
        return 0

    # 逐次作用して p に一致するか確認
    r = q_bits.copy()
    # a_j
    phase_j = parity_before(r, j)
    r[j] = 0
    # a_i†
    if r[i] == 1:  # Pauli
        return 0
    phase_i = parity_before(r, i)
    r[i] = 1

    if np.array_equal(r, p_bits):
        if i == j:
            # a_i† a_i = n_i → 対角で 1、位相は (+1)*(+1) になる
            # ただし上の条件で p_bits[i]==1 かつ q_bits[i]==1 なのでOK
            return 1
        return phase_j * phase_i
    return 0

# ---- ⟨p| a_i† a_j† a_l a_k |q⟩(i,j,k,l は 0始まり)----
def me_2body(p_bits, q_bits, i, j, k, l):
    # 同一演算子の二連はゼロ
    if i == j or k == l:
        return 0
    # 消滅は q の占有から
    if q_bits[k] == 0 or q_bits[l] == 0:
        return 0
    # 生成は最終 p の占有へ
    if p_bits[i] == 0 or p_bits[j] == 0:
        return 0

    # 逐次作用:a_k → a_l → a_j† → a_i†(右から左)
    r = q_bits.copy()

    # a_k
    phase1 = parity_before(r, k)
    r[k] = 0

    # a_l
    phase2 = parity_before(r, l)
    if r[l] == 0:
        return 0
    r[l] = 0

    # a_j†
    phase3 = parity_before(r, j)
    if r[j] == 1:
        return 0
    r[j] = 1

    # a_i†
    phase4 = parity_before(r, i)
    if r[i] == 1:
        return 0
    r[i] = 1

    if np.array_equal(r, p_bits):
        return phase1 * phase2 * phase3 * phase4
    return 0

# ---- 積分を読み込み(1始まり) → ルックアップ辞書に ----
one_df = pd.read_pickle(ONE_PKL)
two_df = pd.read_pickle(TWO_PKL)
print(one_df.shape)
print(two_df.shape)


one_lookup = {
    (int(i), int(j)): complex(np.real(v)) if pd.notna(v) else 0.0
    for i, j, v in one_df[['i', 'j', 'one_integral']].itertuples(index=False)
}

two_lookup = {
    (int(i), int(j), int(k), int(l)): complex(np.real(v)) if pd.notna(v) else 0.0
    for i, j, k, l, v in two_df[['i', 'j', 'k', 'l', 'integral']].itertuples(index=False)
}

# ---- CI 行列の構築(252×252)----
NB = len(B_BITS)
H = np.zeros((NB, NB), dtype=complex)

# 励起次数で枝刈り:1体は最大1電子移動、2体は最大2電子移動
def hamming(x, y):
    return int(np.sum(x ^ y))  # xor の1の個数(np.int8でもOK)

for p in range(NB):
    p_bits = B_BITS[p]
    p_occ = B_OCC[p]  # 0始まり
    # p側で 1体:i は pで占有の軌道(0始まり)
    p_occ_1based = [i+1 for i in p_occ]
    # p側で 2体:i<j の2個選び(0始まり)
    p_pairs_0 = list(itertools.combinations(p_occ, 2))
    p_pairs_1b = [(i+1, j+1) for (i,j) in p_pairs_0]

    for q in range(NB):
        q_bits = B_BITS[q]
        q_occ = B_OCC[q]
        q_occ_1based = [k+1 for k in q_occ]
        # 励起次数(xorの1の数)は 0,2,4 のみ寄与
        d = hamming(p_bits, q_bits)
        if d not in (0, 2, 4):
            continue

        # ---- 一電子項:∑_{ij} h_ij ⟨p|a_i† a_j|q⟩ ----
        # 条件:j は q で占有、i は p で占有(i,j は 0始まり)
        term1 = 0.0
        for i0 in p_occ:        # 0始まり
            for j0 in q_occ:    # 0始まり
                val = me_1body(p_bits, q_bits, i0, j0)
                if val == 0:
                    continue
                hij = one_lookup.get((i0+1, j0+1), 0.0)  # 1始まりで検索
                if hij != 0.0:
                    term1 += val * hij

        # ---- 二電子項: 1/2 ∑_{ijkl} (ij|kl) ⟨p|a_i† a_j† a_l a_k|q⟩ ----
        term2_sum = 0.0
        # i<j, k<l で十分だが、位相は me_2body に任せるので全順序にしてもOK。
        # ただし計算量を抑えるため i<j, k<l で回す。
        for (i0, j0) in p_pairs_0:   # 0始まり
            for (k0, l0) in itertools.combinations(q_occ, 2):
                # 4通りの順序を試す((i,j)の順、(k,l)の順)※辞書は1始まり
                # Coulomb integral は (ij|kl) = (ji|lk) = (kl|ij) ... 対称だが、
                # two_lookup が完全表でない可能性に備えて必要な順序を全部問い合わせる。
                contrib = 0.0
                # (i,j,k,l)
                for (ii,jj) in ((i0,j0),(j0,i0)):
                    for (kk,ll) in ((k0,l0),(l0,k0)):
                        val = me_2body(p_bits, q_bits, ii, jj, kk, ll)
                        if val == 0:
                            continue
                        gij = two_lookup.get((ii+1, jj+1, kk+1, ll+1), 0.0)
                        if gij != 0.0:
                            contrib += val * gij
                term2_sum += contrib

        H[p, q] = term1 + 0.5 * term2_sum

# 数値非対称を消す(理論上 H は対称)
H = 0.5 * (H + H.T)

# ---- 対角化 ----
eigvals, eigvecs = np.linalg.eigh(H)

print("Matrix size:", H.shape)
print(eigvals[:40])

上記コードでの結果

スクリーンショット 2025-09-22 16.09.15.png
のように、縮退数は6→6→1→となり本来の$^6A_{1g}$(6重縮退)→$^4T_{1g}$(12重縮退)→$^4T_{2g}$(12重縮退)とは縮退数が異なります。
自分でも何度も考えなおしましたが、何が原因か分かりません。
皆様のお力を貸していただけないでしょうか。何卒よろしくお願いします。

1 likes

No Answers yet.

Your answer might help someone💌