From b590b6fa51183b8c9ef2a292b806bd178c204b37 Mon Sep 17 00:00:00 2001
From: Seunghoon Lee <seunghoonlee89@gmail.com>
Date: Sat, 15 Feb 2025 23:51:15 +0900
Subject: [PATCH 01/10] sfnoci

---
 pyscf/lib/CMakeLists.txt       |    9 +
 pyscf/sfnoci/SFNOCI_contract.c |  213 ++++++
 pyscf/sfnoci/direct_sfnoci.py  |  819 ++++++++++++++++++++++
 pyscf/sfnoci/sfnoci.py         | 1165 ++++++++++++++++++++++++++++++++
 4 files changed, 2206 insertions(+)
 create mode 100644 pyscf/sfnoci/SFNOCI_contract.c
 create mode 100644 pyscf/sfnoci/direct_sfnoci.py
 create mode 100644 pyscf/sfnoci/sfnoci.py

diff --git a/pyscf/lib/CMakeLists.txt b/pyscf/lib/CMakeLists.txt
index 1fb802c..baa5d92 100644
--- a/pyscf/lib/CMakeLists.txt
+++ b/pyscf/lib/CMakeLists.txt
@@ -138,3 +138,12 @@ set_target_properties (clib_dsrg PROPERTIES
     CLEAN_DIRECT_OUTPUT 1
     LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
     OUTPUT_NAME "dsrg")
+
+# Build the SFNOCI library
+set (SFNOCI_SOURCE_FILES "../sfnoci/SFNOCI_contract.c")
+add_library (clib_sfnoci SHARED ${SFNOCI_SOURCE_FILES})
+set_target_properties (clib_sfnoci PROPERTIES
+    CLEAN_DIRECT_OUTPUT 1
+    LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
+    OUTPUT_NAME "sfnoci")
+
diff --git a/pyscf/sfnoci/SFNOCI_contract.c b/pyscf/sfnoci/SFNOCI_contract.c
new file mode 100644
index 0000000..aa636a8
--- /dev/null
+++ b/pyscf/sfnoci/SFNOCI_contract.c
@@ -0,0 +1,213 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <stdbool.h>
+#include <inttypes.h> //
+#include <unistd.h>  //
+
+bool is_matching_row(int *matrix_row, int *target_row, int cols) {
+    for (int i = 0; i < cols; i++) {
+        if (matrix_row[i] != target_row[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+int find_matching_row(int *matrix, int *occ, int nrows, int ncols) {
+    for (int i = 0; i < nrows; i++) {
+        int match = 1;
+        for (int j = 0; j < ncols; j++) {
+            if (matrix[i*ncols+j] != occ[j]) {
+                match = 0;
+                break;
+            }
+        }
+        if (match) return i;
+    }
+    return -1;  
+}
+
+int num_to_group(int *group, int group_sizes[], int num_groups, int number) {
+    int index = 0;
+    for (int i = 0; i < num_groups; i++) {
+        for (int j = 0; j < group_sizes[i]; j++) {
+            if (group[index] == number) {
+                return i; 
+            }
+            index++;
+        }
+    }
+    return -1; 
+}
+
+void str2occ(int *occ, uint64_t str, int norb) {
+    for (int i = 0; i < norb; i++) {
+        occ[i] = (str & (1ULL << i)) ? 1 : 0;
+    }
+}
+
+void SFNOCIcontract_H_spin1(double *erieff, double *ci0, double *ci1, int ncas, int nelecasa, int nelecasb, 
+int *PO, int PO_nrows, 
+int na, uint64_t *stringsa, int nb, uint64_t *stringsb,
+int *group, int group_sizes[], int num_groups, 
+int *t1a, int *t1a_nonzero, int t1a_nonzero_size, 
+int *t1b, int *t1b_nonzero, int t1b_nonzero_size,
+int *t2aa, int *t2aa_nonzero, int t2aa_nonzero_size,
+int *t2bb, int *t2bb_nonzero, int t2bb_nonzero_size,
+double *TSc, double *energy_core){
+
+int aa, ia, str1a, str0a;
+int ab, ib, str1b, str0b;
+int a1, i1, a2, i2;
+int* w_occa = (int*) malloc(ncas * sizeof(int));
+int* w_occb = (int*) malloc(ncas * sizeof(int));
+int* x_occa = (int*) malloc(ncas * sizeof(int));
+int* x_occb = (int*) malloc(ncas * sizeof(int));
+int* w_occ = (int*) malloc(ncas * sizeof(int));
+int* x_occ = (int*) malloc(ncas * sizeof(int));
+int p1, p2, p; 
+int num;
+
+for (int i = 0; i <t1a_nonzero_size; i++){
+    aa = t1a_nonzero[i*4+0];
+    ia = t1a_nonzero[i*4+1];
+    str1a = t1a_nonzero[i*4+2];
+    str0a = t1a_nonzero[i*4+3];
+    str2occ(w_occa, stringsa[str0a], ncas);
+    str2occ(x_occa, stringsa[str1a], ncas);
+    
+    for (int j = 0; j < t1b_nonzero_size; j++){
+        ab = t1b_nonzero[j*4+0];
+        ib = t1b_nonzero[j*4+1];
+        str1b = t1b_nonzero[j*4+2];
+        str0b = t1b_nonzero[j*4+3];
+        str2occ(w_occb, stringsb[str0b], ncas);
+        str2occ(x_occb, stringsb[str1b], ncas);
+        
+        for (int k = 0; k < ncas; k++) {
+                x_occ[k] = x_occa[k] + x_occb[k];
+                w_occ[k] = w_occa[k] + w_occb[k];
+            }
+        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
+        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
+        
+        num = PO_nrows;
+        if (group!= NULL){
+            p1 = num_to_group(group,group_sizes,num_groups,p1);
+            p2 = num_to_group(group,group_sizes,num_groups,p2);
+            num = num_groups;
+        }
+
+        ci1[str1a * nb + str1b] += ci0[str0a * nb + str0b]
+        *erieff[p1 * num * ncas * ncas* ncas* ncas 
+        + p2 * ncas * ncas * ncas * ncas 
+        + aa * ncas * ncas * ncas 
+        + ia * ncas *ncas 
+        + ab * ncas + ib] 
+        * t1a[aa * ncas * na * na + ia * na * na  + str1a * na  + str0a]
+        * t1b[ab * ncas * nb * nb + ib * nb * nb + str1b * nb + str0b]
+        * TSc[p1 * num + p2] * 2.0;
+    }
+}
+
+for (int i =0; i <t2aa_nonzero_size; i++){
+    a1 = t2aa_nonzero[i*6+0];
+    i1 = t2aa_nonzero[i*6+1];
+    a2 = t2aa_nonzero[i*6+2];
+    i2 = t2aa_nonzero[i*6+3];
+    str1a = t2aa_nonzero[i*6+4];
+    str0a = t2aa_nonzero[i*6+5];
+    str2occ(w_occa, stringsa[str0a], ncas);
+    str2occ(x_occa, stringsa[str1a], ncas);
+    for (int str0b = 0; str0b < nb; str0b++){
+            str2occ(w_occb, stringsb[str0b], ncas);
+            for (int k = 0; k < ncas; k++) {
+                x_occ[k] = x_occa[k] + w_occb[k];
+                w_occ[k] = w_occa[k] + w_occb[k];
+            }
+        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
+        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
+        num = PO_nrows;
+        
+        if (group!= NULL){
+            p1 = num_to_group(group,group_sizes,num_groups,p1);
+            p2 = num_to_group(group,group_sizes,num_groups,p2);
+            num = num_groups;
+        }    
+        
+        ci1[str1a * nb + str0b] += ci0[str0a * nb + str0b] 
+        * erieff[p1 * num * ncas * ncas* ncas* ncas 
+        + p2 * ncas * ncas * ncas * ncas 
+        + a1 * ncas * ncas * ncas 
+        + i1 * ncas  * ncas 
+        + a2 * ncas 
+        + i2]
+        * t2aa[a1 * ncas * ncas * ncas * na * na + i1* ncas * ncas * na * na + a2 * ncas* na * na + i2* na * na + str1a * na + str0a]
+        * TSc[p1 * num + p2];
+    }
+}
+
+for (int i =0; i <t2bb_nonzero_size; i++){
+    a1 = t2bb_nonzero[i*6+0];
+    i1 = t2bb_nonzero[i*6+1];
+    a2 = t2bb_nonzero[i*6+2];
+    i2 = t2bb_nonzero[i*6+3];
+    str1b = t2bb_nonzero[i*6+4];
+    str0b = t2bb_nonzero[i*6+5];
+    str2occ(w_occb, stringsb[str0b], ncas);
+    str2occ(x_occb, stringsb[str1b], ncas);
+    for (int str0a = 0; str0a < na; str0a++){
+        str2occ(w_occa, stringsa[str0a], ncas);
+        
+
+            for (int k = 0; k < ncas; k++) {
+                x_occ[k] = w_occa[k] + x_occb[k];
+                w_occ[k] = w_occa[k] + w_occb[k];
+            }
+        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
+        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
+        num = PO_nrows;
+      
+        if (group!= NULL){
+            p1 = num_to_group(group,group_sizes,num_groups,p1);
+            p2 = num_to_group(group,group_sizes,num_groups,p2);
+            num = num_groups;
+        }    
+        ci1[str0a * nb + str1b] += ci0[str0a * nb + str0b] 
+        * erieff[p1 * num * ncas * ncas* ncas* ncas 
+        + p2 * ncas * ncas * ncas * ncas 
+        + a1 * ncas * ncas * ncas 
+        + i1 * ncas  * ncas 
+        + a2 * ncas 
+        + i2]
+        * t2bb[a1 * ncas * ncas * ncas * nb * nb + i1* ncas * ncas * nb * nb + a2 * ncas* nb * nb + i2* nb * nb + str1b * nb + str0b]
+        * TSc[p1 * num + p2];
+    }
+}
+for (int str0a = 0; str0a < na; str0a++) {
+        for (int str0b = 0; str0b < nb; str0b++) {
+
+            str2occ(w_occa, stringsa[str0a], ncas);
+            str2occ(w_occb, stringsb[str0b], ncas);
+
+            for (int k = 0; k < ncas; k++) {
+                w_occ[k] = w_occa[k] + w_occb[k];
+            }
+            p = find_matching_row(PO, w_occ, PO_nrows, ncas);
+            num = PO_nrows;
+            if (group!= NULL){
+            p = num_to_group(group,group_sizes,num_groups,p);
+            num = num_groups;
+            }
+            ci1[str0a * nb + str0b] += energy_core[p] * ci0[str0a * nb + str0b];
+}
+}
+free(w_occ);
+free(w_occa);
+free(w_occb);
+free(x_occ);
+free(x_occa);
+free(x_occb);
+}
\ No newline at end of file
diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
new file mode 100644
index 0000000..cc061c8
--- /dev/null
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -0,0 +1,819 @@
+#!/usr/bin/env python
+#
+# Copyright 2024 The PySCF Developers. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Jiseong Park <fark4308@snu.ac.kr> 
+# Edited by: Seunghoon Lee <seunghoonlee@snu.ac.kr>
+
+'''
+Spin Flip Non-Orthogonal Configuration Interaction (SF-NOCI)
+and Grouped-Bath Ansatz for SF-NOCI (SF-GNOCI)
+
+References:
+[1] Spin-flip non-orthogonal configuration interaction: a variational and 
+    almost black-box method for describing strongly correlated molecules
+    Nicholas J. Mayhall, Paul R. Horn, Eric J. Sundstrom and Martin Head-Gordon
+    Phys. Chem. Chem. Phys. 2014, 16, 22694
+[2] Efficient grouped-bath ansatz for spin-flip non-orthogonal configuration
+    interaction (SF-GNOCI) in transition-metal charge-transfer complexes 
+    Jiseong Park and Seunghoon Lee
+    J. Chem. Theory Comput. 2025 
+'''
+
+import numpy
+import ctypes
+import scipy.linalg
+import types
+from pyscf import fci
+from pyscf import ao2mo
+from pyscf import lib
+from pyscf import __config__
+from pyscf.lib import logger
+from pyscf.fci import cistring
+from pyscf.fci import direct_uhf
+from pyscf.fci.direct_spin1 import FCIBase, FCISolver
+
+libsf = lib.load_library("libsfnoci")
+
+PENALTY = getattr(__config__, 'sfnoci_SFNOCI_fix_spin_shift', 0.2)
+
+def find_matching_rows(matrix, target_row):
+    matching_rows = numpy.where((matrix == target_row).all(axis=1))[0]
+    return matching_rows
+ 
+def num_to_group(groups,number):
+    for i, group in enumerate(groups):
+        if number in group:
+            return i
+    return None
+
+def make_hdiag(h1e, eri, ncas, nelecas, PO, group, energy_core, opt=None):
+    if isinstance(nelecas, (int, numpy.integer)):
+        nelecb = nelecas//2
+        neleca = nelecas - nelecb
+    else:
+        neleca, nelecb = nelecas
+    occslista = cistring.gen_occslst(range(ncas), neleca)
+    occslistb = cistring.gen_occslst(range(ncas), nelecb)
+    eri = ao2mo.restore(1, eri, ncas)
+    diagj = numpy.einsum('iijj->ij', eri)
+    diagk = numpy.einsum('ijji->ij', eri)
+    hdiag = []
+    for aocc in occslista:
+        for bocc in occslistb:
+            occ = numpy.zeros(ncas)
+            for i in aocc:
+                occ[i] += 1
+            for i in bocc:
+                occ[i] +=1
+            p = find_matching_rows(PO,occ)
+            if group is not None:
+                p = num_to_group(group, p)
+            e1 = h1e[p,p,aocc,aocc].sum() + h1e[p,p,bocc,bocc].sum()
+            e2 = diagj[aocc][:,aocc].sum() + diagj[aocc][:,bocc].sum() \
+               + diagj[bocc][:,aocc].sum() + diagj[bocc][:,bocc].sum() \
+               - diagk[aocc][:,aocc].sum() - diagk[bocc][:,bocc].sum()
+            hdiag.append(e1 + e2*.5 + energy_core[p])
+    return numpy.array(hdiag)
+
+def absorb_h1e(h1e, eri, ncas, nelecas, fac=1):
+    '''Modify 2e Hamiltonian to include effective 1e Hamiltonian contribution
+
+    input : h1e : (ngroup, ngroup, ncas, ncas)
+            eri   : (ncas, ncas, ncas, ncas)
+
+    return : erieff : (ngroup,ngroup,ncas,ncas,ncas,ncas)
+    '''
+    if not isinstance(nelecas, (int, numpy.number)):
+        nelecas = sum(nelecas)
+    h2e = ao2mo.restore(1, eri.copy(), ncas)
+    p = h1e.shape[0]
+    f1e = h1e.copy()
+    f1e -= numpy.einsum('jiik->jk', h2e)[numpy.newaxis, numpy.newaxis, :, :] * .5
+    f1e = f1e * (1./(nelecas+1e-100))
+    erieff = numpy.zeros((p, p, ncas, ncas, ncas, ncas))
+    erieff += h2e[numpy.newaxis, numpy.newaxis, :, :, :, :]
+    for k in range(ncas):
+        erieff[:,:,k,k,:,:] += f1e
+        erieff[:,:,:,:,k,k] += f1e
+    return erieff * fac
+
+def gen_excitations(ncas, nelecas, na, nb, link_index=None):
+    if isinstance(nelecas, (int, numpy.integer)):
+        nelecb = nelecas//2
+        neleca = nelecas - nelecb
+    else:
+        neleca, nelecb = nelecas
+    if link_index is None:
+        link_indexa = cistring.gen_linkstr_index(range(ncas), neleca)
+        link_indexb = cistring.gen_linkstr_index(range(ncas), nelecb)
+    else:
+        link_indexa, link_indexb = link_index
+    t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na), dtype=numpy.int32)
+    t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb), dtype=numpy.int32)
+    t1a = numpy.zeros((ncas,ncas,na,na), dtype=numpy.int32)
+    t1b = numpy.zeros((ncas,ncas,nb,nb), dtype=numpy.int32) 
+    for str0a , taba in enumerate(link_indexa):
+          for a1, i1, str1a, signa1 in link_indexa[str0a]:
+              t1a[a1,i1,str1a,str0a] += signa1 
+              for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+                  t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+    for str0b , tabb in enumerate(link_indexb):
+          for a1, i1, str1b, signb1 in link_indexb[str0b]:
+              t1b[a1,i1,str1b,str0b] += signb1
+              for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+                  t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+    return t1a, t1b, t2aa, t2bb
+
+def gen_nonzero_excitations(t1a, t1b, t2aa, t2bb):
+    t1a_nonzero = numpy.array(numpy.array(numpy.nonzero(t1a)).T, order = 'C', dtype = numpy.int32)
+    t1b_nonzero = numpy.array(numpy.array(numpy.nonzero(t1b)).T, order = 'C', dtype = numpy.int32)
+    t2aa_nonzero = numpy.array(numpy.array(numpy.nonzero(t2aa)).T, order = 'C', dtype = numpy.int32)
+    t2bb_nonzero = numpy.array(numpy.array(numpy.nonzero(t2bb)).T, order = 'C', dtype = numpy.int32)
+    return t1a_nonzero, t1b_nonzero, t2aa_nonzero, t2bb_nonzero
+
+def python_list_to_c_array(python_list):
+    if python_list is None: return ctypes.c_void_p(None), ctypes.c_void_p(None), 0 
+    else:
+        num_groups = len(python_list)
+        flat_list = sum(python_list, [])
+        flat_list = (ctypes.c_int *len(flat_list))(*flat_list)
+        group_sizes = (ctypes.c_int * num_groups)()  
+        for i, group in enumerate(python_list):
+            group_size = len(group)
+            group_sizes[i] = group_size  
+        return flat_list, group_sizes, num_groups
+ 
+def contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core,
+               link_index=None, ts=None, t_nonzero=None):
+    '''Compute H|CI>
+    '''
+    if isinstance(nelecas, (int, numpy.integer)):
+        nelecb = nelecas//2
+        neleca = nelecas - nelecb
+    else:
+        neleca, nelecb = nelecas
+    
+    na = cistring.num_strings(ncas,neleca)
+    nb = cistring.num_strings(ncas,nelecb)
+
+    if ts is None:
+        if link_index is None:
+            link_indexa = cistring.gen_linkstr_index(range(ncas), neleca)
+            link_indexb = cistring.gen_linkstr_index(range(ncas), nelecb)
+            link_index = (link_indexa, link_indexb)
+        else:
+            link_indexa, link_indexb = link_index
+        t1a, t1b, t2aa, t2bb= gen_excitations(ncas, nelecas,na,nb,link_index)
+    else:
+        t1a, t1b, t2aa, t2bb = ts
+    if t_nonzero is None:
+        t1a_nonzero, t1b_nonzero, t2aa_nonzero, t2bb_nonzero = \
+            gen_nonzero_excitations(t1a, t1b, t2aa, t2bb)
+    else:
+        t1a_nonzero, t1b_nonzero, t2aa_nonzero, t2bb_nonzero = t_nonzero
+    civec = numpy.asarray(civec, order = 'C')
+    cinew = numpy.zeros_like(civec)
+    erieff = numpy.asarray(erieff, order = 'C', dtype= numpy.float64)
+    PO = numpy.asarray(PO, order = 'C', dtype=numpy.int32)
+    PO_nrows = PO.shape[0]
+    cgroup, group_sizes, num_groups = python_list_to_c_array(group)
+    stringsa = cistring.make_strings(range(ncas),neleca)
+    stringsb = cistring.make_strings(range(ncas),nelecb)
+    # t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na), dtype=numpy.int32)
+    # t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb),dtype=numpy.int32)
+    # t1a = numpy.zeros((ncas,ncas,na,na),dtype=numpy.int32)
+    # t1b = numpy.zeros((ncas,ncas,nb,nb),dtype=numpy.int32) 
+    # for str0a , taba in enumerate(link_indexa):
+    #       for a1, i1, str1a, signa1 in link_indexa[str0a]:
+    #           t1a[a1,i1,str1a,str0a] += signa1 
+    #           for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+    #               t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+    # for str0b , tabb in enumerate(link_indexb):
+    #       for a1, i1, str1b, signb1 in link_indexb[str0b]:
+    #           t1b[a1,i1,str1b,str0b] += signb1
+    #           for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+    #               t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+    # t1a_nonzero = numpy.array(numpy.array(numpy.nonzero(t1a)).T, order = 'C', dtype = numpy.int32)
+    # t1b_nonzero = numpy.array(numpy.array(numpy.nonzero(t1b)).T, order = 'C', dtype = numpy.int32)
+    # t2aa_nonzero = numpy.array(numpy.array(numpy.nonzero(t2aa)).T, order = 'C', dtype = numpy.int32)
+    # t2bb_nonzero = numpy.array(numpy.array(numpy.nonzero(t2bb)).T, order = 'C', dtype = numpy.int32)
+    t1ann = t1a_nonzero.shape[0]
+    t1bnn = t1b_nonzero.shape[0]
+    t2aann = t2aa_nonzero.shape[0]
+    t2bbnn = t2bb_nonzero.shape[0]
+    TSc = numpy.asarray(TSc, order = 'C', dtype=numpy.float64)
+    energy_core = numpy.asarray(energy_core, order = 'C', dtype=numpy.float64)
+
+    libsf.SFNOCIcontract_H_spin1(erieff.ctypes.data_as(ctypes.c_void_p),
+         civec.ctypes.data_as(ctypes.c_void_p),
+         cinew.ctypes.data_as(ctypes.c_void_p),
+         ctypes.c_int(ncas),
+         ctypes.c_int(neleca), ctypes.c_int(nelecb),
+         PO.ctypes.data_as(ctypes.c_void_p),ctypes.c_int(PO_nrows),
+         ctypes.c_int(na), stringsa.ctypes.data_as(ctypes.c_void_p), 
+         ctypes.c_int(nb), stringsb.ctypes.data_as(ctypes.c_void_p),
+         cgroup, group_sizes, ctypes.c_int(num_groups),
+         t1a.ctypes.data_as(ctypes.c_void_p), 
+         t1a_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t1ann),
+         t1b.ctypes.data_as(ctypes.c_void_p), 
+         t1b_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t1bnn),
+         t2aa.ctypes.data_as(ctypes.c_void_p), 
+         t2aa_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2aann),
+         t2bb.ctypes.data_as(ctypes.c_void_p), 
+         t2bb_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2bbnn),
+         TSc.ctypes.data_as(ctypes.c_void_p), energy_core.ctypes.data_as(ctypes.c_void_p))
+    return cinew
+
+def contract_H_slow(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, link_index=None):
+    '''Compute H|CI>
+    '''
+    if isinstance(nelecas, (int, numpy.integer)):
+        nelecb = nelecas//2
+        neleca = nelecas - nelecb
+    else:
+        neleca, nelecb = nelecas
+    if link_index is None:
+        link_indexa = cistring.gen_linkstr_index(range(ncas), neleca)
+        link_indexb = cistring.gen_linkstr_index(range(ncas), nelecb)
+    else:
+        link_indexa, link_indexb = link_index
+    na = cistring.num_strings(ncas,neleca)
+    nb = cistring.num_strings(ncas,nelecb)
+    civec = civec.reshape(na,nb)
+    cinew = numpy.zeros((na,nb))
+    stringsa = cistring.make_strings(range(ncas),neleca)
+    stringsb = cistring.make_strings(range(ncas),nelecb)
+    t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na))
+    t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb))
+    t1a = numpy.zeros((ncas,ncas,na,na))
+    t1b = numpy.zeros((ncas,ncas,nb,nb)) 
+    for str0a , taba in enumerate(link_indexa):
+          for a1, i1, str1a, signa1 in link_indexa[str0a]:
+              t1a[a1,i1,str1a,str0a] += signa1 
+              for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+                  t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+    for str0b , tabb in enumerate(link_indexb):
+          for a1, i1, str1b, signb1 in link_indexb[str0b]:
+              t1b[a1,i1,str1b,str0b] += signb1
+              for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+                  t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+    t1a_nonzero = numpy.array(numpy.nonzero(t1a)).T
+    t1b_nonzero = numpy.array(numpy.nonzero(t1b)).T
+    t2aa_nonzero = numpy.array(numpy.nonzero(t2aa)).T
+    t2bb_nonzero = numpy.array(numpy.nonzero(t2bb)).T
+    for aa, ia, str1a, str0a in t1a_nonzero:
+        for ab, ib, str1b, str0b in t1b_nonzero:
+            w_occa = str2occ(stringsa[str0a],ncas)
+            w_occb = str2occ(stringsb[str0b],ncas)
+            x_occa = str2occ(stringsa[str1a],ncas)
+            x_occb = str2occ(stringsb[str1b],ncas)
+            x_occ = x_occa + x_occb
+            w_occ = w_occa + w_occb
+            p1 = find_matching_rows(PO,x_occ)[0]
+            p2 = find_matching_rows(PO,w_occ)[0]
+            if group is not None:
+                p1 = num_to_group(group,p1)
+                p2 = num_to_group(group,p2)
+           
+            cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] *t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] * TSc[p1,p2] *2
+    for a1, i1, a2,i2, str1a, str0a in t2aa_nonzero:
+        for str0b, stringb in enumerate(stringsb):
+            w_occa = str2occ(stringsa[str0a],ncas)
+            w_occb = str2occ(stringsb[str0b],ncas)
+            x_occa = str2occ(stringsa[str1a],ncas)
+            x_occ = x_occa + w_occb
+            w_occ = w_occa + w_occb
+            p1 = find_matching_rows(PO,x_occ)[0]
+            p2 = find_matching_rows(PO,w_occ)[0]
+            if group is not None:
+                p1 = num_to_group(group,p1)
+                p2 = num_to_group(group,p2)
+            cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] *t2aa[a1,i1,a2,i2,str1a,str0a] * TSc[p1,p2]
+    for a1, i1, a2,i2, str1b, str0b in t2bb_nonzero:
+        for str0a, stringa in enumerate(stringsa):
+            w_occa = str2occ(stringsa[str0a],ncas)
+            w_occb = str2occ(stringsb[str0b],ncas)
+            x_occb = str2occ(stringsb[str1b],ncas)
+            x_occ = w_occa + x_occb
+            w_occ = w_occa + w_occb
+            p1 = find_matching_rows(PO,x_occ)[0]
+            p2 = find_matching_rows(PO,w_occ)[0]
+            if group is not None:
+                p1 = num_to_group(group,p1)
+                p2 = num_to_group(group,p2)
+            cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2]* t2bb[a1,i1,a2,i2,str1b,str0b] * TSc[p1,p2]
+    for str0a, stringa in enumerate(stringsa):
+        for str0b, stringb in enumerate(stringsb):
+            w_occa = str2occ(stringsa[str0a],ncas)
+            w_occb = str2occ(stringsb[str0b],ncas)
+            w_occ = w_occa + w_occb
+            p = find_matching_rows(PO,w_occ)[0]
+            if group is not None:
+                p = num_to_group(group,p)
+            cinew[str0a,str0b] += energy_core[p] * civec[str0a,str0b]
+    cinew.reshape(-1)
+    return cinew
+
+def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, PO, group, TSc,
+                  ci0=None, link_index=None, tol=None, lindep=None, 
+                  max_cycle=None, max_space=None, nroots=None, 
+                  davidson_only=None, pspace_size=None, hop=None,
+                  max_memory=None, verbose=None, ecore=0, **kwargs):
+    '''
+    Args:
+        h1e: ndarray
+            1-electron Hamiltonian
+        eri: ndarray
+            2-electron integrals in chemist's notation
+        norb: int
+            Number of orbitals
+        nelec: int or (int, int)
+            Number of electrons of the system
+
+    Kwargs:
+        ci0: ndarray
+            Initial guess
+        link_index: ndarray
+            A lookup table to cache the addresses of CI determinants in
+            wave-function vector
+        tol: float
+            Convergence tolerance
+        lindep: float
+            Linear dependence threshold
+        max_cycle: int
+            Max. iterations for diagonalization
+        max_space: int
+            Max. trial vectors to store for sub-space diagonalization method
+        nroots: int
+            Number of states to solve
+        davidson_only: bool
+            Whether to call subspace diagonalization (davidson solver) or do a
+            full diagonalization (lapack eigh) for small systems
+        pspace_size: int
+            Number of determinants as the threshold of "small systems",
+        hop: function(c) => array_like_c
+            Function to use for the Hamiltonian multiplication with trial vector
+
+    Note: davidson solver requires more arguments. For the parameters not
+    dispatched, they can be passed to davidson solver via the extra keyword
+    arguments **kwargs
+    '''
+    if nroots is None: nroots = sfnoci.nroots
+    if davidson_only is None: davidson_only = sfnoci.davidson_only
+    if pspace_size is None: pspace_size = sfnoci.pspace_size
+    if max_memory is None:
+        max_memory = sfnoci.max_memory - lib.current_memory()[0]
+    log = logger.new_logger(sfnoci, verbose)
+    nelec = nelecas
+    assert (0 <= nelec[0] <= ncas and 0 <= nelec[1] <= ncas)
+    hdiag = sfnoci.make_hdiag(h1e, eri, ncas, nelec, PO, group, ecore).ravel()
+    num_dets = hdiag.size
+    civec_size = num_dets
+    precond = sfnoci.make_precond(hdiag)
+    addr = [0]
+    erieff = sfnoci.absorb_h1e(h1e, eri, ncas, nelec, .5)
+    na = cistring.num_strings(ncas, nelec[0])
+    nb = cistring.num_strings(ncas, nelec[1])
+    if link_index is None:
+            link_indexa = cistring.gen_linkstr_index(range(ncas), nelec[0])
+            link_indexb = cistring.gen_linkstr_index(range(ncas), nelec[1])
+            link_index = (link_indexa, link_indexb)
+    else:
+        link_indexa, link_indexb = link_index
+    
+    ts = gen_excitations(ncas, nelecas, na, nb, link_index)
+    t_nonzero = gen_nonzero_excitations(ts[0], ts[1], ts[2], ts[3])
+    if hop is None:
+        cpu0 = [logger.process_clock(), logger.perf_counter()]
+        def hop(c):
+            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, PO, group, 
+                                   TSc, ecore,link_index, ts, t_nonzero)
+            cpu0[:] = log.timer_debug1('contract_H', *cpu0)
+            return hc.ravel()
+    def init_guess():
+        if callable(getattr(sfnoci, 'get_init_guess', None)):
+            return sfnoci.get_init_guess(ncas, nelecas, nroots, hdiag)
+        else:
+            x0 = []
+            for i in range(min(len(addr), nroots)):
+                x = numpy.zeros(civec_size)
+                x[addr[i]] = 1
+                x0.append(x)
+            return x0
+    if ci0 is None:
+        ci0 = init_guess
+    if tol is None: tol = sfnoci.conv_tol
+    if lindep is None: lindep = sfnoci.lindep
+    if max_cycle is None: max_cycle = sfnoci.max_cycle
+    if max_space is None: max_space = sfnoci.max_space
+    with lib.with_omp_threads(None):
+        e, c = sfnoci.eig(hop, ci0, precond, tol=tol, lindep=lindep,
+                       max_cycle=max_cycle, max_space=max_space, nroots=nroots,
+                       max_memory=max_memory, verbose=log, follow_state=True,
+                       tol_residual=None, **kwargs)
+    return e, c
+
+def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+    N = mo_coeff.shape[0]
+    mo_cas = mo_coeff[:,ncore:ncore+ncas]
+    stringsa = cistring.make_strings(range(ncas),nelecas[0])
+    stringsb = cistring.make_strings(range(ncas),nelecas[1])
+    link_indexa = cistring.gen_linkstr_index(range(ncas),nelecas[0])
+    link_indexb = cistring.gen_linkstr_index(range(ncas),nelecas[1])
+    na = cistring.num_strings(ncas,nelecas[0])
+    nb = cistring.num_strings(ncas,nelecas[1])
+    rdm1c = numpy.zeros((N,N))
+    ci = ci.reshape(na,nb)
+    for str0a, strsa in enumerate(stringsa):
+        for str0b, strsb in enumerate(stringsb):
+            w_occa = str2occ(strsa,ncas)
+            w_occb = str2occ(strsb,ncas)
+            w_occ = w_occa + w_occb
+            p = find_matching_rows(PO,w_occ)[0]
+            if group is not None: 
+                     p = num_to_group(group,p)
+            rdm1c += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b]*W[p,p]
+  
+    rdm1asmoa = numpy.zeros((ncas,ncas))
+    rdm1asmob = numpy.zeros((ncas,ncas))    
+    for str0a , taba in enumerate(link_indexa):
+        for aa, ia, str1a, signa in link_indexa[str0a]:
+            for str0b, strsb in enumerate(stringsb):
+                w_occa = str2occ(stringsa[str0a],ncas)
+                w_occb = str2occ(strsb,ncas)
+                x_occa = str2occ(stringsa[str1a],ncas)
+                x_occ = x_occa + w_occb
+                w_occ = w_occa + w_occb   
+                p1 = find_matching_rows(PO,x_occ)[0]
+                p2 = find_matching_rows(PO,w_occ)[0]
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                     p2 = num_to_group(group,p2) 
+                rdm1asmoa[aa,ia] += signa * numpy.conjugate(ci[str1a,str0b]) * ci[str0a,str0b] * TSc[p1,p2]
+    for str0b, tabb in enumerate(link_indexb):
+        for ab, ib, str1b, signb in link_indexb[str0b]:
+            for str0a, strsa in enumerate(stringsa):
+                w_occa = str2occ(strsa,ncas)
+                w_occb = str2occ(stringsb[str0b],ncas)
+                x_occb = str2occ(stringsb[str1b],ncas)
+                x_occ = w_occa + x_occb
+                w_occ = w_occa + w_occb  
+                p1 = find_matching_rows(PO,x_occ)[0]
+                p2 = find_matching_rows(PO,w_occ)[0] 
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                     p2 = num_to_group(group,p2)       
+                rdm1asmob[ab,ib] += signb * numpy.conjugate(ci[str0a,str1b]) * ci[str0a,str0b] * TSc[p1,p2]
+    rdm1a = lib.einsum('ia,ab,jb -> ij', numpy.conjugate(mo_cas),rdm1asmoa,mo_cas)
+    rdm1b = lib.einsum('ia,ab,jb-> ij', numpy.conjugate(mo_cas),rdm1asmob,mo_cas)
+    rdm1a += rdm1c
+    rdm1b += rdm1c
+    
+    return rdm1a, rdm1b
+
+def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+    rdm1a, rdm1b = make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+    return rdm1a + rdm1b
+
+def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+    mo_cas = mo_coeff[:,ncore:ncore+ncas]
+    N = mo_coeff.shape[0]
+    rdm2aa = numpy.zeros((N,N,N,N))
+    rdm2ab = numpy.zeros((N,N,N,N))
+    rdm2ba = numpy.zeros((N,N,N,N))
+    rdm2bb = numpy.zeros((N,N,N,N))
+    stringsa = cistring.make_strings(range(ncas),nelecas[0])
+    stringsb = cistring.make_strings(range(ncas),nelecas[1])
+    link_indexa = cistring.gen_linkstr_index(range(ncas),nelecas[0])
+    link_indexb = cistring.gen_linkstr_index(range(ncas),nelecas[1])
+    na = cistring.num_strings(ncas,nelecas[0])
+    nb = cistring.num_strings(ncas,nelecas[1])
+    ci = ci.reshape(na,nb)
+    t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na))
+    t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb))
+    t1a = numpy.zeros((ncas,ncas,na,na))
+    t1b = numpy.zeros((ncas,ncas,nb,nb)) 
+    
+    rdm2aaac = numpy.zeros((ncas,ncas,ncas,ncas))
+    rdm2abac = numpy.zeros((ncas,ncas,ncas,ncas))
+    rdm2baac = numpy.zeros((ncas,ncas,ncas,ncas))
+    rdm2bbac = numpy.zeros((ncas,ncas,ncas,ncas))
+    for str0a , taba in enumerate(link_indexa):
+        for a1, i1, str1a, signa1 in link_indexa[str0a]:
+            t1a[a1,i1,str1a,str0a] += signa1 
+            for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+                t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+    for str0b , tabb in enumerate(link_indexb):
+        for a1, i1, str1b, signb1 in link_indexb[str0b]:
+            t1b[a1,i1,str1b,str0b] += signb1
+            for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+                t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+    for str0a, strs0a in enumerate(stringsa):
+        for str0b, strs0b in enumerate(stringsb):
+            w_occa = str2occ(strs0a,ncas)
+            w_occb = str2occ(strs0b,ncas)
+            w_occ = w_occa + w_occb
+            p2 = find_matching_rows(PO,w_occ)[0]
+            if group is not None: 
+               p2 = num_to_group(group,p2) 
+            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', W[p2,p2,:,:],W[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])) 
+            rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])
+            rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])
+            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', W[p2,p2,:,:],W[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:]))  
+            for str1a, strs1a in enumerate(stringsa):
+                x_occa = str2occ(strs1a,ncas)
+                x_occ = x_occa + w_occb
+                p1 = find_matching_rows(PO,x_occ)[0]
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*TSc[p1,p2]
+                for k in range(ncas):
+                    rdm2aaac[:,k,k,:] -= numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t1a[:,:,str1a,str0a]*TSc[p1,p2]
+            for str1b, strs1b in enumerate(stringsb):
+                x_occb = str2occ(strs1b,ncas)
+                x_occ = w_occa + x_occb
+                p1 = find_matching_rows(PO,x_occ)[0]
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                rdm2bbac[:,:,:,:] += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]*t2bb[:,:,:,:,str1b,str0b]*TSc[p1,p2]
+                for k in range(ncas):
+                    rdm2bbac[:,k,k,:] -= numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b] * t1b[:,:,str1b,str0b]*TSc[p1,p2]
+            for str1a, strs1a in enumerate(stringsa):
+                for str1b, strs1b in enumerate(stringsb):
+                    w_occa = str2occ(strs0a,ncas)
+                    w_occb = str2occ(strs0b,ncas)
+                    x_occa = str2occ(strs1a,ncas)
+                    x_occb = str2occ(strs1b,ncas)
+                    w_occ = w_occa + w_occb
+                    x_occ = x_occa + x_occb
+                    p1 = find_matching_rows(PO,x_occ)[0] 
+                    p2 = find_matching_rows(PO,w_occ)[0]
+                    if group is not None: 
+                       p1 = num_to_group(group,p1)
+                       p2 = num_to_group(group,p2)  
+                    rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*TSc[p1,p2]
+                    rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*TSc[p1,p2]
+    
+    rdm2aa += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2aaac)
+    rdm2ab += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2abac)
+    rdm2ba += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2baac)
+    rdm2bb += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2bbac)
+    t1aao = lib.einsum('ia,jb,abcd -> ijcd', mo_cas, mo_cas, t1a)
+    t1bao = lib.einsum('ia,jb,abcd -> ijcd', mo_cas, mo_cas, t1b)
+    
+    
+    for str0a, taba in enumerate(link_indexa):
+        for str1a in numpy.unique(link_indexa[str0a][:,2]):
+            for str0b, strsb in enumerate(stringsb):
+                w_occa = str2occ(stringsa[str0a],ncas)
+                w_occb = str2occ(strsb,ncas)
+                x_occa = str2occ(stringsa[str1a],ncas)
+                x_occ = x_occa + w_occb
+                w_occ = w_occa + w_occb   
+                p1 = find_matching_rows(PO,x_occ)[0]
+                p2 = find_matching_rows(PO,w_occ)[0]
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                     p2 = num_to_group(group,p2) 
+                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+  
+    for str0b, tabb in enumerate(link_indexb):
+        for str1b in numpy.unique(link_indexb[str0b][:,2]):
+            for str0a, strsa, in enumerate(stringsa):
+                w_occa = str2occ(strsa,ncas)
+                w_occb = str2occ(stringsb[str0b],ncas)
+                x_occb = str2occ(stringsb[str1b],ncas)
+                x_occ = w_occa + x_occb
+                w_occ = w_occa + w_occb  
+                p1 = find_matching_rows(PO,x_occ)[0]
+                p2 = find_matching_rows(PO,w_occ)[0]
+                if group is not None: 
+                     p1 = num_to_group(group,p1)
+                     p2 = num_to_group(group,p2)  
+                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+    
+    return rdm2aa, rdm2ab, rdm2ba, rdm2bb
+
+def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+    rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
+        make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+    return rdm2aa + rdm2ab + rdm2ba + rdm2bb
+
+def fix_spin(fciobj, shift=PENALTY, ss=None, **kwargs):
+    r'''If FCI solver cannot stay on spin eigenfunction, this function can
+    add a shift to the states which have wrong spin.
+
+    .. math::
+
+        (H + shift*S^2) |\Psi\rangle = E |\Psi\rangle
+
+    Args:
+        fciobj : An instance of :class:`FCISolver`
+
+    Kwargs:
+        shift : float
+            Level shift for states which have different spin
+        ss : number
+            S^2 expection value == s*(s+1)
+
+    Returns
+            A modified FCI object based on fciobj.
+    '''
+    if isinstance(fciobj, direct_uhf.FCISolver):
+        raise NotImplementedError
+
+    if isinstance (fciobj, types.ModuleType):
+        raise DeprecationWarning('fix_spin should be applied on FCI object only')
+
+    if 'ss_value' in kwargs:
+        sys.stderr.write('fix_spin_: kwarg "ss_value" will be removed in future release. '
+                         'It was replaced by "ss"\n')
+        ss_value = kwargs['ss_value']
+    else:
+        ss_value = ss
+
+    if isinstance (fciobj, SpinPenaltySFNOCISolver):
+        # recursion avoidance
+        fciobj.ss_penalty = shift
+        fciobj.ss_value = ss_value
+        return fciobj
+
+    return lib.set_class(SpinPenaltySFNOCISolver(fciobj, shift, ss_value),
+                         (SpinPenaltySFNOCISolver, fciobj.__class__))
+
+def fix_spin_(fciobj, shift=.1, ss=None):
+    sp_fci = fix_spin(fciobj, shift, ss)
+    fciobj.__class__ = sp_fci.__class__
+    fciobj.__dict__ = sp_fci.__dict__
+    return fciobj
+
+
+class SFNOCISolver(FCISolver):
+  '''SF-NOCI
+  '''
+  def make_hdiag(self, h1e, eri, ncas, nelecas, PO, group, energy_core, opt=None):
+      return make_hdiag(h1e, eri, ncas, nelecas, PO, group, energy_core, opt)
+  
+  def make_precond(self, hdiag, level_shift=0):
+      return lib.make_diag_precond(hdiag, level_shift)
+
+  def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
+      return absorb_h1e(h1e, eri, ncas, nelecas, fac)
+
+  def contract_H(self, erieff, civec, ncas, nelecas, PO, group, TSc, 
+                 energy_core, link_index=None, ts=None, t_nonzero=None):
+      return contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, 
+                        energy_core ,link_index, ts, t_nonzero)
+
+  def get_init_guess(self, ncas, nelecas, nroots, hdiag):
+      return fci.direct_spin1.get_init_guess(ncas, nelecas, nroots, hdiag)
+
+  def eig(self, op, x0=None, precond=None, **kwargs):
+        if isinstance(op, numpy.ndarray):
+            self.converged = True
+            return scipy.linalg.eigh(op)
+        
+        self.converged, e, ci = \
+                lib.davidson1(lambda xs: [op(x) for x in xs],
+                              x0, precond, lessio=False, **kwargs)
+        if kwargs['nroots'] == 1:
+            self.converged = self.converged[0]
+            e = e[0]
+            ci = ci[0]
+        return e, ci
+
+  def kernel(self, h1e, eri, norb, nelec, PO, group, TSc, ci0=None,
+             tol=None, lindep=None, max_cycle=None, max_space=None,
+             nroots=None, davidson_only=None, pspace_size=None,
+             orbsym=None, wfnsym=None, ecore=0, **kwargs):
+      if nroots is None: nroots = self.nroots
+      if self.verbose >= logger.WARN:
+          self.check_sanity()
+      assert self.spin is None or self.spin == 0
+      self.norb = norb
+      self.nelec = nelec
+      #link_index = fci.direct_spin1._unpack(norb, nelec, None)
+      link_indexa = cistring.gen_linkstr_index(range(norb), nelec[0])
+      link_indexb = cistring.gen_linkstr_index(range(norb), nelec[1])
+      link_index = (link_indexa, link_indexb)
+
+      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, PO, group, TSc, ci0,
+                           link_index, tol, lindep, max_cycle, max_space, nroots,
+                           davidson_only, pspace_size, ecore=ecore, **kwargs)
+      self.eci = e
+
+      return self.eci, self.ci
+
+  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+
+  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+
+  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+
+  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+ 
+  def contract_ss(self, civec, ncas=None, nelecas=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      return spin_op.contract_ss(civec,ncas,nelecas)
+
+  def fix_spin_(self, shift=PENALTY, ss = None):
+       '''Use level shift to control FCI solver spin.
+
+        .. math::
+
+            (H + shift*S^2) |\Psi\rangle = E |\Psi\rangle
+
+        Kwargs:
+            shift : float
+                Energy penalty for states which have wrong spin
+            ss : number
+                S^2 expection value == s*(s+1)
+        '''
+       fix_spin_(self, shift, ss)
+       return self
+  fix_spin = fix_spin_
+
+  def spin_square(self, civec, ncas = None, nelecas = None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      return spin_op.spin_square0(civec, ncas, nelecas)
+
+#  def S2_list(self, mo_coeff = None, ci = None):
+#      if ci is None: ci = self.ci
+#      cin = ci.shape[1]
+#      S2_list = numpy.zeros(cin) 
+#      for i in range(cin):
+#          civec = ci[:,i]
+#          S2_list[i] = self.spin_square(civec)[0]
+#      return S2_list    
+
+class SpinPenaltySFNOCISolver:
+    __name_mixin__ = 'SpinPenalty'
+    _keys = {'ss_value', 'ss_penalty', 'base'}
+
+    def __init__(self, sfnocibase, shift, ss_value):
+        self.base = sfnocibase.copy()
+        self.__dict__.update (sfnocibase.__dict__)
+        self.ss_value = ss_value
+        self.ss_penalty = shift
+        self.davidson_only = self.base.davidson_only = True
+
+    def undo_fix_spin(self):
+        obj = lib.view(self, lib.drop_class(self.__class__, SpinPenaltySFNOCISolver))
+        del obj.base
+        del obj.ss_value
+        del obj.ss_penalty
+        return obj
+    
+    def base_contract_H(self, *args, **kwargs):
+        return super().contract_H(*args, **kwargs)
+    
+    def contract_H(self, erieff, civec, ncas, nelecas, PO, group, TSc, 
+                   energy_core, link_index=None, ts=None, t_nonzero=None, **kwargs):
+        if isinstance(nelecas, (int, numpy.number)):
+            sz = (nelecas % 2) * .5
+        else:
+            sz = abs(nelecas[0]-nelecas[1]) * .5
+        if self.ss_value is None:
+            ss = sz*(sz+1)
+        else:
+            ss = self.ss_value
+        if ss < sz*(sz+1)+.1:
+            # (S^2-ss)|Psi> to shift state other than the lowest state
+            ci1 = self.contract_ss(civec, ncas, nelecas).reshape(civec.shape)
+            ci1 -= ss * civec
+        else:
+            # (S^2-ss)^2|Psi> to shift states except the given spin.
+            # It still relies on the quality of initial guess
+            tmp = self.contract_ss(civec, ncas, nelecas).reshape(civec.shape)
+            tmp -= ss * civec
+            ci1 = -ss * tmp
+            ci1 += self.contract_ss(tmp, ncas, nelecas).reshape(civec.shape)
+            tmp = None
+        ci1 *= self.ss_penalty
+        ci0 = super().contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, link_index, ts, t_nonzero, **kwargs)
+        ci1 += ci0.reshape(civec.shape)
+        return ci1
+ 
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
new file mode 100644
index 0000000..f4cde4f
--- /dev/null
+++ b/pyscf/sfnoci/sfnoci.py
@@ -0,0 +1,1165 @@
+#!/usr/bin/env python
+#
+# Copyright 2024 The PySCF Developers. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Author: Jiseong Park <fark4308@snu.ac.kr> 
+# Edited by: Seunghoon Lee <seunghoonlee@snu.ac.kr>
+
+'''
+Spin Flip Non-Orthogonal Configuration Interaction (SF-NOCI)
+and Grouped-Bath Ansatz for SF-NOCI (SF-GNOCI)
+
+References:
+[1] Spin-flip non-orthogonal configuration interaction: a variational and 
+    almost black-box method for describing strongly correlated molecules
+    Nicholas J. Mayhall, Paul R. Horn, Eric J. Sundstrom and Martin Head-Gordon
+    Phys. Chem. Chem. Phys. 2014, 16, 22694
+[2] Efficient grouped-bath ansatz for spin-flip non-orthogonal configuration
+    interaction (SF-GNOCI) in transition-metal charge-transfer complexes 
+    Jiseong Park and Seunghoon Lee
+    J. Chem. Theory Comput. 2025 
+'''
+
+import sys
+
+import numpy
+from functools import reduce
+from pyscf import lib
+from pyscf.lib import logger
+from pyscf import __config__
+from itertools import product
+from pyscf.mcscf.casci import CASBase, CASCI
+from pyscf.fci import spin_op
+from pyscf.sfnoci.direct_sfnoci import SFNOCISolver
+#from pyscf.fci import cistring
+#from scipy.linalg import eigh as gen_eig
+#from pyscf.fci import fci_slow
+
+WITH_META_LOWDIN = getattr(__config__, 'scf_analyze_with_meta_lowdin', True)
+PRE_ORTH_METHOD = getattr(__config__, 'scf_analyze_pre_orth_method', 'ANO')
+MO_BASE = getattr(__config__, 'MO_BASE', 1)
+TIGHT_GRAD_CONV_TOL = getattr(__config__, 'scf_hf_kernel_tight_grad_conv_tol', True)
+
+def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
+    '''SFNOCI solver
+
+    Args:
+        sfnoci: SFNOCI object
+
+        mo_coeff : ndarray
+            orbitals to construct active space Hamiltonian
+        ci0 : ndarray or custom types
+            FCI sovler initial guess. For external FCI-like solvers, it can be
+            overloaded different data type. For example, in the state-average
+            FCI solver, ci0 is a list of ndarray. In other solvers such as
+            DMRGCI solver, SHCI solver, ci0 are custom types.
+
+    kwargs:
+        envs: dict
+            The variable envs is created (for PR 807) to passes MCSCF runtime
+            environment variables to SHCI solver. For solvers which do not
+            need this parameter, a kwargs should be created in kernel method
+            and "envs" pop in kernel function
+    '''
+    if mo_coeff is None: mo_coeff = sfnoci.mo_coeff
+    if ci0 is None: ci0 = sfnoci.ci
+
+    log = logger.new_logger(sfnoci, verbose)
+    t0 = (logger.process_clock(), logger.perf_counter())
+    log.debug('Start SFNOCI')
+
+    ncas = sfnoci.ncas
+    nelecas = sfnoci.nelecas
+
+    # FASSCF
+    mo_list, po_list, group = sfnoci.optimize_mo(mo_coeff)
+    t1 = log.timer('FASSCF', *t0)
+
+    # SVD and core density matrix 
+    if group is None :
+        dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, po_list)
+    else:
+        dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, group)
+    t1 = log.timer('SVD and core density matrix', *t1)
+
+    # 1e
+    dmet_act_list = sfnoci.get_active_dm(mo_coeff)
+    h1e, energy_core = sfnoci.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
+    t1 = log.timer('effective 1e hamiltonians and core energies', *t1)
+
+    # 2e
+    eri = sfnoci.get_h2eff(mo_coeff)
+    t1 = log.timer('effective 2e hamiltonian', *t1)
+
+    # FCI
+    max_memory = max(400, sfnoci.max_memory-lib.current_memory()[0])
+    e_tot, fcivec = sfnoci.fcisolver.kernel(h1e, eri, ncas, nelecas,
+                                            po_list, group, ov_list, ecore=energy_core,
+                                            ci0=ci0, verbose=log,
+                                            max_memory=max_memory)
+
+#    e_tot, fcivec = kernel_SFNOCI(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list,
+#                         energy_core, ci0, link_index=None, tol = tol, 
+#                         lindep= lindep, max_cycle=max_cycle, max_space=max_space,
+#                         nroots=nroots, davidson_only= davidson_only,
+#                         pspace_size= pspace_size, ecore= ecore, verbose=verbose)
+
+    log.timer('SFNOCI solver', *t1)
+    log.timer('All SFNOCI process', *t0)
+
+    if isinstance(e_tot, (float, numpy.float64)):
+        e_cas = e_tot - energy_core
+    else:
+        e_cas = [e - energy_core for e in e_tot] 
+
+    return e_tot, e_cas, fcivec
+
+def possible_occ(n_as,n_ase):
+    def find_arrays(n_as,n_ase):
+        possible_values=[0,1,2]
+        arrays=[]
+
+        for combination in product(possible_values,repeat=n_as):
+            if sum(combination)==n_ase:
+                arrays.append(numpy.array(combination))
+        return arrays
+    result_arrays=find_arrays(n_as,n_ase)
+    array_list=[]
+    for arr in result_arrays:
+        array_list.append(arr)
+    concantenated_array=numpy.array(array_list, order = 'C', dtype = numpy.int32)
+    return concantenated_array
+
+def fill_array_with_sum_N(length, N):
+    result = [0] * length
+    assert N <= length *2
+    if N <= length:
+        for i in range(N):
+            result[i] = 1
+    else:
+        num_ones = length
+        num_twos = N - num_ones
+
+        for i in range(length):
+            result[i] = 1
+        for i in range(num_twos):
+            result[i] = 2
+
+    return result
+
+def group_occ(po_list, group):
+    best_row = None
+    best_one_count = -1
+    best_two_score = numpy.inf
+    best_one_score = numpy.inf
+    best_zero_score = numpy.inf
+
+    for idx in group:
+        row = po_list[idx]
+        one_count = numpy.count_nonzero(row==1)
+        two_positions = numpy.where(row==2)[0]
+        two_score = sum(two_positions) if len(two_positions) > 0 else numpy.inf
+        zero_positions = numpy.where(row == 0)[0]
+        zero_score = sum(len(row) - zero_positions) if len(zero_positions) > 0 else numpy.inf
+
+        if one_count > best_one_count:
+            best_row = idx
+            best_one_count = one_count
+            best_two_score = two_score
+            best_zero_score = zero_score
+        elif one_count == best_one_count:
+            if two_score < best_two_score:
+                best_row = idx
+                best_two_score = two_score
+                best_zero_score = zero_score
+            if zero_score < best_zero_score:
+                best_row = idx
+                best_two_score = two_score
+                best_zero_score = zero_score
+
+    return po_list[best_row]
+
+def grouping_by_occ(po_list, groupA):
+    a = len(groupA)
+    p = len(po_list)
+    n = len(po_list[0])
+    A_occ = numpy.zeros((p,a))
+    for index, occ in enumerate(po_list):
+        for i in range(a):
+            A_occ[index][i] = numpy.sum(occ[groupA[i]])
+    grouped_rows = {}
+    for i, row in enumerate(A_occ):
+        row_tuple = tuple(row)
+        if row_tuple not in grouped_rows:
+            grouped_rows[row_tuple]=[]
+        grouped_rows[row_tuple].append(i)
+    return list(grouped_rows.values())
+  
+def grouping_by_lowdin(mol, ac_mo_coeff,po_list, aolabel, thres = 0.2):
+    ova = mol.intor_symmetric("cint1e_ovlp_sph")
+    e,v = numpy.linalg.eigh(ova)
+    s12 = numpy.dot(v *numpy.sqrt(e), v.T.conj())
+
+    aolist = mol.search_ao_label(aolabel)
+    print(aolist)
+    a = len(aolist)
+    p = len(po_list)
+    n = len(po_list[0])
+    N = ac_mo_coeff.shape[0]
+    ao_elecnums = numpy.zeros(p)
+    for i in range(p):
+        one_list = numpy.where(po_list[i] == 1)[0]
+        two_list = numpy.where(po_list[i] == 2)[0]
+        pT1 = numpy.dot(ac_mo_coeff[:,one_list],ac_mo_coeff[:,one_list].T)
+        pT2 = numpy.dot(ac_mo_coeff[:,two_list],ac_mo_coeff[:,two_list].T)
+        pT = pT1 + pT2 * 2
+        pTOAO = reduce(numpy.dot,(s12,pT,s12))
+        for index, j in enumerate(aolist):
+            ao_elecnums[i] += pTOAO[j,j]
+
+    print(ao_elecnums)
+    groups = []
+    visited = set() 
+
+    for i in range(len(ao_elecnums)):
+        if i in visited:
+            continue  
+
+        current_group = [i]  
+        visited.add(i) 
+
+        for j in range(len(ao_elecnums)):
+            if i != j and j not in visited and abs(ao_elecnums[i] - ao_elecnums[j]) <= thres:
+                current_group.append(j)  
+                visited.add(j)  
+
+        groups.append(current_group)
+
+    print(groups)
+    return groups
+
+def mo_overlap(mo1, mo2, s1e):
+    mo_overlap_list = lib.einsum('ai,bj,ij->ab', numpy.conjugate(mo1.T), mo2.T, s1e)
+    return mo_overlap_list
+
+def biorthogonalize(mo1, mo2, s1e):
+    u, s, vt = numpy.linalg.svd(mo_overlap(mo1, mo2, s1e))
+    mo1_bimo_coeff = mo1.dot(u)
+    mo2_bimo_coeff = mo2.dot(vt.T)
+    return s, mo1_bimo_coeff, mo2_bimo_coeff, u, vt
+    
+def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
+           dump_chk=True, dm0=None, callback=None, conv_check=True, **kwargs):
+    mf.max_cycle = max_cycle
+    n_as=len(as_list)
+    N=highspin_mo_coeff.shape[1]
+    cn=len(core_list)
+    vir_list=numpy.array(range(numpy.max(as_list)+1,N))
+    highspin_mo_occ=numpy.zeros(N)
+    for i in core_list:
+        highspin_mo_occ[i]=2
+    for idx, value in zip(as_list,as_occ):
+        highspin_mo_occ[idx]=value
+    for i in vir_list:
+        highspin_mo_occ[i]=0
+
+    if'init_dm' in kwargs:
+        raise RuntimeError('''
+You see this error message because of the API updates in pyscf v0.11.
+Keyword argument "init_dm" is replaced by "dm0"''')
+    cput0 = (logger.process_clock(), logger.perf_counter())
+    if conv_tol_grad is None:
+        conv_tol_grad = numpy.sqrt(conv_tol)
+        logger.info(mf, 'Set gradient conv threshold to %g', conv_tol_grad)
+
+    mol = mf.mol
+    if dm0 is None:
+        dm = mf.make_rdm1(highspin_mo_coeff,highspin_mo_occ)
+    else:
+        dm = dm0
+
+    h1e = mf.get_hcore(mol)
+    vhf = mf.get_veff(mol, dm)
+    e_tot = mf.energy_tot(dm, h1e, vhf)
+    logger.info(mf, 'init E= %.15g', e_tot)
+
+    scf_conv = False
+    mo_energy = mo_coeff = mo_occ = None
+
+    s1e = mf.get_ovlp(mol)
+    cond = lib.cond(s1e)
+    logger.debug(mf, 'cond(S) = %s', cond)
+    if numpy.max(cond)*1e-17 > conv_tol:
+        logger.warn(mf, 'Singularity detected in overlap matrix (condition number = %4.3g). '
+                    'SCF may be inaccurate and hard to converge.', numpy.max(cond))
+
+    # Skip SCF iterations. Compute only the total energy of the initial density
+    if mf.max_cycle <= 0:
+        fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf, no DIIS
+        mo_energy, mo_coeff = mf.eig(fock, s1e)
+        mo_occ = mf.get_occ(mo_energy, mo_coeff)
+        return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
+
+    if isinstance(mf.diis, lib.diis.DIIS):
+        mf_diis = mf.diis
+    elif mf.diis:
+        assert issubclass(mf.DIIS, lib.diis.DIIS)
+        mf_diis = mf.DIIS(mf, mf.diis_file)
+        mf_diis.space = mf.diis_space
+        mf_diis.rollback = mf.diis_space_rollback
+
+        # We get the used orthonormalized AO basis from any old eigendecomposition.
+        # Since the ingredients for the Fock matrix has already been built, we can
+        # just go ahead and use it to determine the orthonormal basis vectors.
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        _, mf_diis.Corth = mf.eig(fock, s1e)
+    else:
+        mf_diis = None
+
+    # A preprocessing hook before the SCF iteration
+    mf.pre_kernel(locals())
+
+    cput1 = logger.timer(mf, 'initialize scf', *cput0)
+    
+    mo_energy=highspin_mo_energy
+    mo_coeff=highspin_mo_coeff
+    mo_occ=highspin_mo_occ
+    for cycle in range(mf.max_cycle):
+        dm_last = dm
+        last_hf_e = e_tot
+
+        
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
+        I=numpy.identity(N-n_as)
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
+        reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
+        new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
+        
+ 
+
+        for i in as_list:
+            new_mo_coeff=numpy.insert(new_mo_coeff,i,highspin_mo_coeff[:,i],axis=1)
+        mo_coeff=new_mo_coeff
+
+
+        AS_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
+        for i in as_list:
+            new_mo_energy=numpy.insert(new_mo_energy,i,AS_fock_energy[i])
+        mo_energy=new_mo_energy
+        dm = mf.make_rdm1(mo_coeff,mo_occ)
+        vhf = mf.get_veff(mol, dm, dm_last, vhf)
+        e_tot = mf.energy_tot(dm, h1e, vhf)
+        # Here Fock matrix is h1e + vhf, without DIIS.  Calling get_fock
+        # instead of the statement "fock = h1e + vhf" because Fock matrix may
+        # be modified in some methods.i
+        fock_last=fock
+        fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf, no DIIS
+        norm_gorb = numpy.linalg.norm(mf.get_grad(mo_coeff, mo_occ, fock))
+        if not TIGHT_GRAD_CONV_TOL:
+            norm_gorb = norm_gorb / numpy.sqrt(norm_gorb.size)
+        norm_ddm = numpy.linalg.norm(dm-dm_last)
+        logger.info(mf, 'cycle= %d E= %.15g  delta_E= %4.3g  |g|= %4.3g  |ddm|= %4.3g',
+                    cycle+1, e_tot, e_tot-last_hf_e, norm_gorb, norm_ddm)
+
+        if callable(mf.check_convergence):
+            scf_conv = mf.check_convergence(locals())
+        elif abs(e_tot-last_hf_e) < conv_tol and norm_ddm < numpy.sqrt(conv_tol):
+            scf_conv = True
+
+        if dump_chk:
+            mf.dump_chk(locals())
+
+        if callable(callback):
+            callback(locals())
+
+        cput1 = logger.timer(mf, 'cycle= %d'%(cycle+1), *cput1)
+
+        if scf_conv:
+            break
+
+    if scf_conv and conv_check:
+        # An extra diagonalization, to remove level shift
+        #fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf
+        mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
+        I=numpy.identity(N-n_as)
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+
+        new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
+        reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
+        new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
+
+
+
+        for i in as_list:
+            new_mo_coeff=numpy.insert(new_mo_coeff,i,mo_coeff[:,i],axis=1)
+
+        mo_coeff=new_mo_coeff
+            
+        AS_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
+        for i in as_list:
+            new_mo_energy=numpy.insert(new_mo_energy,i,AS_fock_energy[i])
+        mo_energy=new_mo_energy
+        dm, dm_last = mf.make_rdm1(mo_coeff, mo_occ), dm
+        vhf = mf.get_veff(mol, dm,dm_last,vhf)
+        e_tot, last_hf_e = mf.energy_tot(dm, h1e, vhf), e_tot
+
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        norm_gorb = numpy.linalg.norm(mf.get_grad(mo_coeff, mo_occ, fock))
+        if not TIGHT_GRAD_CONV_TOL:
+            norm_gorb = norm_gorb / numpy.sqrt(norm_gorb.size)
+        norm_ddm = numpy.linalg.norm(dm-dm_last)
+
+        conv_tol = conv_tol * 10
+        conv_tol_grad = conv_tol_grad * 3
+        if callable(mf.check_convergence):
+            scf_conv = mf.check_convergence(locals())
+        elif abs(e_tot-last_hf_e) < conv_tol or norm_gorb < conv_tol_grad:
+            scf_conv = True
+        logger.info(mf, 'Extra cycle  E= %.15g  delta_E= %4.3g  |g|= %4.3g  |ddm|= %4.3g',
+                    e_tot, e_tot-last_hf_e, norm_gorb, norm_ddm)
+        if dump_chk:
+            mf.dump_chk(locals())
+
+    if mf.disp is not None:
+        e_disp = mf.get_dispersion()
+        mf.scf_summary['dispersion'] = e_disp
+        e_tot += e_disp
+
+    logger.timer(mf, 'scf_cycle', *cput0)
+    # A post-processing hook before return
+    mf.post_kernel(locals())
+    if scf_conv==False:
+        mo_coeff=highspin_mo_coeff
+
+    return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
+
+def optimize_mo(mf,mo_energy,mo_coeff,as_list,core_list,nelecas,mode=0,conv_tol = 1e-10, max_cycle = 100, groupA = None, thres = 0.2):
+    n_as = len(as_list)
+    n_ase = nelecas[0] + nelecas[1]
+    N = mo_coeff.shape[0]
+    po_list = possible_occ(n_as, n_ase)
+    p = len(po_list)
+    group = None
+    if groupA is None:
+       optimized_mo=numpy.zeros((p,N,N))
+       #SF-NOCI
+       if mode == 0:
+          for i, occ in enumerate(po_list):
+              conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
+              print(conv, et)
+              optimized_mo[i]=moce
+              print("occuped pattern index:")
+              print(i)
+       #SF-CAS
+       if mode==1:
+          for i, occ in enumerate(po_list):
+              optimized_mo[i]=mo_coeff
+    else :
+        if isinstance(groupA, str):
+            group = grouping_by_lowdin(mf.mol,mo_coeff[:,as_list],po_list, groupA, thres= thres)
+        elif isinstance(groupA, list):
+            group = grouping_by_occ(po_list,groupA)
+        else: NotImplementedError
+        g = len(group)
+        optimized_mo = numpy.zeros((g,N,N))
+        for i in range(0,g):
+            if mode == 0:
+                occ = group_occ(po_list,group[i])
+                conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
+                print(conv, et)
+                optimized_mo[i]=moce
+                print(occ)
+            if mode == 1:
+                optimized_mo[i] = mo_coeff
+            print("occuped pattern index:")
+            print(i)
+    return optimized_mo, po_list, group 
+
+#def _construct_block_hamiltonian(mol,nelec,n_as,po_list,h1c,eri,ov_list,Kc,group):
+#    stringsa = cistring.make_strings(range(n_as),nelec[0])
+#    stringsb = cistring.make_strings(range(n_as),nelec[1])
+#    link_indexa = cistring.gen_linkstr_index(range(n_as),nelec[0])
+#    link_indexb = cistring.gen_linkstr_index(range(n_as),nelec[1])
+#    na = cistring.num_strings(n_as,nelec[0])
+#    nb = cistring.num_strings(n_as,nelec[1])
+#    idx_a = numpy.arange(na)
+#    idx_b = numpy.arange(nb)
+#    mat1 = numpy.zeros((na,nb,na,nb))
+#    matov_list = numpy.zeros((na,nb,na,nb))
+#    for str0a, strs0a in enumerate(stringsa):
+#        for str1a, strsa in enumerate(stringsa):
+#            for str0b, strs0b in enumerate(stringsb):
+#                for str1b, strs1b in enumerate(stringsb):
+#                    w_occa = str2occ(stringsa[str0a],n_as)
+#                    w_occb = str2occ(stringsb[str0b],n_as)
+#                    x_occa = str2occ(stringsa[str1a],n_as)
+#                    x_occb = str2occ(stringsb[str1b],n_as)
+#                    x_occ = numpy.array(x_occa) + numpy.array(x_occb)
+#                    w_occ = numpy.array(w_occa) + numpy.array(w_occb)
+#                    p1=find_matching_rows(po_list,x_occ)[0]
+#                    p2=find_matching_rows(po_list,w_occ)[0]
+#                    if group is not None: 
+#                        p1 = num_to_group(group,p1)
+#                        p2 = num_to_group(group,p2)
+#                    matov_list[str1a,str1b,str0a,str0b] += ov_list[p1,p2]
+#    for str0a, taba in enumerate(link_indexa):
+#        for pa, qa, str1a, signa in taba:
+#            for str0b, strsb in enumerate(stringsb):
+#                 w_occa = str2occ(stringsa[str0a],n_as)
+#                 w_occb = str2occ(stringsb[str0b],n_as)
+#                 x_occa = str2occ(stringsa[str1a],n_as)
+#                 x_occ = numpy.array(x_occa) + numpy.array(w_occb)
+#                 w_occ = numpy.array(w_occa) + numpy.array(w_occb)
+#                 p1=find_matching_rows(po_list,x_occ)[0]
+#                 p2=find_matching_rows(po_list,w_occ)[0]
+#                 if group is not None: 
+#                    p1 = num_to_group(group,p1)
+#                    p2 = num_to_group(group,p2)
+#                 if matov_list[str1a,str0b,str0a,str0b]==0:
+#                    matov_list[str1a,str0b,str0a,str0b] += ov_list[p1,p2]
+#                 mat1[str1a,str0b,str0a,str0b] += signa * h1c[p1,p2,pa,qa]
+#    for str0b, tabb in enumerate(link_indexb):
+#        for pb, qb, str1b, signb in tabb:
+#            for str0a, strsa in enumerate(stringsa):
+#                w_occa = str2occ(stringsa[str0a],n_as)
+#                w_occb = str2occ(stringsb[str0b],n_as)
+#                x_occb = str2occ(stringsb[str1b],n_as)
+#                x_occ = numpy.array(w_occa) + numpy.array(x_occb)
+#                w_occ = numpy.array(w_occa) + numpy.array(w_occb)
+#                p1=find_matching_rows(po_list,x_occ)[0]
+#                p2=find_matching_rows(po_list,w_occ)[0]
+#                if group is not None: 
+#                   p1 = num_to_group(group,p1)
+#                   p2 = num_to_group(group,p2)
+#                if matov_list[str0a,str1b,str0a,str0b]==0:
+#                   matov_list[str0a,str1b,str0a,str0b] += ov_list[p1,p2]
+#                mat1[str0a,str1b,str0a,str0b] += signb * h1c[p1,p2,pb,qb]
+#    #mat1 = mat1.reshape(na*nb,na*nb)
+#    h2 = fci_slow.absorb_h1e(h1c[0,0]*0, eri, n_as, nelec)
+#    t1 = numpy.zeros((n_as,n_as,na,nb,na,nb))
+#    for str0, tab in enumerate(link_indexa):
+#        for a, i, str1, sign in tab:
+#            # alpha spin
+#            t1[a,i,str1,idx_b,str0,idx_b] += sign
+#    for str0, tab in enumerate(link_indexb):
+#        for a, i, str1, sign in tab:
+#            # beta spin
+#            t1[a,i,idx_a,str1,idx_a,str0] += sign
+#    t1 = lib.einsum('psqr,qrABab->psABab', h2, t1)
+#    mat2 = numpy.zeros((na,nb,na,nb))
+#    for str0, tab in enumerate(link_indexa):
+#        for a, i, str1, sign in tab:
+#            # alpha spin
+#            mat2[str1] += sign * t1[a,i,str0]
+#    for str0, tab in enumerate(link_indexb):
+#        for a, i, str1, sign in tab:
+#           # beta spin
+#            mat2[:,str1] += sign * t1[a,i,:,str0]
+#    #mat2 = mat2.reshape(na*nb,na*nb)
+#    ham = (mat1+0.5*mat2)*matov_list
+#    ham = ham.reshape(na*nb,na*nb)
+#    K = numpy.zeros((na,nb))
+#    for i in range(0,na):
+#        for j in range(0,nb):
+#            x_occa = str2occ(stringsa[i],n_as)
+#            x_occb = str2occ(stringsb[j],n_as)        
+#            x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
+#            p1=find_matching_rows(po_list,x_state_occ)[0]
+#            if group is not None: 
+#                p1 = num_to_group(group,p1) 
+#            K[i,j] = Kc[p1]
+#    K = K.reshape(-1)
+#    K = numpy.diag(K)
+#    print("mat1")
+#    print(mat1.reshape(na*nb,na*nb)) 
+#    print("mat2")
+#    print(0.5*mat2.reshape(na*nb,na*nb))
+#    print("K")
+#    print(K)
+#    
+#    hamiltonian = ham + K 
+#    print(hamiltonian)
+#    return hamiltonian
+
+#def construct_block_hamiltonian(mol,nelec,n_as,po_list,h1c,eri,ov_list,Kc,group):
+#    stringsa = cistring.make_strings(range(n_as),nelec[0])
+#    stringsb = cistring.make_strings(range(n_as),nelec[1])
+#    link_indexa = cistring.gen_linkstr_index(range(n_as),nelec[0])
+#    link_indexb = cistring.gen_linkstr_index(range(n_as),nelec[1])
+#    na = cistring.num_strings(n_as,nelec[0])
+#    nb = cistring.num_strings(n_as,nelec[1])
+#    idx_a = numpy.arange(na)
+#    idx_b = numpy.arange(nb)
+#    mat1 = numpy.zeros((na,nb,na,nb))
+#    matov_list = numpy.zeros((na,nb,na,nb))
+#    for str0a, taba in enumerate(link_indexa):
+#        for pa, qa, str1a, signa in taba:
+#            for str0b, tabb in enumerate(link_indexb):
+#                for pb, qb, str1b, signb in tabb:
+#                    w_occa = str2occ(stringsa[str0a],n_as)
+#                    w_occb = str2occ(stringsb[str0b],n_as)
+#                    x_occa = str2occ(stringsa[str1a],n_as)
+#                    x_occb = str2occ(stringsb[str1b],n_as)
+#                    x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
+#                    w_state_occ = numpy.array(w_occa) + numpy.array(w_occb)
+#                    p1=find_matching_rows(po_list,x_state_occ)[0]
+#                    p2=find_matching_rows(po_list,w_state_occ)[0]
+#                    if group is not None: 
+#                       p1 = num_to_group(group,p1)
+#                       p2 = num_to_group(group,p2) 
+#                    if matov_list[str1a,str1b,str0a,str0b]==0:
+#                        matov_list[str1a,str1b,str0a,str0b] += ov_list[p1,p2]    
+#                    if pa==qa and pb ==qb:
+#                        mat1[str1a,str1b,str0a,str0b] += (signa * h1c[p1,p2,pa,qa]/nelec[1]  + signb * h1c[p1,p2,pb,qb]/nelec[0])
+#                    elif pa!=qa and pb == qb:
+#                        mat1[str1a,str1b,str0a,str0b] += signa * h1c[p1,p2,pa,qa]/nelec[1]
+#                    elif pa==qa and pb !=qb:
+#                        mat1[str1a,str1b,str0a,str0b] += signb * h1c[p1,p2,pb,qb]/nelec[0]
+#                    elif pa!=qa and pb !=qb:
+#                        mat1[str1a,str1b,str0a,str0b] += 0
+#                    #mat1[str1a,idx_b,str0a,idx_b] += signa * h1c[g1,g2,pa,qa]
+#                    #mat1[idx_a,str1b,idx_a,str0b] += signb * h1c[g1,g2,pb,qb]
+#
+#    #mat1 = mat1.reshape(na*nb,na*nb)
+#    h2 = fci_slow.absorb_h1e(h1c[0,0]*0, eri, n_as, nelec)
+#    t1 = numpy.zeros((n_as,n_as,na,nb,na,nb))
+#    for str0, tab in enumerate(link_indexa):
+#        for a, i, str1, sign in tab:
+#            # alpha spin
+#            t1[a,i,str1,idx_b,str0,idx_b] += sign
+#    for str0, tab in enumerate(link_indexb):
+#        for a, i, str1, sign in tab:
+#            # beta spin
+#            t1[a,i,idx_a,str1,idx_a,str0] += sign
+#    t1 = lib.einsum('psqr,qrABab->psABab', h2, t1)
+#    mat2 = numpy.zeros((na,nb,na,nb))
+#    for str0, tab in enumerate(link_indexa):
+#        for a, i, str1, sign in tab:
+#            # alpha spin
+#            mat2[str1] += sign * t1[a,i,str0]
+#    for str0, tab in enumerate(link_indexb):
+#        for a, i, str1, sign in tab:
+#           # beta spin
+#            mat2[:,str1] += sign * t1[a,i,:,str0]
+#    #mat2 = mat2.reshape(na*nb,na*nb)
+#    ham = (mat1+0.5*mat2)*matov_list
+#    ham = ham.reshape(na*nb,na*nb)
+#    K = numpy.zeros((na,nb))
+#    for i in range(0,na):
+#        for j in range(0,nb):
+#            x_occa = str2occ(stringsa[i],n_as)
+#            x_occb = str2occ(stringsb[j],n_as)        
+#            x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
+#            p1=find_matching_rows(po_list,x_state_occ)[0]
+#            if group is not None: 
+#                p1 = num_to_group(group,p1) 
+#            K[i,j] = Kc[p1]
+#    K = K.reshape(-1)
+#    K = numpy.diag(K)
+#    print("mat1")
+#    print(mat1.reshape(na*nb,na*nb)) 
+#    print("mat2")
+#    print(0.5*mat2.reshape(na*nb,na*nb))
+#    print("K")
+#    print(K)
+#    
+#    hamiltonian = ham + K 
+#    print(hamiltonian)
+#    
+#    return hamiltonian
+
+def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None, 
+                   ncas=None, ncore=None):
+    if ncas is None : ncas = sfnoci.ncas
+    if ncore is None : ncore = sfnoci.ncore
+    if mo_list is None:
+        mo_list, po_list, group = self.optimize_mo(self.mo_coeff)
+    if dmet_core_list is None:
+        dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+    if dmet_act_list is None:
+        dmet_act_list = sfnoci.get_active_dm(self.mo_coeff)
+    p = dmet_core_list.shape[0]           
+    mo_cas = mo_list[0][:,ncore:ncore+ncas]
+    hcore = sfnoci.get_hcore()
+    h1e = numpy.zeros((p,p,ncas,ncas))       
+    energy_core = numpy.zeros(p)
+    energy_nuc = sfnoci.energy_nuc()   
+    ha1e = lib.einsum('ai,ab,bj->ij',mo_cas,hcore,mo_cas)      
+    
+    for i in range(0,p):
+        for j in range(0,p):
+            corevhf = sfnoci.get_veff(dm = 2 * dmet_core_list[i,j])
+            h1e[i,j] = ha1e + lib.einsum('ijab,ab -> ij', dmet_act_list , corevhf)
+            if i==j:
+                 energy_core[i] += lib.einsum('ab,ab -> ', dmet_core_list[i,i],corevhf)
+                 energy_core[i] += energy_nuc
+                 energy_core[i] += 2*lib.einsum('ab,ab->', dmet_core_list[i,i], hcore)   
+    sfnoci.h1e = h1e
+    sfnoci.core_energies = energy_core
+    return h1e, energy_core
+                         
+def spin_square(sfnoci, rdm1, rdm2ab,rdm2ba):
+    M_s = sfnoci.spin/2
+    mo = sfnoci.mo_coeff
+    s1e = sfnoci.mol.intor('int1e_ovlp')
+    rdm1mo = lib.einsum('qi,pl,kj,qp,lk->ij', mo, rdm1, mo,s1e,s1e)
+    rdm2mo = lib.einsum('ai,bj,ck,dl,ap,bq,cr,ds,pqrs',mo,mo,mo,mo,s1e,s1e,s1e,s1e,rdm2ab+rdm2ba)
+    
+    return M_s**2 + 0.5*lib.einsum('ii ->',rdm1mo) - 0.5*lib.einsum('ijji ->', rdm2mo)       
+
+#def make_diag_precond(hdiag, level_shift=0):
+#    return lib.make_diag_precond(hdiag, level_shift)
+
+class SFNOCI(CASBase):
+  '''SF-NOCI
+  dmet_core_list : density matrix of core orbitals between different bath in atomic basis : (ngroup, ngroup, N, N)
+  po_list : Possible occupation pattern. 
+       for example, for (2e, 2o): po_list = [[0,2], [1,1], [2,0]]. It is 2D numpy array.
+  h1e : effective one electron hamiltonian : (ngroup, ngroup, ncas, ncas)
+  ov_list : overlap between different bath : (ngroup, ngroup)
+  dmet_act_list : density matrix between specific two active orbitals in atomic basis : (ncas, ncas, N, N)
+  core_energies : 1D numpy array of core energies for each bath : (ngroup)
+  '''
+  def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None): #, spin = None, mo_coeff = None, mo_occ = None, groupA = None, mode = 0, thres = 0.2):  
+      
+      mol = mf.mol
+      self.mol = mol
+      self._scf = mf
+      self.verbose = mol.verbose
+      self.stdout = mol.stdout
+      self.max_memory = mf.max_memory
+      self.ncas = ncas
+      self._groupA = None 
+      if isinstance(nelecas, (int, numpy.integer)):
+         raise NotImplementedError 
+      else:
+         self.nelecas = (nelecas[0], nelecas[1])
+         self._spin = nelecas[0] - nelecas[1]
+      self.ncore = ncore
+
+      self.fcisolver = SFNOCISolver(mol)
+      self.fcisolver.lindep = getattr(__config__,
+                                      'sfnoci_SFNOCI_fcisolver_lindep', 1e-14)
+      self.fcisolver.max_cycle = getattr(__config__,
+                                         'sfnoci_SFNOCI_fcisolver_max_cycle', 100)
+      self.fcisolver.conv_tol = getattr(__config__,
+                                        'sfnoci_SFNOCI_fcisolver_conv_tol', 1e-10)
+
+################################################## don't modify the following attributes, they are not input options
+      self.e_tot = 0       
+      self.e_cas = None
+      self.ci = None
+      self.mo_coeff = mf.mo_coeff
+      self.mo_energy = mf.mo_energy
+      self.mo_occ = None 
+      self.converged = False
+      self._thres = 0.2 
+
+  @property
+  def spin(self):
+      if self._spin is None:
+         return self.mol.spin
+      else:
+         return self._spin
+      
+  @spin.setter
+  def spin(self,x):
+      assert x is None or isinstance(x, (int, numpy.integer)) 
+      self._spin = x
+      nelecas = self.nelecas
+      necas = nelecas[0] + nelecas[1]
+      nelecb = (necas- x)//2
+      neleca = necas - nelecb
+      self.nelecas = (neleca,nelecb)
+
+  @property
+  def groupA(self):
+      return self._groupA
+  
+  @groupA.setter
+  def groupA(self,x):
+      self._groupA = x
+
+  @property
+  def lowdin_thres(self):
+      return self._thres
+  
+  @lowdin_thres.setter
+  def lowdin_thres(self, x):
+      self._thres = x
+                            
+  def possible_occ(self):
+      po_list = possible_occ(self.ncas, sum(self.nelecas))
+      return po_list
+                 
+  def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100):
+      if mo_coeff is None : mo_coeff = self.mo_coeff
+      if ncas is None : ncas = self.ncas
+      if ncore is None : ncore = self.ncore
+      mf = self._scf
+      as_list = numpy.array(range(ncore,ncore + ncas))  
+      core_list = numpy.array(range(0,ncore))
+      FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
+      return FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ 
+
+  def optimize_mo(self, mo_coeff=None, debug=False, groupA=None, conv_tol=1e-10, max_cycle=100):
+      if mo_coeff is None : mo_coeff = self.mo_coeff
+      if groupA is None : groupA = self.groupA
+      mode = 0
+      if debug : mode = 1 
+      mf = self._scf
+      mo_energy = mf.mo_energy
+      ncore = self.ncore
+      ncas = self.ncas       
+      as_list = numpy.array(range(ncore,ncore + ncas))       
+      core_list = numpy.array(range(0,ncore))
+      mo_list, po_list, group = optimize_mo(self._scf,mo_energy,mo_coeff,as_list,core_list,self.nelecas, mode, conv_tol=conv_tol, max_cycle=max_cycle, groupA=groupA, thres=self.lowdin_thres)
+      self.AS_mo_coeff = mo_list[0][:,as_list]
+      return mo_list, po_list, group
+                 
+  def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
+      if mo_list is None or po_list_or_group is None:
+          mo_list, po_list, group = self.optimize_mo(self.mo_coeff)
+          if group is None : po_list_or_group = po_list
+          else : po_list_or_group = group 
+      ncore = self.ncore
+      ncas = self.ncas  
+      s1e = self._scf.get_ovlp(self.mol)
+      as_list = numpy.array(range(ncore,ncore+ncas))
+      core_list = numpy.array(range(0,ncore))       
+      N = mo_list.shape[1]
+      p = len(po_list_or_group)
+      dmet_core_list = numpy.zeros((p,p,N,N))
+      ov_list = numpy.zeros((p,p))       
+      for i in range(0,p):
+          xc_mo_coeff = mo_list[i][:,core_list]       
+          for j in range(0,p):
+              wc_mo_coeff = mo_list[j][:,core_list]
+              S, xc_bimo_coeff, wc_bimo_coeff, U, Vt = biorthogonalize(xc_mo_coeff, wc_mo_coeff, s1e)       
+              ov_list[i,j] = numpy.prod(S[numpy.abs(S)>1e-10])*numpy.linalg.det(U)*numpy.linalg.det(Vt)
+              for c in range(0,ncore):
+                  dmet_core_list[i,j] +=numpy.outer(xc_bimo_coeff[:,c],wc_bimo_coeff[:,c])/S[c]
+      return dmet_core_list, ov_list       
+
+  def get_active_dm(self,mo_coeff = None):
+      ncas = self.ncas
+      ncore = self.ncore
+      nocc = ncore + ncas
+      if mo_coeff is None:
+          ncore = self.ncore
+          mo_coeff = self.mo_coeff[:,ncore:nocc]
+      elif mo_coeff.shape[1] != ncas:
+           mo_coeff = mo_coeff[:,ncore:nocc] 
+      N = mo_coeff.shape[0]
+      dmet_act_list = numpy.zeros((ncas,ncas,N,N))
+      for i in range(0,ncas):
+          for j in range(0,ncas):
+              dmet_act_list[i,j] = numpy.outer(mo_coeff[:,i],mo_coeff[:,j])
+      self.dmet_act_list = dmet_act_list
+      return dmet_act_list
+           
+  def get_h1cas(self, dmet_act_list = None, mo_list = None, dmet_core_list = None, ncas = None, ncore = None):
+      return self.get_h1e(dmet_act_list, mo_list, dmet_core_list, ncas, ncore)
+  get_h1e = h1e_for_sfnoci
+             
+  def get_h2eff(self, mo_coeff=None):
+      '''Compute the active space two-particle Hamiltonian.
+      '''
+      return CASCI.get_h2eff(self,mo_coeff)
+
+#  def construct_reduced_hamiltonian(self, mo_coeff = None, h1e= None, energy_core = None, po_list = None, ov_list = None):
+#      if mo_coeff is None : mo_coeff = self.mo_coeff
+#      if h1e is None and energy_core is None: h1e, energy_core = self.get_h1e()
+#      if po_list is None : po_list = self.possible_occ()
+#      if ov_list is None : ov_list = ov_list          
+#      nelecas = self.nelecas   
+#      ncas = self.ncas  
+#      mol = self.mol
+#      group = self.group
+#      eri = self.get_h2eff(mo_coeff) 
+#      hamiltonian = _construct_block_hamiltonian(mol,nelecas,ncas,po_list,h1e,eri,ov_list,energy_core,group)
+#      return hamiltonian
+             
+#  def kernel_diag(self,mo = None, debug = False):
+#      '''
+#      Solve CI problem by just diagonalizing Hamiltonian matrix
+#      '''
+#      if mo is not None: self.mo_coeff = mo
+#      cput0 = (logger.process_clock(), logger.perf_counter())
+#      mo_list, po_list, group = self.optimize_mo(mo, debug)
+#      cput1 = logger.timer(self, 'core-vir rotation', *cput0)
+#      dmet_act_list = self.get_active_dm(mo)
+#      if group is None :
+#         dmet_core_list, ov_list = self.get_svd_matrices(mo_list , po_list)
+#      else: dmet_core_list, ov_list = self.get_svd_matrices(mo_list , group)
+#      cput1 = logger.timer(self,'SVD and core density matrix calculation', *cput1)          
+#      h1e, energy_core = self.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
+#      cput1 = logger.timer(self,'effective 1e hamiltonians and core energies calculation', *cput1)
+#      #self.mo_eri = self.get_h2eff(mo)
+#      #cput1 = logger.timer(self, 'effective 2e hamiltonian calculation', *cput1)
+#      hamiltonian = self.construct_reduced_hamiltonian(mo, h1e, energy_core, po_list, ov_list)
+#      eigenvalues, eigenvectors = gen_eig(hamiltonian)
+#      self.ci = eigenvectors
+#      logger.timer(self, 'CI solving', *cput1)
+#      logger.timer(self, 'All process', *cput0)
+#      return eigenvalues, eigenvectors
+  
+#  def matrix_kernel(self, mo = None, debug = False):
+#      '''Calculate necessary matrices before constructing hamiltonian.
+#      '''
+#      if mo is not None : self.mo_coeff = mo
+#      mo_list, po_list, group = self.optimize_mo(mo, debug = debug)
+#      dmet_act_list = self.get_active_dm(mo)
+#      if group is None :
+#         dmet_core_list, ov_list = self.get_svd_matrices(mo_list , po_list)
+#      else: dmet_core_list, ov_list = self.get_svd_matrices(mo_list , group)       
+#      h1e, energy_core = self.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
+#      #self.mo_eri = self.get_h2eff(mo)
+#      return mo_list, po_list, group, dmet_core_list, h1e, energy_core, ov_list
+
+  def kernel(self, mo_coeff=None, ci0=None, verbose=None):
+      '''
+      Returns:
+          Five elements, they are
+          total energy,
+          active space CI energy,
+          the active space FCI wavefunction coefficients,
+          the MCSCF canonical orbital coefficients,
+          the MCSCF canonical orbital coefficients.
+
+      They are attributes of mcscf object, which can be accessed by
+      .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
+      '''
+      if mo_coeff is None:
+          mo_coeff = self.mo_coeff
+      else:
+          self.mo_coeff = mo_coeff
+      if ci0 is None:
+          ci0 = self.ci
+      log = logger.new_logger(self, verbose)
+
+      #self.check_sanity()
+      #self.dump_flags(log)
+
+      self.e_tot, self.e_cas, self.ci = \
+              kernel(self, mo_coeff, ci0=ci0, verbose=log)
+
+      if getattr(self.fcisolver, 'converged', None) is not None:
+          self.converged = numpy.all(self.fcisolver.converged)
+          if self.converged:
+              log.info('SFNOCI converged')
+          else:
+              log.info('SFNOCI not converged')
+      else:
+          self.converged = True
+      #self._finalize()
+      return self.e_tot, self.e_cas, self.ci   # will provide group info
+  
+#  def skip_scf(self, mo = None, ci0=None,
+#               tol=None, lindep=None, max_cycle=None, max_space=None,
+#               nroots=None, davidson_only=None, pspace_size=None,
+#               orbsym=None, wfnsym=None, ecore=0, **kwargs ):
+#      if mo is not None: self.mo_coeff
+#      e, c = kernel_SFNOCI(self, self.h1e, self.mo_eri, self.ncas, self.nelecas, self.po_list, self.group, ov_list, self.core_energies, ci0, link_index=None,
+#                           tol = tol, lindep= lindep, max_cycle=max_cycle, max_space=max_space, nroots=nroots, davidson_only= davidson_only,
+#                           pspace_size= pspace_size, ecore= ecore, verbose=self.verbose, **kwargs)
+#      return e, c
+
+  def make_rdm1s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None, 
+                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if ci is None : ci = self.ci
+      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+          ci = ci[:,0]
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if po_list is None or group is None:
+          mo_list, po_list, group = self.optimize_mo(mo_coeff)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+      rdm1a, rdm1b = \
+          self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+                                    ncore, dmet_core_list, po_list, ov_list, group)
+      return rdm1a, rdm1b
+
+  def make_rdm1(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if ci is None : ci = self.ci
+      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+          ci = ci[:,0]
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if po_list is None or group is None:
+          mo_list, po_list, group = self.optimize_mo(mo_coeff)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+      rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
+                                     ncore, dmet_core_list, po_list, ov_list, group)
+      return rdm
+
+  def make_rdm2s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if ci is None : ci = self.ci
+      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+          ci = ci[:,0]
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if po_list is None or group is None:
+          mo_list, po_list, group = self.optimize_mo(mo_coeff)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+      rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
+          self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas, 
+                                    ncore, dmet_core_list, po_list, ov_list, group)
+      return rdm2aa, rdm2ab, rdm2ba, rdm2bb
+  
+  def make_rdm2(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if ci is None : ci = self.ci
+      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+          ci = ci[:,0]
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if po_list is None or group is None:
+          mo_list, po_list, group = self.optimize_mo(mo_coeff)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+      rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas, 
+                                     ncore, dmet_core_list, po_list, ov_list, group)
+      return rdm
+
+if  __name__ == '__main__':
+    from pyscf import gto
+    from pyscf import scf
+    from pyscf.fci import cistring
+    mol = gto.Mole()
+    mol.verbose = 5
+    mol.output = None
+
+    mol.atom = [['Li', (0, 0, 0)],['F',(0,0,1.4)]]
+    mol.basis = 'ccpvdz'
+    
+    x_list=[]
+    e1_list=[]
+    e2_list=[]
+    e3_list=[]
+    e4_list=[]
+    e5_list=[]
+    e6_list=[]
+    e7_list=[]
+    e8_list=[]
+    ma=[]
+    mode=0
+    
+    mol.spin=2
+    mol.build(0,0)
+    rm=scf.ROHF(mol)
+    rm.kernel()
+  
+    molr = gto.Mole()
+    molr.verbose = 5
+    molr.output = None
+    molr.atom = [['Li', (0, 0, 0)],['F',(0,0,1.3)]]
+    molr.basis = 'ccpvdz'
+    mr=scf.RHF(molr)
+    mr.kernel()
+
+    mo0=mr.mo_coeff
+    occ=mr.mo_occ
+    setocc=numpy.zeros((2,occ.size))
+    setocc[:,occ==2]=1
+    setocc[1][3]=0
+    setocc[0][6]=1
+    ro_occ=setocc[0][:]+setocc[1][:]
+    dm_ro=rm.make_rdm1(mo0,ro_occ)
+    rm=scf.addons.mom_occ(rm,mo0,setocc)
+    rm.scf(dm_ro)
+    mo=rm.mo_coeff
+    as_list=[3,6,7,10]
+    s1e = mol.intor('int1e_ovlp')
+    mySFNOCI = SFNOCI(rm,4,(2,2),groupA = 'Li')
+    mySFNOCI.lowdin_thres= 0.2
+    mySFNOCI.fcisolver.nroots = 4 
+    
+    from pyscf.mcscf import addons
+    mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
+    reei, _, ci = mySFNOCI.kernel(mo)
+    print(reei)
+
+    i=1
+    while i <= 4:
+        x_list.append(i)
+        mol.atom=[['Li',(0,0,0)],['F',(0,0,i)]]
+        mol.build(0,0)
+        mol.spin=2
+        m=scf.RHF(mol)
+        m.kernel()
+        m=scf.addons.mom_occ(m,mo0,setocc)
+        m.scf(dm_ro)
+
+        mySFNOCI = SFNOCI(m,4,(2,2),groupA = 'Li')
+        mySFNOCI.spin = 0
+        mySFNOCI.fcisolver.nroots = 4 
+        mo = addons.sort_mo(mySFNOCI,m.mo_coeff,as_list,1)
+        eigenvalues, _, eigenvectors = mySFNOCI.kernel(mo)
+
+        e1_list.append(eigenvalues[0])
+        e2_list.append(eigenvalues[1])
+        e3_list.append(eigenvalues[2])
+        e4_list.append(eigenvalues[3])
+        
+        i+=0.5
+
+    print(e1_list)
+    print(e2_list)
+    print(e3_list)
+    print(e4_list)
+
+    import matplotlib.pyplot as plt
+    from pyscf.data.nist import HARTREE2EV
+    ref = e1_list[-1] 
+    e1_list = (numpy.array(e1_list) - ref) * HARTREE2EV
+    e2_list = (numpy.array(e2_list) - ref) * HARTREE2EV
+    e3_list = (numpy.array(e3_list) - ref) * HARTREE2EV
+    e4_list = (numpy.array(e4_list) - ref) * HARTREE2EV
+ 
+    plt.plot(x_list, e1_list, '-o', label='SF-NOCI $1{}^1\\Sigma^+$')
+    plt.plot(x_list, e2_list, '-o', label='SF-NOCI $1{}^3\\Sigma^+$')
+    plt.plot(x_list, e3_list, '-o', label='SF-NOCI $2{}^1\\Sigma^+$')
+    plt.plot(x_list, e4_list, '-o', label='SF-NOCI $2{}^3\\Sigma^+$')
+
+    #plt.xlabel("Li-F distance, $\\AA$")
+    plt.xlabel("Li-F distance, A")
+    plt.ylabel("Relative Energy, eV")
+    plt.legend()
+
+    plt.xlim(1, 4)
+    plt.ylim(-4, 10)
+
+    plt.show()
+ 
\ No newline at end of file

From eeefab4d0ab4159d72275145a062ec2248ae64da Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Tue, 18 Feb 2025 12:47:17 +0900
Subject: [PATCH 02/10] sfnoci

---
 pyscf/sfnoci/direct_sfnoci.py | 187 +++++++++++-------
 pyscf/sfnoci/sfnoci.py        | 362 ++++++++++++++++++++++++++--------
 2 files changed, 391 insertions(+), 158 deletions(-)

diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
index cc061c8..545e557 100644
--- a/pyscf/sfnoci/direct_sfnoci.py
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -32,6 +32,8 @@
     J. Chem. Theory Comput. 2025 
 '''
 
+import sys
+
 import numpy
 import ctypes
 import scipy.linalg
@@ -41,9 +43,10 @@
 from pyscf import lib
 from pyscf import __config__
 from pyscf.lib import logger
+from pyscf.fci import spin_op
 from pyscf.fci import cistring
 from pyscf.fci import direct_uhf
-from pyscf.fci.direct_spin1 import FCIBase, FCISolver
+from pyscf.fci.direct_spin1 import FCIBase, FCISolver, FCIvector
 
 libsf = lib.load_library("libsfnoci")
 
@@ -52,6 +55,14 @@
 def find_matching_rows(matrix, target_row):
     matching_rows = numpy.where((matrix == target_row).all(axis=1))[0]
     return matching_rows
+
+def str2occ(str0,norb):
+    occ=numpy.zeros(norb)
+    for i in range(norb):
+        if str0 & (1<<i):
+            occ[i]=1
+
+    return occ
  
 def num_to_group(groups,number):
     for i, group in enumerate(groups):
@@ -59,7 +70,24 @@ def num_to_group(groups,number):
             return i
     return None
 
-def make_hdiag(h1e, eri, ncas, nelecas, PO, group, energy_core, opt=None):
+def group_info_list(ncas, nelecas, PO, group):
+    stringsa = cistring.make_strings(range(0,ncas), nelecas[0])
+    stringsb = cistring.make_strings(range(0,ncas), nelecas[1])
+    na = len(stringsa)
+    nb = len(stringsb)
+    group_info = numpy.zeros((na,nb))
+    for stra, strsa in enumerate(stringsa):
+        for strb, strsb in enumerate(stringsb):
+            occa = str2occ(stringsa[stra],ncas)
+            occb = str2occ(stringsb[strb],ncas)
+            occ = occa + occb
+            p = find_matching_rows(PO,occ)[0]
+            if group is not None:
+                p = num_to_group(group,p)
+            group_info[stra, strb] = p
+    return group_info
+
+def make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
     if isinstance(nelecas, (int, numpy.integer)):
         nelecb = nelecas//2
         neleca = nelecas - nelecb
@@ -78,14 +106,14 @@ def make_hdiag(h1e, eri, ncas, nelecas, PO, group, energy_core, opt=None):
                 occ[i] += 1
             for i in bocc:
                 occ[i] +=1
-            p = find_matching_rows(PO,occ)
+            p = find_matching_rows(po_list,occ)
             if group is not None:
                 p = num_to_group(group, p)
             e1 = h1e[p,p,aocc,aocc].sum() + h1e[p,p,bocc,bocc].sum()
             e2 = diagj[aocc][:,aocc].sum() + diagj[aocc][:,bocc].sum() \
                + diagj[bocc][:,aocc].sum() + diagj[bocc][:,bocc].sum() \
                - diagk[aocc][:,aocc].sum() - diagk[bocc][:,bocc].sum()
-            hdiag.append(e1 + e2*.5 + energy_core[p])
+            hdiag.append(e1 + e2*.5 + ecore_list[p])
     return numpy.array(hdiag)
 
 def absorb_h1e(h1e, eri, ncas, nelecas, fac=1):
@@ -156,7 +184,7 @@ def python_list_to_c_array(python_list):
             group_sizes[i] = group_size  
         return flat_list, group_sizes, num_groups
  
-def contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core,
+def contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list,
                link_index=None, ts=None, t_nonzero=None):
     '''Compute H|CI>
     '''
@@ -187,8 +215,8 @@ def contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core,
     civec = numpy.asarray(civec, order = 'C')
     cinew = numpy.zeros_like(civec)
     erieff = numpy.asarray(erieff, order = 'C', dtype= numpy.float64)
-    PO = numpy.asarray(PO, order = 'C', dtype=numpy.int32)
-    PO_nrows = PO.shape[0]
+    po_list = numpy.asarray(po_list, order = 'C', dtype=numpy.int32)
+    po_nrows = po_list.shape[0]
     cgroup, group_sizes, num_groups = python_list_to_c_array(group)
     stringsa = cistring.make_strings(range(ncas),neleca)
     stringsb = cistring.make_strings(range(ncas),nelecb)
@@ -214,15 +242,15 @@ def contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core,
     t1bnn = t1b_nonzero.shape[0]
     t2aann = t2aa_nonzero.shape[0]
     t2bbnn = t2bb_nonzero.shape[0]
-    TSc = numpy.asarray(TSc, order = 'C', dtype=numpy.float64)
-    energy_core = numpy.asarray(energy_core, order = 'C', dtype=numpy.float64)
+    ov_list = numpy.asarray(ov_list, order = 'C', dtype=numpy.float64)
+    ecore_list = numpy.asarray(ecore_list, order = 'C', dtype=numpy.float64)
 
     libsf.SFNOCIcontract_H_spin1(erieff.ctypes.data_as(ctypes.c_void_p),
          civec.ctypes.data_as(ctypes.c_void_p),
          cinew.ctypes.data_as(ctypes.c_void_p),
          ctypes.c_int(ncas),
          ctypes.c_int(neleca), ctypes.c_int(nelecb),
-         PO.ctypes.data_as(ctypes.c_void_p),ctypes.c_int(PO_nrows),
+         po_list.ctypes.data_as(ctypes.c_void_p),ctypes.c_int(po_nrows),
          ctypes.c_int(na), stringsa.ctypes.data_as(ctypes.c_void_p), 
          ctypes.c_int(nb), stringsb.ctypes.data_as(ctypes.c_void_p),
          cgroup, group_sizes, ctypes.c_int(num_groups),
@@ -234,7 +262,7 @@ def contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core,
          t2aa_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2aann),
          t2bb.ctypes.data_as(ctypes.c_void_p), 
          t2bb_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2bbnn),
-         TSc.ctypes.data_as(ctypes.c_void_p), energy_core.ctypes.data_as(ctypes.c_void_p))
+         ov_list.ctypes.data_as(ctypes.c_void_p), ecore_list.ctypes.data_as(ctypes.c_void_p))
     return cinew
 
 def contract_H_slow(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, link_index=None):
@@ -327,11 +355,11 @@ def contract_H_slow(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, l
     cinew.reshape(-1)
     return cinew
 
-def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, PO, group, TSc,
+def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list, ecore_list,
                   ci0=None, link_index=None, tol=None, lindep=None, 
                   max_cycle=None, max_space=None, nroots=None, 
                   davidson_only=None, pspace_size=None, hop=None,
-                  max_memory=None, verbose=None, ecore=0, **kwargs):
+                  max_memory=None, verbose=None, **kwargs):
     '''
     Args:
         h1e: ndarray
@@ -379,7 +407,7 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, PO, group, TSc,
     log = logger.new_logger(sfnoci, verbose)
     nelec = nelecas
     assert (0 <= nelec[0] <= ncas and 0 <= nelec[1] <= ncas)
-    hdiag = sfnoci.make_hdiag(h1e, eri, ncas, nelec, PO, group, ecore).ravel()
+    hdiag = sfnoci.make_hdiag(h1e, eri, ncas, nelec, po_list, group, ecore_list).ravel()
     num_dets = hdiag.size
     civec_size = num_dets
     precond = sfnoci.make_precond(hdiag)
@@ -399,8 +427,8 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, PO, group, TSc,
     if hop is None:
         cpu0 = [logger.process_clock(), logger.perf_counter()]
         def hop(c):
-            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, PO, group, 
-                                   TSc, ecore,link_index, ts, t_nonzero)
+            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, po_list, group, 
+                                   ov_list, ecore_list,link_index, ts, t_nonzero)
             cpu0[:] = log.timer_debug1('contract_H', *cpu0)
             return hc.ravel()
     def init_guess():
@@ -426,7 +454,7 @@ def init_guess():
                        tol_residual=None, **kwargs)
     return e, c
 
-def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
     N = mo_coeff.shape[0]
     mo_cas = mo_coeff[:,ncore:ncore+ncas]
     stringsa = cistring.make_strings(range(ncas),nelecas[0])
@@ -442,10 +470,10 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
             w_occa = str2occ(strsa,ncas)
             w_occb = str2occ(strsb,ncas)
             w_occ = w_occa + w_occb
-            p = find_matching_rows(PO,w_occ)[0]
+            p = find_matching_rows(po_list,w_occ)[0]
             if group is not None: 
                      p = num_to_group(group,p)
-            rdm1c += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b]*W[p,p]
+            rdm1c += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b]*dmet_core_list[p,p]
   
     rdm1asmoa = numpy.zeros((ncas,ncas))
     rdm1asmob = numpy.zeros((ncas,ncas))    
@@ -457,12 +485,12 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
                 x_occa = str2occ(stringsa[str1a],ncas)
                 x_occ = x_occa + w_occb
                 w_occ = w_occa + w_occb   
-                p1 = find_matching_rows(PO,x_occ)[0]
-                p2 = find_matching_rows(PO,w_occ)[0]
+                p1 = find_matching_rows(po_list,x_occ)[0]
+                p2 = find_matching_rows(po_list,w_occ)[0]
                 if group is not None: 
                      p1 = num_to_group(group,p1)
                      p2 = num_to_group(group,p2) 
-                rdm1asmoa[aa,ia] += signa * numpy.conjugate(ci[str1a,str0b]) * ci[str0a,str0b] * TSc[p1,p2]
+                rdm1asmoa[aa,ia] += signa * numpy.conjugate(ci[str1a,str0b]) * ci[str0a,str0b] * ov_list[p1,p2]
     for str0b, tabb in enumerate(link_indexb):
         for ab, ib, str1b, signb in link_indexb[str0b]:
             for str0a, strsa in enumerate(stringsa):
@@ -471,12 +499,12 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
                 x_occb = str2occ(stringsb[str1b],ncas)
                 x_occ = w_occa + x_occb
                 w_occ = w_occa + w_occb  
-                p1 = find_matching_rows(PO,x_occ)[0]
-                p2 = find_matching_rows(PO,w_occ)[0] 
+                p1 = find_matching_rows(po_list,x_occ)[0]
+                p2 = find_matching_rows(po_list,w_occ)[0] 
                 if group is not None: 
                      p1 = num_to_group(group,p1)
                      p2 = num_to_group(group,p2)       
-                rdm1asmob[ab,ib] += signb * numpy.conjugate(ci[str0a,str1b]) * ci[str0a,str0b] * TSc[p1,p2]
+                rdm1asmob[ab,ib] += signb * numpy.conjugate(ci[str0a,str1b]) * ci[str0a,str0b] * ov_list[p1,p2]
     rdm1a = lib.einsum('ia,ab,jb -> ij', numpy.conjugate(mo_cas),rdm1asmoa,mo_cas)
     rdm1b = lib.einsum('ia,ab,jb-> ij', numpy.conjugate(mo_cas),rdm1asmob,mo_cas)
     rdm1a += rdm1c
@@ -484,11 +512,11 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
     
     return rdm1a, rdm1b
 
-def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
-    rdm1a, rdm1b = make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+    rdm1a, rdm1b = make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
     return rdm1a + rdm1b
 
-def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, po_list, ov_list, group):
     mo_cas = mo_coeff[:,ncore:ncore+ncas]
     N = mo_coeff.shape[0]
     rdm2aa = numpy.zeros((N,N,N,N))
@@ -526,31 +554,31 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
             w_occa = str2occ(strs0a,ncas)
             w_occb = str2occ(strs0b,ncas)
             w_occ = w_occa + w_occb
-            p2 = find_matching_rows(PO,w_occ)[0]
+            p2 = find_matching_rows(po_list,w_occ)[0]
             if group is not None: 
                p2 = num_to_group(group,p2) 
-            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', W[p2,p2,:,:],W[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])) 
-            rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])
-            rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:])
-            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', W[p2,p2,:,:],W[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',W[p2,p2,:,:],W[p2,p2,:,:]))  
+            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])) 
+            rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
+            rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
+            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))  
             for str1a, strs1a in enumerate(stringsa):
                 x_occa = str2occ(strs1a,ncas)
                 x_occ = x_occa + w_occb
-                p1 = find_matching_rows(PO,x_occ)[0]
+                p1 = find_matching_rows(po_list,x_occ)[0]
                 if group is not None: 
                      p1 = num_to_group(group,p1)
-                rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*TSc[p1,p2]
+                rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*ov_list[p1,p2]
                 for k in range(ncas):
-                    rdm2aaac[:,k,k,:] -= numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t1a[:,:,str1a,str0a]*TSc[p1,p2]
+                    rdm2aaac[:,k,k,:] -= numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t1a[:,:,str1a,str0a]*ov_list[p1,p2]
             for str1b, strs1b in enumerate(stringsb):
                 x_occb = str2occ(strs1b,ncas)
                 x_occ = w_occa + x_occb
-                p1 = find_matching_rows(PO,x_occ)[0]
+                p1 = find_matching_rows(po_list,x_occ)[0]
                 if group is not None: 
                      p1 = num_to_group(group,p1)
-                rdm2bbac[:,:,:,:] += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]*t2bb[:,:,:,:,str1b,str0b]*TSc[p1,p2]
+                rdm2bbac[:,:,:,:] += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]*t2bb[:,:,:,:,str1b,str0b]*ov_list[p1,p2]
                 for k in range(ncas):
-                    rdm2bbac[:,k,k,:] -= numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b] * t1b[:,:,str1b,str0b]*TSc[p1,p2]
+                    rdm2bbac[:,k,k,:] -= numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b] * t1b[:,:,str1b,str0b]*ov_list[p1,p2]
             for str1a, strs1a in enumerate(stringsa):
                 for str1b, strs1b in enumerate(stringsb):
                     w_occa = str2occ(strs0a,ncas)
@@ -559,13 +587,13 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
                     x_occb = str2occ(strs1b,ncas)
                     w_occ = w_occa + w_occb
                     x_occ = x_occa + x_occb
-                    p1 = find_matching_rows(PO,x_occ)[0] 
-                    p2 = find_matching_rows(PO,w_occ)[0]
+                    p1 = find_matching_rows(po_list,x_occ)[0] 
+                    p2 = find_matching_rows(po_list,w_occ)[0]
                     if group is not None: 
                        p1 = num_to_group(group,p1)
                        p2 = num_to_group(group,p2)  
-                    rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*TSc[p1,p2]
-                    rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*TSc[p1,p2]
+                    rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*ov_list[p1,p2]
+                    rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*ov_list[p1,p2]
     
     rdm2aa += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2aaac)
     rdm2ab += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2abac)
@@ -583,14 +611,14 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
                 x_occa = str2occ(stringsa[str1a],ncas)
                 x_occ = x_occa + w_occb
                 w_occ = w_occa + w_occb   
-                p1 = find_matching_rows(PO,x_occ)[0]
-                p2 = find_matching_rows(PO,w_occ)[0]
+                p1 = find_matching_rows(po_list,x_occ)[0]
+                p2 = find_matching_rows(po_list,w_occ)[0]
                 if group is not None: 
                      p1 = num_to_group(group,p1)
                      p2 = num_to_group(group,p2) 
-                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
-                rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
-                rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
   
     for str0b, tabb in enumerate(link_indexb):
         for str1b in numpy.unique(link_indexb[str0b][:,2]):
@@ -600,20 +628,20 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
                 x_occb = str2occ(stringsb[str1b],ncas)
                 x_occ = w_occa + x_occb
                 w_occ = w_occa + w_occb  
-                p1 = find_matching_rows(PO,x_occ)[0]
-                p2 = find_matching_rows(PO,w_occ)[0]
+                p1 = find_matching_rows(po_list,x_occ)[0]
+                p2 = find_matching_rows(po_list,w_occ)[0]
                 if group is not None: 
                      p1 = num_to_group(group,p1)
                      p2 = num_to_group(group,p2)  
-                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
-                rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
-                rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
     
     return rdm2aa, rdm2ab, rdm2ba, rdm2bb
 
-def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
+def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
     rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
-        make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+        make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, po_list, ov_list, group)
     return rdm2aa + rdm2ab + rdm2ba + rdm2bb
 
 def fix_spin(fciobj, shift=PENALTY, ss=None, **kwargs):
@@ -668,8 +696,8 @@ def fix_spin_(fciobj, shift=.1, ss=None):
 class SFNOCISolver(FCISolver):
   '''SF-NOCI
   '''
-  def make_hdiag(self, h1e, eri, ncas, nelecas, PO, group, energy_core, opt=None):
-      return make_hdiag(h1e, eri, ncas, nelecas, PO, group, energy_core, opt)
+  def make_hdiag(self, h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
+      return make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt)
   
   def make_precond(self, hdiag, level_shift=0):
       return lib.make_diag_precond(hdiag, level_shift)
@@ -677,10 +705,10 @@ def make_precond(self, hdiag, level_shift=0):
   def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
       return absorb_h1e(h1e, eri, ncas, nelecas, fac)
 
-  def contract_H(self, erieff, civec, ncas, nelecas, PO, group, TSc, 
-                 energy_core, link_index=None, ts=None, t_nonzero=None):
-      return contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, 
-                        energy_core ,link_index, ts, t_nonzero)
+  def contract_H(self, erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+                 ecore_list, link_index=None, ts=None, t_nonzero=None):
+      return contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+                        ecore_list ,link_index, ts, t_nonzero)
 
   def get_init_guess(self, ncas, nelecas, nroots, hdiag):
       return fci.direct_spin1.get_init_guess(ncas, nelecas, nroots, hdiag)
@@ -699,10 +727,10 @@ def eig(self, op, x0=None, precond=None, **kwargs):
             ci = ci[0]
         return e, ci
 
-  def kernel(self, h1e, eri, norb, nelec, PO, group, TSc, ci0=None,
+  def kernel(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0=None,
              tol=None, lindep=None, max_cycle=None, max_space=None,
              nroots=None, davidson_only=None, pspace_size=None,
-             orbsym=None, wfnsym=None, ecore=0, **kwargs):
+             orbsym=None, wfnsym=None, **kwargs):
       if nroots is None: nroots = self.nroots
       if self.verbose >= logger.WARN:
           self.check_sanity()
@@ -714,24 +742,31 @@ def kernel(self, h1e, eri, norb, nelec, PO, group, TSc, ci0=None,
       link_indexb = cistring.gen_linkstr_index(range(norb), nelec[1])
       link_index = (link_indexa, link_indexb)
 
-      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, PO, group, TSc, ci0,
+      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0,
                            link_index, tol, lindep, max_cycle, max_space, nroots,
-                           davidson_only, pspace_size, ecore=ecore, **kwargs)
+                           davidson_only, pspace_size, **kwargs)
       self.eci = e
 
+      na = link_index[0].shape[0]
+      nb = link_index[1].shape[0]
+      if nroots > 1:
+         self.ci = [x.reshape(na,nb).view(FCIvector) for x in c]
+      else:
+        self.ci = c.reshape(na,nb).view(FCIvector)
+    
       return self.eci, self.ci
 
-  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
-      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
 
-  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
-      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
 
-  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
-      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, po_list, ov_list, group):
+      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
 
-  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group):
-      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, W, PO, TSc, group)
+  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
  
   def contract_ss(self, civec, ncas=None, nelecas=None):
       if ncas is None : ncas = self.ncas
@@ -790,8 +825,8 @@ def undo_fix_spin(self):
     def base_contract_H(self, *args, **kwargs):
         return super().contract_H(*args, **kwargs)
     
-    def contract_H(self, erieff, civec, ncas, nelecas, PO, group, TSc, 
-                   energy_core, link_index=None, ts=None, t_nonzero=None, **kwargs):
+    def contract_H(self, erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+                   ecore_list, link_index=None, ts=None, t_nonzero=None, **kwargs):
         if isinstance(nelecas, (int, numpy.number)):
             sz = (nelecas % 2) * .5
         else:
@@ -813,7 +848,7 @@ def contract_H(self, erieff, civec, ncas, nelecas, PO, group, TSc,
             ci1 += self.contract_ss(tmp, ncas, nelecas).reshape(civec.shape)
             tmp = None
         ci1 *= self.ss_penalty
-        ci0 = super().contract_H(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, link_index, ts, t_nonzero, **kwargs)
+        ci0 = super().contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list, link_index, ts, t_nonzero, **kwargs)
         ci1 += ci0.reshape(civec.shape)
         return ci1
  
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index f4cde4f..ae64cbd 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -32,17 +32,17 @@
     J. Chem. Theory Comput. 2025 
 '''
 
-import sys
-
 import numpy
 from functools import reduce
 from pyscf import lib
 from pyscf.lib import logger
+from pyscf.scf import rohf
 from pyscf import __config__
 from itertools import product
 from pyscf.mcscf.casci import CASBase, CASCI
-from pyscf.fci import spin_op
+from pyscf.fci import cistring
 from pyscf.sfnoci.direct_sfnoci import SFNOCISolver
+from pyscf.sfnoci.direct_sfnoci import group_info_list, str2occ
 #from pyscf.fci import cistring
 #from scipy.linalg import eigh as gen_eig
 #from pyscf.fci import fci_slow
@@ -96,7 +96,7 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
 
     # 1e
     dmet_act_list = sfnoci.get_active_dm(mo_coeff)
-    h1e, energy_core = sfnoci.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
+    h1e, ecore_list = sfnoci.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
     t1 = log.timer('effective 1e hamiltonians and core energies', *t1)
 
     # 2e
@@ -106,7 +106,7 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     # FCI
     max_memory = max(400, sfnoci.max_memory-lib.current_memory()[0])
     e_tot, fcivec = sfnoci.fcisolver.kernel(h1e, eri, ncas, nelecas,
-                                            po_list, group, ov_list, ecore=energy_core,
+                                            po_list, group, ov_list, ecore_list,
                                             ci0=ci0, verbose=log,
                                             max_memory=max_memory)
 
@@ -120,9 +120,9 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     log.timer('All SFNOCI process', *t0)
 
     if isinstance(e_tot, (float, numpy.float64)):
-        e_cas = e_tot - energy_core
+        e_cas = e_tot - ecore_list
     else:
-        e_cas = [e - energy_core for e in e_tot] 
+        e_cas = [e - ecore_list for e in e_tot] 
 
     return e_tot, e_cas, fcivec
 
@@ -447,47 +447,246 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
 
     return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
 
-def optimize_mo(mf,mo_energy,mo_coeff,as_list,core_list,nelecas,mode=0,conv_tol = 1e-10, max_cycle = 100, groupA = None, thres = 0.2):
-    n_as = len(as_list)
-    n_ase = nelecas[0] + nelecas[1]
+def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None, 
+                        ncas = None, nelecas = None, ncore = None, conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
+                        dump_chk=True, dm0=None, callback=None, conv_check=True, **kwarg):
+    if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
+    if ncas is None: ncas = sfnoci.ncas
+    if nelecas is None : nelecas = sfnoci.nelecas
+    if ncore is None : ncore = sfnoci.ncore
+    mf = sfnoci._scf
+    assert isinstance(mf, rohf.ROHF), "The SCF class of SF-GNOCI must be ROHF class."
+
+    cput0 = (logger.process_clock(), logger.perf_counter())
+    stringsa = cistring.make_strings(range(ncas),nelecas[0])
+    stringsb = cistring.make_strings(range(ncas),nelecas[1])
+    na = len(stringsa)
+    nb = len(stringsb)
     N = mo_coeff.shape[0]
-    po_list = possible_occ(n_as, n_ase)
-    p = len(po_list)
-    group = None
-    if groupA is None:
-       optimized_mo=numpy.zeros((p,N,N))
-       #SF-NOCI
-       if mode == 0:
-          for i, occ in enumerate(po_list):
-              conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
-              print(conv, et)
-              optimized_mo[i]=moce
-              print("occuped pattern index:")
-              print(i)
-       #SF-CAS
-       if mode==1:
-          for i, occ in enumerate(po_list):
-              optimized_mo[i]=mo_coeff
-    else :
-        if isinstance(groupA, str):
-            group = grouping_by_lowdin(mf.mol,mo_coeff[:,as_list],po_list, groupA, thres= thres)
-        elif isinstance(groupA, list):
-            group = grouping_by_occ(po_list,groupA)
-        else: NotImplementedError
-        g = len(group)
-        optimized_mo = numpy.zeros((g,N,N))
-        for i in range(0,g):
-            if mode == 0:
-                occ = group_occ(po_list,group[i])
-                conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
+    as_list = numpy.array(range(ncore, ncore + ncas))
+    asn = len(as_list)
+    as_mo_coeff = mo_coeff[:,as_list]
+    group_info = group_info_list(ncas, nelecas, po_list, group)
+    group_info = group_info.reshape(-1)
+    #print(group_info)
+    #PO_info = SFNOCI.group_info_list(ncas, nelecas, PO, None).reshape[-1]
+    target_conf = numpy.where(group_info == target_group)[0]
+    #occ_list = PO_info[target_conf]
+    
+    
+    mol = mf.mol
+    #mf.max_cycle = max_cycle
+    as_dm_a = numpy.zeros((N,N))
+    as_dm_b = numpy.zeros((N,N))
+    #target_conf = [8]
+    #print(target_conf)
+    for conf in target_conf:
+        stra = conf // nb
+        strb = conf % nb
+        #print(stra, strb)
+        mo_occa = str2occ(stringsa[stra], ncas)
+        mo_occb = str2occ(stringsb[strb], ncas)
+        mo_occ = (mo_occa, mo_occb)
+        #print(mo_occ)
+        dm_a, dm_b = mf.make_rdm1(as_mo_coeff, mo_occ)
+        as_dm_a += dm_a
+        as_dm_b += dm_b
+    as_dm_a = as_dm_a / len(target_conf)
+    as_dm_b = as_dm_b / len(target_conf)
+    if dm0 is None:
+        core_mo_coeff = mo_coeff[:,:ncore]
+        dm0_core = (core_mo_coeff ).dot(core_mo_coeff.conj().T)
+        dm = numpy.asarray((dm0_core  + as_dm_a , dm0_core + as_dm_b))
+        #dm = 2 * dm0_core + AS_dm_a + AS_dm_b
+    else: 
+        dm = dm0
+    
+    h1e = mf.get_hcore(mol)
+    vhf = mf.get_veff(mol, dm)
+    e_tot = mf.energy_tot(dm, h1e, vhf)
+    logger.info(mf, 'init E= %.15g', e_tot)
+
+    scf_conv = False
+    s1e = mf.get_ovlp(mol)
+    cput1 = logger.timer(mf, 'initialize scf', *cput0)
+    
+    for cycle in range(max_cycle):
+        dm_last = dm
+        last_hf_e = e_tot
+       
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
+        I=numpy.identity(N-asn)
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
+        reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
+        new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
+        
+ 
+
+        for i in as_list:
+            new_mo_coeff=numpy.insert(new_mo_coeff,i, mo_coeff[:,i],axis=1)
+        mo_coeff=new_mo_coeff
+
+
+        as_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
+        for i in as_list:
+            new_mo_energy=numpy.insert(new_mo_energy,i,as_fock_energy[i])
+        mo_energy = new_mo_energy
+
+        core_mo_coeff = mo_coeff[:,:ncore]
+        dm_core = (core_mo_coeff).dot(core_mo_coeff.conj().T)
+        dm =numpy.asarray((dm_core + as_dm_a, dm_core + as_dm_b))
+        #dm = mf.make_rdm1(mo_coeff,mo_occ)
+        vhf = mf.get_veff(mol, dm, dm_last, vhf)
+        e_tot = mf.energy_tot(dm, h1e, vhf)
+
+        fock_last = fock
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        norm_ddm = numpy.linalg.norm(dm-dm_last)
+        logger.info(mf, 'cycle= %d E= %.15g  delta_E= %4.3g |ddm|= %4.3g',
+                    cycle+1, e_tot, e_tot-last_hf_e, norm_ddm)
+        if abs(e_tot-last_hf_e) < conv_tol and norm_ddm < numpy.sqrt(conv_tol):
+            scf_conv = True
+
+        cput1 = logger.timer(mf, 'cycle= %d'%(cycle+1), *cput1)
+
+        if scf_conv:
+            break
+
+    if scf_conv and conv_check:
+        # An extra diagonalization, to remove level shift
+        #fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf
+        mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
+        I=numpy.identity(N-asn)
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+
+        new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
+        reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
+        new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
+
+
+
+        for i in as_list:
+            new_mo_coeff=numpy.insert(new_mo_coeff,i,mo_coeff[:,i],axis=1)
+
+        mo_coeff=new_mo_coeff
+            
+        AS_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
+        for i in as_list:
+            new_mo_energy=numpy.insert(new_mo_energy,i,AS_fock_energy[i])
+        mo_energy=new_mo_energy
+        dm_last = dm
+        core_mo_coeff = mo_coeff[:,:ncore]
+        dm_core = (core_mo_coeff * 2).dot(core_mo_coeff.conj().T)
+        dm = (dm_core / 2 + as_dm_a, dm_core / 2 + as_dm_b)
+        
+        vhf = mf.get_veff(mol, dm,dm_last,vhf)
+        e_tot, last_hf_e = mf.energy_tot(dm, h1e, vhf), e_tot
+        fock = mf.get_fock(h1e, s1e, vhf, dm)
+        norm_ddm = numpy.linalg.norm(dm-dm_last)
+
+        if abs(e_tot-last_hf_e) < conv_tol or norm_ddm < conv_tol_grad:
+            scf_conv = True
+        logger.info(mf, 'Extra cycle  E= %.15g  delta_E= %4.3g |ddm|= %4.3g',
+                    e_tot, e_tot-last_hf_e, norm_ddm)
+    logger.timer(mf, 'scf_cycle', *cput0)
+
+    if scf_conv==False:
+        mo_coeff = sfnoci.mo_coeff
+
+    
+    return scf_conv, e_tot, mo_energy, mo_coeff
+
+# def optimize_mo(mf,mo_energy,mo_coeff,as_list,core_list,nelecas,mode=0,conv_tol = 1e-10, max_cycle = 100, groupA = None, thres = 0.2):
+#     n_as = len(as_list)
+#     n_ase = nelecas[0] + nelecas[1]
+#     N = mo_coeff.shape[0]
+#     po_list = possible_occ(n_as, n_ase)
+#     p = len(po_list)
+#     group = None
+#     if groupA is None:
+#        optimized_mo=numpy.zeros((p,N,N))
+#        #SF-NOCI
+#        if mode == 0:
+#           for i, occ in enumerate(po_list):
+#               conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
+#               print(conv, et)
+#               optimized_mo[i]=moce
+#               print("occuped pattern index:")
+#               print(i)
+#        #SF-CAS
+#        if mode==1:
+#           for i, occ in enumerate(po_list):
+#               optimized_mo[i]=mo_coeff
+#     else :
+#         if isinstance(groupA, str):
+#             group = grouping_by_lowdin(mf.mol,mo_coeff[:,as_list],po_list, groupA, thres= thres)
+#         elif isinstance(groupA, list):
+#             group = grouping_by_occ(po_list,groupA)
+#         else: NotImplementedError
+#         g = len(group)
+#         optimized_mo = numpy.zeros((g,N,N))
+#         for i in range(0,g):
+#             if mode == 0:
+#                 occ = group_occ(po_list,group[i])
+#                 conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
+#                 print(conv, et)
+#                 optimized_mo[i]=moce
+#                 print(occ)
+#             if mode == 1:
+#                 optimized_mo[i] = mo_coeff
+#             print("occuped pattern index:")
+#             print(i)
+#     return optimized_mo, po_list, group 
+
+def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = None, groupA = None, debug = False):
+      if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
+      if ncas is None : ncas = sfnoci.ncas
+      if nelecas is None : nelecas = sfnoci.nelecas
+      if ncore is None : ncore = sfnoci.ncore
+      if groupA is None : groupA = sfnoci._groupA
+      po_list = possible_occ(ncas, nelecas[0] + nelecas[1])
+      N=mo_coeff.shape[0]
+      p = len(po_list)
+      group = None
+      if groupA is None :
+            optimized_mo=numpy.zeros((p,N,N))
+            #SF-CAS
+            if debug:
+                for i, occ in enumerate(po_list):
+                    optimized_mo[i]=mo_coeff
+            #SF-NOCI
+            else:
+               for i, occ in enumerate(po_list):
+                conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore, conv_tol= sfnoci.fcisolver.conv_tol , max_cycle= sfnoci.fcisolver.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
-                print(occ)
-            if mode == 1:
+                print("occuped pattern index:")
+                print(i)
+        
+      else:
+          if isinstance(groupA, str):
+            group = grouping_by_lowdin(sfnoci.mol,mo_coeff[:,ncore:ncore+ncas],po_list, groupA, thres= sfnoci._thres)
+          elif isinstance(groupA, list):
+            group = grouping_by_occ(po_list,groupA)
+          else: NotImplementedError
+          g = len(group)
+          optimized_mo = numpy.zeros((g,N,N))
+          for i in range(0,g):
+            #SF-CAS
+            if debug:
                 optimized_mo[i] = mo_coeff
-            print("occuped pattern index:")
-            print(i)
-    return optimized_mo, po_list, group 
+            #SF-GNOCI
+            else:
+                conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff, ncas, nelecas, ncore, 
+                                                          conv_tol= sfnoci.fcisolver.conv_tol , max_cycle= sfnoci.fcisolver.max_cycle)
+                print(conv, et)
+                optimized_mo[i]=moce
+                print("occuped pattern index:")
+                print(i)
+
+      return optimized_mo, po_list, group 
 
 #def _construct_block_hamiltonian(mol,nelec,n_as,po_list,h1c,eri,ov_list,Kc,group):
 #    stringsa = cistring.make_strings(range(n_as),nelec[0])
@@ -687,16 +886,16 @@ def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None
     if ncas is None : ncas = sfnoci.ncas
     if ncore is None : ncore = sfnoci.ncore
     if mo_list is None:
-        mo_list, po_list, group = self.optimize_mo(self.mo_coeff)
+        mo_list, po_list, group = sfnoci.optimize_mo(sfnoci.mo_coeff)
     if dmet_core_list is None:
-        dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+        dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, group)
     if dmet_act_list is None:
-        dmet_act_list = sfnoci.get_active_dm(self.mo_coeff)
+        dmet_act_list = sfnoci.get_active_dm(sfnoci.mo_coeff)
     p = dmet_core_list.shape[0]           
     mo_cas = mo_list[0][:,ncore:ncore+ncas]
     hcore = sfnoci.get_hcore()
     h1e = numpy.zeros((p,p,ncas,ncas))       
-    energy_core = numpy.zeros(p)
+    ecore_list = numpy.zeros(p)
     energy_nuc = sfnoci.energy_nuc()   
     ha1e = lib.einsum('ai,ab,bj->ij',mo_cas,hcore,mo_cas)      
     
@@ -705,12 +904,12 @@ def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None
             corevhf = sfnoci.get_veff(dm = 2 * dmet_core_list[i,j])
             h1e[i,j] = ha1e + lib.einsum('ijab,ab -> ij', dmet_act_list , corevhf)
             if i==j:
-                 energy_core[i] += lib.einsum('ab,ab -> ', dmet_core_list[i,i],corevhf)
-                 energy_core[i] += energy_nuc
-                 energy_core[i] += 2*lib.einsum('ab,ab->', dmet_core_list[i,i], hcore)   
+                 ecore_list[i] += lib.einsum('ab,ab -> ', dmet_core_list[i,i],corevhf)
+                 ecore_list[i] += energy_nuc
+                 ecore_list[i] += 2*lib.einsum('ab,ab->', dmet_core_list[i,i], hcore)   
     sfnoci.h1e = h1e
-    sfnoci.core_energies = energy_core
-    return h1e, energy_core
+    sfnoci.core_energies = ecore_list
+    return h1e, ecore_list
                          
 def spin_square(sfnoci, rdm1, rdm2ab,rdm2ba):
     M_s = sfnoci.spin/2
@@ -743,7 +942,7 @@ def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None): #, spin = None,
       self.stdout = mol.stdout
       self.max_memory = mf.max_memory
       self.ncas = ncas
-      self._groupA = None 
+      self._groupA = groupA 
       if isinstance(nelecas, (int, numpy.integer)):
          raise NotImplementedError 
       else:
@@ -757,7 +956,7 @@ def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None): #, spin = None,
       self.fcisolver.max_cycle = getattr(__config__,
                                          'sfnoci_SFNOCI_fcisolver_max_cycle', 100)
       self.fcisolver.conv_tol = getattr(__config__,
-                                        'sfnoci_SFNOCI_fcisolver_conv_tol', 1e-10)
+                                        'sfnoci_SFNOCI_fcisolver_conv_tol', 5e-7)
 
 ################################################## don't modify the following attributes, they are not input options
       self.e_tot = 0       
@@ -818,17 +1017,18 @@ def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10,
 
   def optimize_mo(self, mo_coeff=None, debug=False, groupA=None, conv_tol=1e-10, max_cycle=100):
       if mo_coeff is None : mo_coeff = self.mo_coeff
-      if groupA is None : groupA = self.groupA
+      if groupA is None : groupA = self._groupA
       mode = 0
       if debug : mode = 1 
       mf = self._scf
-      mo_energy = mf.mo_energy
+      #mo_energy = mf.mo_energy
+      nelecas = self.nelecas
       ncore = self.ncore
-      ncas = self.ncas       
-      as_list = numpy.array(range(ncore,ncore + ncas))       
-      core_list = numpy.array(range(0,ncore))
-      mo_list, po_list, group = optimize_mo(self._scf,mo_energy,mo_coeff,as_list,core_list,self.nelecas, mode, conv_tol=conv_tol, max_cycle=max_cycle, groupA=groupA, thres=self.lowdin_thres)
-      self.AS_mo_coeff = mo_list[0][:,as_list]
+      ncas = self.ncas
+      mo_list, po_list, group = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, groupA, debug)       
+    #   as_list = numpy.array(range(ncore,ncore + ncas))       
+    #   core_list = numpy.array(range(0,ncore))
+    #   mo_list, po_list, group = optimize_mo(self._scf,mo_energy,mo_coeff,as_list,core_list,self.nelecas, mode, conv_tol=conv_tol, max_cycle=max_cycle, groupA=groupA, thres=self.lowdin_thres)
       return mo_list, po_list, group
                  
   def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
@@ -979,14 +1179,14 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
 #                           pspace_size= pspace_size, ecore= ecore, verbose=self.verbose, **kwargs)
 #      return e, c
 
-  def make_rdm1s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None, 
+  def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None, 
                  ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      if ci is None : ci = self.ci
-      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-          ci = ci[:,0]
+      #if ci is None : ci = self.ci
+      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
       if po_list is None or group is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
@@ -998,14 +1198,14 @@ def make_rdm1s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
                                     ncore, dmet_core_list, po_list, ov_list, group)
       return rdm1a, rdm1b
 
-  def make_rdm1(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+  def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      if ci is None : ci = self.ci
-      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-          ci = ci[:,0]
+      #if ci is None : ci = self.ci
+      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
       if po_list is None or group is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
@@ -1016,14 +1216,14 @@ def make_rdm1(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
                                      ncore, dmet_core_list, po_list, ov_list, group)
       return rdm
 
-  def make_rdm2s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+  def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      if ci is None : ci = self.ci
-      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-          ci = ci[:,0]
+      #if ci is None : ci = self.ci
+      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
       if po_list is None or group is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
@@ -1035,14 +1235,14 @@ def make_rdm2s(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
                                     ncore, dmet_core_list, po_list, ov_list, group)
       return rdm2aa, rdm2ab, rdm2ba, rdm2bb
   
-  def make_rdm2(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
+  def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      if ci is None : ci = self.ci
-      if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-          ci = ci[:,0]
+      #if ci is None : ci = self.ci
+      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
+      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
       if po_list is None or group is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
@@ -1103,14 +1303,12 @@ def make_rdm2(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
     as_list=[3,6,7,10]
     s1e = mol.intor('int1e_ovlp')
     mySFNOCI = SFNOCI(rm,4,(2,2),groupA = 'Li')
-    mySFNOCI.lowdin_thres= 0.2
+    mySFNOCI.lowdin_thres= 0.5
     mySFNOCI.fcisolver.nroots = 4 
     
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
     reei, _, ci = mySFNOCI.kernel(mo)
-    print(reei)
-
     i=1
     while i <= 4:
         x_list.append(i)
@@ -1122,7 +1320,7 @@ def make_rdm2(self, mo_coeff=None, ci=None, ncas=None, nelecas=None,
         m=scf.addons.mom_occ(m,mo0,setocc)
         m.scf(dm_ro)
 
-        mySFNOCI = SFNOCI(m,4,(2,2),groupA = 'Li')
+        mySFNOCI = SFNOCI(m,4,(2,2), groupA = 'Li')
         mySFNOCI.spin = 0
         mySFNOCI.fcisolver.nroots = 4 
         mo = addons.sort_mo(mySFNOCI,m.mo_coeff,as_list,1)

From 3bfde18b75ced014afc5092a3f2292d34fabf8c0 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Tue, 18 Feb 2025 12:49:32 +0900
Subject: [PATCH 03/10] sfnoci

---
 pyscf/sfnoci/sfnoci.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index ae64cbd..235f4de 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -659,7 +659,7 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
             #SF-NOCI
             else:
                for i, occ in enumerate(po_list):
-                conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore, conv_tol= sfnoci.fcisolver.conv_tol , max_cycle= sfnoci.fcisolver.max_cycle)
+                conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore, conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
                 print("occuped pattern index:")
@@ -680,7 +680,7 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
             #SF-GNOCI
             else:
                 conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff, ncas, nelecas, ncore, 
-                                                          conv_tol= sfnoci.fcisolver.conv_tol , max_cycle= sfnoci.fcisolver.max_cycle)
+                                                          conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
                 print("occuped pattern index:")
@@ -1305,7 +1305,7 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     mySFNOCI = SFNOCI(rm,4,(2,2),groupA = 'Li')
     mySFNOCI.lowdin_thres= 0.5
     mySFNOCI.fcisolver.nroots = 4 
-    
+
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
     reei, _, ci = mySFNOCI.kernel(mo)

From fbff1c09964701d2a48f933556d5d41532ee09fa Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Mon, 24 Feb 2025 17:47:52 +0900
Subject: [PATCH 04/10] Update

---
 pyscf/sfnoci/SFNOCI_contract.c | 134 +------
 pyscf/sfnoci/direct_sfnoci.py  | 300 ++++-----------
 pyscf/sfnoci/sfnoci.py         | 677 ++++++++++++++-------------------
 3 files changed, 378 insertions(+), 733 deletions(-)

diff --git a/pyscf/sfnoci/SFNOCI_contract.c b/pyscf/sfnoci/SFNOCI_contract.c
index aa636a8..987d16a 100644
--- a/pyscf/sfnoci/SFNOCI_contract.c
+++ b/pyscf/sfnoci/SFNOCI_contract.c
@@ -6,52 +6,9 @@
 #include <inttypes.h> //
 #include <unistd.h>  //
 
-bool is_matching_row(int *matrix_row, int *target_row, int cols) {
-    for (int i = 0; i < cols; i++) {
-        if (matrix_row[i] != target_row[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-int find_matching_row(int *matrix, int *occ, int nrows, int ncols) {
-    for (int i = 0; i < nrows; i++) {
-        int match = 1;
-        for (int j = 0; j < ncols; j++) {
-            if (matrix[i*ncols+j] != occ[j]) {
-                match = 0;
-                break;
-            }
-        }
-        if (match) return i;
-    }
-    return -1;  
-}
-
-int num_to_group(int *group, int group_sizes[], int num_groups, int number) {
-    int index = 0;
-    for (int i = 0; i < num_groups; i++) {
-        for (int j = 0; j < group_sizes[i]; j++) {
-            if (group[index] == number) {
-                return i; 
-            }
-            index++;
-        }
-    }
-    return -1; 
-}
-
-void str2occ(int *occ, uint64_t str, int norb) {
-    for (int i = 0; i < norb; i++) {
-        occ[i] = (str & (1ULL << i)) ? 1 : 0;
-    }
-}
 
 void SFNOCIcontract_H_spin1(double *erieff, double *ci0, double *ci1, int ncas, int nelecasa, int nelecasb, 
-int *PO, int PO_nrows, 
-int na, uint64_t *stringsa, int nb, uint64_t *stringsb,
-int *group, int group_sizes[], int num_groups, 
+int *conf_info_list, int na, uint64_t *stringsa, int nb, uint64_t *stringsb, int num,
 int *t1a, int *t1a_nonzero, int t1a_nonzero_size, 
 int *t1b, int *t1b_nonzero, int t1b_nonzero_size,
 int *t2aa, int *t2aa_nonzero, int t2aa_nonzero_size,
@@ -61,44 +18,21 @@ double *TSc, double *energy_core){
 int aa, ia, str1a, str0a;
 int ab, ib, str1b, str0b;
 int a1, i1, a2, i2;
-int* w_occa = (int*) malloc(ncas * sizeof(int));
-int* w_occb = (int*) malloc(ncas * sizeof(int));
-int* x_occa = (int*) malloc(ncas * sizeof(int));
-int* x_occb = (int*) malloc(ncas * sizeof(int));
-int* w_occ = (int*) malloc(ncas * sizeof(int));
-int* x_occ = (int*) malloc(ncas * sizeof(int));
 int p1, p2, p; 
-int num;
 
 for (int i = 0; i <t1a_nonzero_size; i++){
     aa = t1a_nonzero[i*4+0];
     ia = t1a_nonzero[i*4+1];
     str1a = t1a_nonzero[i*4+2];
     str0a = t1a_nonzero[i*4+3];
-    str2occ(w_occa, stringsa[str0a], ncas);
-    str2occ(x_occa, stringsa[str1a], ncas);
     
     for (int j = 0; j < t1b_nonzero_size; j++){
         ab = t1b_nonzero[j*4+0];
         ib = t1b_nonzero[j*4+1];
         str1b = t1b_nonzero[j*4+2];
         str0b = t1b_nonzero[j*4+3];
-        str2occ(w_occb, stringsb[str0b], ncas);
-        str2occ(x_occb, stringsb[str1b], ncas);
-        
-        for (int k = 0; k < ncas; k++) {
-                x_occ[k] = x_occa[k] + x_occb[k];
-                w_occ[k] = w_occa[k] + w_occb[k];
-            }
-        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
-        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
-        
-        num = PO_nrows;
-        if (group!= NULL){
-            p1 = num_to_group(group,group_sizes,num_groups,p1);
-            p2 = num_to_group(group,group_sizes,num_groups,p2);
-            num = num_groups;
-        }
+        p1 = conf_info_list[str1a * nb + str1b]; 
+        p2 = conf_info_list[str0a * nb + str0b];
 
         ci1[str1a * nb + str1b] += ci0[str0a * nb + str0b]
         *erieff[p1 * num * ncas * ncas* ncas* ncas 
@@ -119,24 +53,9 @@ for (int i =0; i <t2aa_nonzero_size; i++){
     i2 = t2aa_nonzero[i*6+3];
     str1a = t2aa_nonzero[i*6+4];
     str0a = t2aa_nonzero[i*6+5];
-    str2occ(w_occa, stringsa[str0a], ncas);
-    str2occ(x_occa, stringsa[str1a], ncas);
-    for (int str0b = 0; str0b < nb; str0b++){
-            str2occ(w_occb, stringsb[str0b], ncas);
-            for (int k = 0; k < ncas; k++) {
-                x_occ[k] = x_occa[k] + w_occb[k];
-                w_occ[k] = w_occa[k] + w_occb[k];
-            }
-        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
-        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
-        num = PO_nrows;
-        
-        if (group!= NULL){
-            p1 = num_to_group(group,group_sizes,num_groups,p1);
-            p2 = num_to_group(group,group_sizes,num_groups,p2);
-            num = num_groups;
-        }    
-        
+    for (int str0b = 0; str0b < nb; str0b++){  
+        p1 = conf_info_list[str1a * nb + str0b];
+        p2 = conf_info_list[str0a * nb + str0b];
         ci1[str1a * nb + str0b] += ci0[str0a * nb + str0b] 
         * erieff[p1 * num * ncas * ncas* ncas* ncas 
         + p2 * ncas * ncas * ncas * ncas 
@@ -156,25 +75,10 @@ for (int i =0; i <t2bb_nonzero_size; i++){
     i2 = t2bb_nonzero[i*6+3];
     str1b = t2bb_nonzero[i*6+4];
     str0b = t2bb_nonzero[i*6+5];
-    str2occ(w_occb, stringsb[str0b], ncas);
-    str2occ(x_occb, stringsb[str1b], ncas);
-    for (int str0a = 0; str0a < na; str0a++){
-        str2occ(w_occa, stringsa[str0a], ncas);
-        
+    for (int str0a = 0; str0a < na; str0a++){  
+        p1 = conf_info_list[str0a * nb + str1b];
+        p2 = conf_info_list[str0a * nb + str0b];
 
-            for (int k = 0; k < ncas; k++) {
-                x_occ[k] = w_occa[k] + x_occb[k];
-                w_occ[k] = w_occa[k] + w_occb[k];
-            }
-        p1 = find_matching_row(PO, x_occ, PO_nrows, ncas);
-        p2 = find_matching_row(PO, w_occ, PO_nrows, ncas);
-        num = PO_nrows;
-      
-        if (group!= NULL){
-            p1 = num_to_group(group,group_sizes,num_groups,p1);
-            p2 = num_to_group(group,group_sizes,num_groups,p2);
-            num = num_groups;
-        }    
         ci1[str0a * nb + str1b] += ci0[str0a * nb + str0b] 
         * erieff[p1 * num * ncas * ncas* ncas* ncas 
         + p2 * ncas * ncas * ncas * ncas 
@@ -188,26 +92,8 @@ for (int i =0; i <t2bb_nonzero_size; i++){
 }
 for (int str0a = 0; str0a < na; str0a++) {
         for (int str0b = 0; str0b < nb; str0b++) {
-
-            str2occ(w_occa, stringsa[str0a], ncas);
-            str2occ(w_occb, stringsb[str0b], ncas);
-
-            for (int k = 0; k < ncas; k++) {
-                w_occ[k] = w_occa[k] + w_occb[k];
-            }
-            p = find_matching_row(PO, w_occ, PO_nrows, ncas);
-            num = PO_nrows;
-            if (group!= NULL){
-            p = num_to_group(group,group_sizes,num_groups,p);
-            num = num_groups;
-            }
+            p = conf_info_list[str0a * nb + str0b];
             ci1[str0a * nb + str0b] += energy_core[p] * ci0[str0a * nb + str0b];
 }
 }
-free(w_occ);
-free(w_occa);
-free(w_occb);
-free(x_occ);
-free(x_occa);
-free(x_occb);
 }
\ No newline at end of file
diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
index 545e557..1d6e4f9 100644
--- a/pyscf/sfnoci/direct_sfnoci.py
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -52,42 +52,7 @@
 
 PENALTY = getattr(__config__, 'sfnoci_SFNOCI_fix_spin_shift', 0.2)
 
-def find_matching_rows(matrix, target_row):
-    matching_rows = numpy.where((matrix == target_row).all(axis=1))[0]
-    return matching_rows
-
-def str2occ(str0,norb):
-    occ=numpy.zeros(norb)
-    for i in range(norb):
-        if str0 & (1<<i):
-            occ[i]=1
-
-    return occ
- 
-def num_to_group(groups,number):
-    for i, group in enumerate(groups):
-        if number in group:
-            return i
-    return None
-
-def group_info_list(ncas, nelecas, PO, group):
-    stringsa = cistring.make_strings(range(0,ncas), nelecas[0])
-    stringsb = cistring.make_strings(range(0,ncas), nelecas[1])
-    na = len(stringsa)
-    nb = len(stringsb)
-    group_info = numpy.zeros((na,nb))
-    for stra, strsa in enumerate(stringsa):
-        for strb, strsb in enumerate(stringsb):
-            occa = str2occ(stringsa[stra],ncas)
-            occb = str2occ(stringsb[strb],ncas)
-            occ = occa + occb
-            p = find_matching_rows(PO,occ)[0]
-            if group is not None:
-                p = num_to_group(group,p)
-            group_info[stra, strb] = p
-    return group_info
-
-def make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
+def make_hdiag(h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt=None):
     if isinstance(nelecas, (int, numpy.integer)):
         nelecb = nelecas//2
         neleca = nelecas - nelecb
@@ -99,16 +64,14 @@ def make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
     diagj = numpy.einsum('iijj->ij', eri)
     diagk = numpy.einsum('ijji->ij', eri)
     hdiag = []
-    for aocc in occslista:
-        for bocc in occslistb:
+    for str0a, aocc in enumerate(occslista):
+        for str0b, bocc in enumerate(occslistb):
             occ = numpy.zeros(ncas)
             for i in aocc:
                 occ[i] += 1
             for i in bocc:
                 occ[i] +=1
-            p = find_matching_rows(po_list,occ)
-            if group is not None:
-                p = num_to_group(group, p)
+            p = conf_info_list[str0a, str0b]
             e1 = h1e[p,p,aocc,aocc].sum() + h1e[p,p,bocc,bocc].sum()
             e2 = diagj[aocc][:,aocc].sum() + diagj[aocc][:,bocc].sum() \
                + diagj[bocc][:,aocc].sum() + diagj[bocc][:,bocc].sum() \
@@ -119,7 +82,7 @@ def make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
 def absorb_h1e(h1e, eri, ncas, nelecas, fac=1):
     '''Modify 2e Hamiltonian to include effective 1e Hamiltonian contribution
 
-    input : h1e : (ngroup, ngroup, ncas, ncas)
+    input : h1e : (nbath, nbath, ncas, ncas)
             eri   : (ncas, ncas, ncas, ncas)
 
     return : erieff : (ngroup,ngroup,ncas,ncas,ncas,ncas)
@@ -184,7 +147,7 @@ def python_list_to_c_array(python_list):
             group_sizes[i] = group_size  
         return flat_list, group_sizes, num_groups
  
-def contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list,
+def contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list,
                link_index=None, ts=None, t_nonzero=None):
     '''Compute H|CI>
     '''
@@ -215,45 +178,25 @@ def contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list
     civec = numpy.asarray(civec, order = 'C')
     cinew = numpy.zeros_like(civec)
     erieff = numpy.asarray(erieff, order = 'C', dtype= numpy.float64)
-    po_list = numpy.asarray(po_list, order = 'C', dtype=numpy.int32)
-    po_nrows = po_list.shape[0]
-    cgroup, group_sizes, num_groups = python_list_to_c_array(group)
+    conf_info_list = numpy.asarray(conf_info_list, order = 'C', dtype = numpy.int32)
     stringsa = cistring.make_strings(range(ncas),neleca)
     stringsb = cistring.make_strings(range(ncas),nelecb)
-    # t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na), dtype=numpy.int32)
-    # t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb),dtype=numpy.int32)
-    # t1a = numpy.zeros((ncas,ncas,na,na),dtype=numpy.int32)
-    # t1b = numpy.zeros((ncas,ncas,nb,nb),dtype=numpy.int32) 
-    # for str0a , taba in enumerate(link_indexa):
-    #       for a1, i1, str1a, signa1 in link_indexa[str0a]:
-    #           t1a[a1,i1,str1a,str0a] += signa1 
-    #           for a2 , i2, str2a, signa2 in link_indexa[str1a]:
-    #               t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
-    # for str0b , tabb in enumerate(link_indexb):
-    #       for a1, i1, str1b, signb1 in link_indexb[str0b]:
-    #           t1b[a1,i1,str1b,str0b] += signb1
-    #           for a2 , i2, str2b, signb2 in link_indexb[str1b]:
-    #               t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
-    # t1a_nonzero = numpy.array(numpy.array(numpy.nonzero(t1a)).T, order = 'C', dtype = numpy.int32)
-    # t1b_nonzero = numpy.array(numpy.array(numpy.nonzero(t1b)).T, order = 'C', dtype = numpy.int32)
-    # t2aa_nonzero = numpy.array(numpy.array(numpy.nonzero(t2aa)).T, order = 'C', dtype = numpy.int32)
-    # t2bb_nonzero = numpy.array(numpy.array(numpy.nonzero(t2bb)).T, order = 'C', dtype = numpy.int32)
     t1ann = t1a_nonzero.shape[0]
     t1bnn = t1b_nonzero.shape[0]
     t2aann = t2aa_nonzero.shape[0]
     t2bbnn = t2bb_nonzero.shape[0]
     ov_list = numpy.asarray(ov_list, order = 'C', dtype=numpy.float64)
     ecore_list = numpy.asarray(ecore_list, order = 'C', dtype=numpy.float64)
-
+    mo_num = erieff.shape[0]
     libsf.SFNOCIcontract_H_spin1(erieff.ctypes.data_as(ctypes.c_void_p),
          civec.ctypes.data_as(ctypes.c_void_p),
          cinew.ctypes.data_as(ctypes.c_void_p),
          ctypes.c_int(ncas),
          ctypes.c_int(neleca), ctypes.c_int(nelecb),
-         po_list.ctypes.data_as(ctypes.c_void_p),ctypes.c_int(po_nrows),
+         conf_info_list.ctypes.data_as(ctypes.c_void_p),
          ctypes.c_int(na), stringsa.ctypes.data_as(ctypes.c_void_p), 
          ctypes.c_int(nb), stringsb.ctypes.data_as(ctypes.c_void_p),
-         cgroup, group_sizes, ctypes.c_int(num_groups),
+         ctypes.c_int(mo_num),
          t1a.ctypes.data_as(ctypes.c_void_p), 
          t1a_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t1ann),
          t1b.ctypes.data_as(ctypes.c_void_p), 
@@ -265,7 +208,7 @@ def contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list
          ov_list.ctypes.data_as(ctypes.c_void_p), ecore_list.ctypes.data_as(ctypes.c_void_p))
     return cinew
 
-def contract_H_slow(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, link_index=None):
+def contract_H_slow(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list, link_index=None):
     '''Compute H|CI>
     '''
     if isinstance(nelecas, (int, numpy.integer)):
@@ -304,58 +247,27 @@ def contract_H_slow(erieff, civec, ncas, nelecas, PO, group, TSc, energy_core, l
     t2bb_nonzero = numpy.array(numpy.nonzero(t2bb)).T
     for aa, ia, str1a, str0a in t1a_nonzero:
         for ab, ib, str1b, str0b in t1b_nonzero:
-            w_occa = str2occ(stringsa[str0a],ncas)
-            w_occb = str2occ(stringsb[str0b],ncas)
-            x_occa = str2occ(stringsa[str1a],ncas)
-            x_occb = str2occ(stringsb[str1b],ncas)
-            x_occ = x_occa + x_occb
-            w_occ = w_occa + w_occb
-            p1 = find_matching_rows(PO,x_occ)[0]
-            p2 = find_matching_rows(PO,w_occ)[0]
-            if group is not None:
-                p1 = num_to_group(group,p1)
-                p2 = num_to_group(group,p2)
-           
+            p1 = conf_info_list[str1a, str1b]
+            p2 = conf_info_list[str0a, str0b]
             cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] *t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] * TSc[p1,p2] *2
     for a1, i1, a2,i2, str1a, str0a in t2aa_nonzero:
         for str0b, stringb in enumerate(stringsb):
-            w_occa = str2occ(stringsa[str0a],ncas)
-            w_occb = str2occ(stringsb[str0b],ncas)
-            x_occa = str2occ(stringsa[str1a],ncas)
-            x_occ = x_occa + w_occb
-            w_occ = w_occa + w_occb
-            p1 = find_matching_rows(PO,x_occ)[0]
-            p2 = find_matching_rows(PO,w_occ)[0]
-            if group is not None:
-                p1 = num_to_group(group,p1)
-                p2 = num_to_group(group,p2)
+            p1 = conf_info_list[str1a, str0b]
+            p2 = conf_info_list[str0a, str0b]
             cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] *t2aa[a1,i1,a2,i2,str1a,str0a] * TSc[p1,p2]
     for a1, i1, a2,i2, str1b, str0b in t2bb_nonzero:
         for str0a, stringa in enumerate(stringsa):
-            w_occa = str2occ(stringsa[str0a],ncas)
-            w_occb = str2occ(stringsb[str0b],ncas)
-            x_occb = str2occ(stringsb[str1b],ncas)
-            x_occ = w_occa + x_occb
-            w_occ = w_occa + w_occb
-            p1 = find_matching_rows(PO,x_occ)[0]
-            p2 = find_matching_rows(PO,w_occ)[0]
-            if group is not None:
-                p1 = num_to_group(group,p1)
-                p2 = num_to_group(group,p2)
+            p1 = conf_info_list[str0a, str1b]
+            p2 = conf_info_list[str0a, str0b]
             cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2]* t2bb[a1,i1,a2,i2,str1b,str0b] * TSc[p1,p2]
     for str0a, stringa in enumerate(stringsa):
         for str0b, stringb in enumerate(stringsb):
-            w_occa = str2occ(stringsa[str0a],ncas)
-            w_occb = str2occ(stringsb[str0b],ncas)
-            w_occ = w_occa + w_occb
-            p = find_matching_rows(PO,w_occ)[0]
-            if group is not None:
-                p = num_to_group(group,p)
-            cinew[str0a,str0b] += energy_core[p] * civec[str0a,str0b]
+            p = conf_info_list[str0a, str0b]
+            cinew[str0a,str0b] += ecore_list[p] * civec[str0a,str0b]
     cinew.reshape(-1)
     return cinew
 
-def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list, ecore_list,
+def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, conf_info_list, ov_list, ecore_list,
                   ci0=None, link_index=None, tol=None, lindep=None, 
                   max_cycle=None, max_space=None, nroots=None, 
                   davidson_only=None, pspace_size=None, hop=None,
@@ -363,13 +275,21 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list, ecor
     '''
     Args:
         h1e: ndarray
-            1-electron Hamiltonian
+            effective 1-electron Hamiltonian defined in SF-NOCI space : (nbath, nbath, N, N)
         eri: ndarray
             2-electron integrals in chemist's notation
-        norb: int
-            Number of orbitals
-        nelec: int or (int, int)
-            Number of electrons of the system
+        ncas: int
+            Number of active orbitals
+        nelecas: (int, int)
+            Number of active electrons of the system
+        conf_info_list : ndarray, (nstringsa, nstringsb)
+            The optimized bath orbitals indices for each configuration.
+        ov_list : ndarray (nbath, nbath)
+            overlap matrix between different baths.
+        ecore_list : ndarray (nbath)
+            1D numpy array of core energies for each bath 
+
+
 
     Kwargs:
         ci0: ndarray
@@ -407,7 +327,7 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list, ecor
     log = logger.new_logger(sfnoci, verbose)
     nelec = nelecas
     assert (0 <= nelec[0] <= ncas and 0 <= nelec[1] <= ncas)
-    hdiag = sfnoci.make_hdiag(h1e, eri, ncas, nelec, po_list, group, ecore_list).ravel()
+    hdiag = sfnoci.make_hdiag(h1e, eri, ncas, nelec, conf_info_list, ecore_list).ravel()
     num_dets = hdiag.size
     civec_size = num_dets
     precond = sfnoci.make_precond(hdiag)
@@ -427,7 +347,7 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list, ecor
     if hop is None:
         cpu0 = [logger.process_clock(), logger.perf_counter()]
         def hop(c):
-            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, po_list, group, 
+            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, conf_info_list, 
                                    ov_list, ecore_list,link_index, ts, t_nonzero)
             cpu0[:] = log.timer_debug1('contract_H', *cpu0)
             return hc.ravel()
@@ -454,7 +374,7 @@ def init_guess():
                        tol_residual=None, **kwargs)
     return e, c
 
-def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
     N = mo_coeff.shape[0]
     mo_cas = mo_coeff[:,ncore:ncore+ncas]
     stringsa = cistring.make_strings(range(ncas),nelecas[0])
@@ -467,12 +387,7 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_l
     ci = ci.reshape(na,nb)
     for str0a, strsa in enumerate(stringsa):
         for str0b, strsb in enumerate(stringsb):
-            w_occa = str2occ(strsa,ncas)
-            w_occb = str2occ(strsb,ncas)
-            w_occ = w_occa + w_occb
-            p = find_matching_rows(po_list,w_occ)[0]
-            if group is not None: 
-                     p = num_to_group(group,p)
+            p = conf_info_list[str0a, str0b]
             rdm1c += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b]*dmet_core_list[p,p]
   
     rdm1asmoa = numpy.zeros((ncas,ncas))
@@ -480,30 +395,14 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_l
     for str0a , taba in enumerate(link_indexa):
         for aa, ia, str1a, signa in link_indexa[str0a]:
             for str0b, strsb in enumerate(stringsb):
-                w_occa = str2occ(stringsa[str0a],ncas)
-                w_occb = str2occ(strsb,ncas)
-                x_occa = str2occ(stringsa[str1a],ncas)
-                x_occ = x_occa + w_occb
-                w_occ = w_occa + w_occb   
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                p2 = find_matching_rows(po_list,w_occ)[0]
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
-                     p2 = num_to_group(group,p2) 
+                p1 = conf_info_list[str1a, str0b]
+                p2 = conf_info_list[str0a, str0b]
                 rdm1asmoa[aa,ia] += signa * numpy.conjugate(ci[str1a,str0b]) * ci[str0a,str0b] * ov_list[p1,p2]
     for str0b, tabb in enumerate(link_indexb):
         for ab, ib, str1b, signb in link_indexb[str0b]:
             for str0a, strsa in enumerate(stringsa):
-                w_occa = str2occ(strsa,ncas)
-                w_occb = str2occ(stringsb[str0b],ncas)
-                x_occb = str2occ(stringsb[str1b],ncas)
-                x_occ = w_occa + x_occb
-                w_occ = w_occa + w_occb  
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                p2 = find_matching_rows(po_list,w_occ)[0] 
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
-                     p2 = num_to_group(group,p2)       
+                p1 = conf_info_list[str0a, str1b]
+                p2 = conf_info_list[str0a, str0b]
                 rdm1asmob[ab,ib] += signb * numpy.conjugate(ci[str0a,str1b]) * ci[str0a,str0b] * ov_list[p1,p2]
     rdm1a = lib.einsum('ia,ab,jb -> ij', numpy.conjugate(mo_cas),rdm1asmoa,mo_cas)
     rdm1b = lib.einsum('ia,ab,jb-> ij', numpy.conjugate(mo_cas),rdm1asmob,mo_cas)
@@ -512,11 +411,11 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_l
     
     return rdm1a, rdm1b
 
-def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
-    rdm1a, rdm1b = make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
+def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+    rdm1a, rdm1b = make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
     return rdm1a + rdm1b
 
-def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, po_list, ov_list, group):
+def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_list, ov_list):
     mo_cas = mo_coeff[:,ncore:ncore+ncas]
     N = mo_coeff.shape[0]
     rdm2aa = numpy.zeros((N,N,N,N))
@@ -551,47 +450,24 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, po_list, ov_
                 t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
     for str0a, strs0a in enumerate(stringsa):
         for str0b, strs0b in enumerate(stringsb):
-            w_occa = str2occ(strs0a,ncas)
-            w_occb = str2occ(strs0b,ncas)
-            w_occ = w_occa + w_occb
-            p2 = find_matching_rows(po_list,w_occ)[0]
-            if group is not None: 
-               p2 = num_to_group(group,p2) 
+            p2 = conf_info_list[str0a, str0b]
             rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])) 
             rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
             rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
             rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))  
             for str1a, strs1a in enumerate(stringsa):
-                x_occa = str2occ(strs1a,ncas)
-                x_occ = x_occa + w_occb
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
+                p1 = conf_info_list[str1a, str0b]
                 rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*ov_list[p1,p2]
                 for k in range(ncas):
                     rdm2aaac[:,k,k,:] -= numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t1a[:,:,str1a,str0a]*ov_list[p1,p2]
             for str1b, strs1b in enumerate(stringsb):
-                x_occb = str2occ(strs1b,ncas)
-                x_occ = w_occa + x_occb
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
+                p1 = conf_info_list[str0a, str1b]
                 rdm2bbac[:,:,:,:] += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]*t2bb[:,:,:,:,str1b,str0b]*ov_list[p1,p2]
                 for k in range(ncas):
                     rdm2bbac[:,k,k,:] -= numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b] * t1b[:,:,str1b,str0b]*ov_list[p1,p2]
             for str1a, strs1a in enumerate(stringsa):
-                for str1b, strs1b in enumerate(stringsb):
-                    w_occa = str2occ(strs0a,ncas)
-                    w_occb = str2occ(strs0b,ncas)
-                    x_occa = str2occ(strs1a,ncas)
-                    x_occb = str2occ(strs1b,ncas)
-                    w_occ = w_occa + w_occb
-                    x_occ = x_occa + x_occb
-                    p1 = find_matching_rows(po_list,x_occ)[0] 
-                    p2 = find_matching_rows(po_list,w_occ)[0]
-                    if group is not None: 
-                       p1 = num_to_group(group,p1)
-                       p2 = num_to_group(group,p2)  
+                for str1b, strs1b in enumerate(stringsb): 
+                    p1 = conf_info_list[str1a, str1b]
                     rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*ov_list[p1,p2]
                     rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*ov_list[p1,p2]
     
@@ -606,42 +482,26 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, po_list, ov_
     for str0a, taba in enumerate(link_indexa):
         for str1a in numpy.unique(link_indexa[str0a][:,2]):
             for str0b, strsb in enumerate(stringsb):
-                w_occa = str2occ(stringsa[str0a],ncas)
-                w_occb = str2occ(strsb,ncas)
-                x_occa = str2occ(stringsa[str1a],ncas)
-                x_occ = x_occa + w_occb
-                w_occ = w_occa + w_occb   
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                p2 = find_matching_rows(po_list,w_occ)[0]
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
-                     p2 = num_to_group(group,p2) 
-                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],W[p1,p2,:,:]))*TSc[p1,p2]
+                p1 = conf_info_list[str1a, str0b]
+                p2 = conf_info_list[str0a, str0b]
+                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
   
     for str0b, tabb in enumerate(link_indexb):
         for str1b in numpy.unique(link_indexb[str0b][:,2]):
             for str0a, strsa, in enumerate(stringsa):
-                w_occa = str2occ(strsa,ncas)
-                w_occb = str2occ(stringsb[str0b],ncas)
-                x_occb = str2occ(stringsb[str1b],ncas)
-                x_occ = w_occa + x_occb
-                w_occ = w_occa + w_occb  
-                p1 = find_matching_rows(po_list,x_occ)[0]
-                p2 = find_matching_rows(po_list,w_occ)[0]
-                if group is not None: 
-                     p1 = num_to_group(group,p1)
-                     p2 = num_to_group(group,p2)  
-                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],W[p1,p2,:,:]))*TSc[p1,p2]
+                p1 = conf_info_list[str0a, str0b]
+                p2 = conf_info_list[str0a, str1b]
+                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
     
     return rdm2aa, rdm2ab, rdm2ba, rdm2bb
 
-def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
+def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
     rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
-        make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, po_list, ov_list, group)
+        make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, conf_info_list, ov_list)
     return rdm2aa + rdm2ab + rdm2ba + rdm2bb
 
 def fix_spin(fciobj, shift=PENALTY, ss=None, **kwargs):
@@ -696,8 +556,8 @@ def fix_spin_(fciobj, shift=.1, ss=None):
 class SFNOCISolver(FCISolver):
   '''SF-NOCI
   '''
-  def make_hdiag(self, h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt=None):
-      return make_hdiag(h1e, eri, ncas, nelecas, po_list, group, ecore_list, opt)
+  def make_hdiag(self, h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt=None):
+      return make_hdiag(h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt)
   
   def make_precond(self, hdiag, level_shift=0):
       return lib.make_diag_precond(hdiag, level_shift)
@@ -705,9 +565,9 @@ def make_precond(self, hdiag, level_shift=0):
   def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
       return absorb_h1e(h1e, eri, ncas, nelecas, fac)
 
-  def contract_H(self, erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+  def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
                  ecore_list, link_index=None, ts=None, t_nonzero=None):
-      return contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+      return contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
                         ecore_list ,link_index, ts, t_nonzero)
 
   def get_init_guess(self, ncas, nelecas, nroots, hdiag):
@@ -727,7 +587,7 @@ def eig(self, op, x0=None, precond=None, **kwargs):
             ci = ci[0]
         return e, ci
 
-  def kernel(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0=None,
+  def kernel(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0=None,
              tol=None, lindep=None, max_cycle=None, max_space=None,
              nroots=None, davidson_only=None, pspace_size=None,
              orbsym=None, wfnsym=None, **kwargs):
@@ -737,12 +597,11 @@ def kernel(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0
       assert self.spin is None or self.spin == 0
       self.norb = norb
       self.nelec = nelec
-      #link_index = fci.direct_spin1._unpack(norb, nelec, None)
       link_indexa = cistring.gen_linkstr_index(range(norb), nelec[0])
       link_indexb = cistring.gen_linkstr_index(range(norb), nelec[1])
       link_index = (link_indexa, link_indexb)
-
-      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0,
+     
+      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0,
                            link_index, tol, lindep, max_cycle, max_space, nroots,
                            davidson_only, pspace_size, **kwargs)
       self.eci = e
@@ -756,17 +615,17 @@ def kernel(self, h1e, eri, norb, nelec, po_list, group, ov_list, ecore_list, ci0
     
       return self.eci, self.ci
 
-  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
-      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
+  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
 
-  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
-      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
+  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list,conf_info_list, ov_list)
 
-  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, po_list, ov_list, group):
-      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
+  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, conf_info_list, ov_list):
+      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
 
-  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group):
-      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, po_list, ov_list, group)
+  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
  
   def contract_ss(self, civec, ncas=None, nelecas=None):
       if ncas is None : ncas = self.ncas
@@ -793,16 +652,7 @@ def fix_spin_(self, shift=PENALTY, ss = None):
   def spin_square(self, civec, ncas = None, nelecas = None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
-      return spin_op.spin_square0(civec, ncas, nelecas)
-
-#  def S2_list(self, mo_coeff = None, ci = None):
-#      if ci is None: ci = self.ci
-#      cin = ci.shape[1]
-#      S2_list = numpy.zeros(cin) 
-#      for i in range(cin):
-#          civec = ci[:,i]
-#          S2_list[i] = self.spin_square(civec)[0]
-#      return S2_list    
+      return spin_op.spin_square0(civec, ncas, nelecas)  
 
 class SpinPenaltySFNOCISolver:
     __name_mixin__ = 'SpinPenalty'
@@ -825,7 +675,7 @@ def undo_fix_spin(self):
     def base_contract_H(self, *args, **kwargs):
         return super().contract_H(*args, **kwargs)
     
-    def contract_H(self, erieff, civec, ncas, nelecas, po_list, group, ov_list, 
+    def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
                    ecore_list, link_index=None, ts=None, t_nonzero=None, **kwargs):
         if isinstance(nelecas, (int, numpy.number)):
             sz = (nelecas % 2) * .5
@@ -848,7 +698,7 @@ def contract_H(self, erieff, civec, ncas, nelecas, po_list, group, ov_list,
             ci1 += self.contract_ss(tmp, ncas, nelecas).reshape(civec.shape)
             tmp = None
         ci1 *= self.ss_penalty
-        ci0 = super().contract_H(erieff, civec, ncas, nelecas, po_list, group, ov_list, ecore_list, link_index, ts, t_nonzero, **kwargs)
+        ci0 = super().contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list, link_index, ts, t_nonzero, **kwargs)
         ci1 += ci0.reshape(civec.shape)
         return ci1
  
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index 235f4de..4da2d7c 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -42,10 +42,6 @@
 from pyscf.mcscf.casci import CASBase, CASCI
 from pyscf.fci import cistring
 from pyscf.sfnoci.direct_sfnoci import SFNOCISolver
-from pyscf.sfnoci.direct_sfnoci import group_info_list, str2occ
-#from pyscf.fci import cistring
-#from scipy.linalg import eigh as gen_eig
-#from pyscf.fci import fci_slow
 
 WITH_META_LOWDIN = getattr(__config__, 'scf_analyze_with_meta_lowdin', True)
 PRE_ORTH_METHOD = getattr(__config__, 'scf_analyze_pre_orth_method', 'ANO')
@@ -78,17 +74,20 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
 
     log = logger.new_logger(sfnoci, verbose)
     t0 = (logger.process_clock(), logger.perf_counter())
-    log.debug('Start SFNOCI')
+    if hasattr(sfnoci, '_groupA'): log.debug('Start SFGNOCI')
+    else: log.debug('Start SFNOCI')
+    
 
     ncas = sfnoci.ncas
     nelecas = sfnoci.nelecas
 
     # FASSCF
     mo_list, po_list, group = sfnoci.optimize_mo(mo_coeff)
+    conf_info_list = group_info_list(ncas, nelecas, po_list, group)
     t1 = log.timer('FASSCF', *t0)
 
     # SVD and core density matrix 
-    if group is None :
+    if group is None:
         dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, po_list)
     else:
         dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, group)
@@ -106,16 +105,10 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     # FCI
     max_memory = max(400, sfnoci.max_memory-lib.current_memory()[0])
     e_tot, fcivec = sfnoci.fcisolver.kernel(h1e, eri, ncas, nelecas,
-                                            po_list, group, ov_list, ecore_list,
+                                            conf_info_list, ov_list, ecore_list,
                                             ci0=ci0, verbose=log,
                                             max_memory=max_memory)
 
-#    e_tot, fcivec = kernel_SFNOCI(sfnoci, h1e, eri, ncas, nelecas, po_list, group, ov_list,
-#                         energy_core, ci0, link_index=None, tol = tol, 
-#                         lindep= lindep, max_cycle=max_cycle, max_space=max_space,
-#                         nroots=nroots, davidson_only= davidson_only,
-#                         pspace_size= pspace_size, ecore= ecore, verbose=verbose)
-
     log.timer('SFNOCI solver', *t1)
     log.timer('All SFNOCI process', *t0)
 
@@ -259,6 +252,41 @@ def biorthogonalize(mo1, mo2, s1e):
     mo1_bimo_coeff = mo1.dot(u)
     mo2_bimo_coeff = mo2.dot(vt.T)
     return s, mo1_bimo_coeff, mo2_bimo_coeff, u, vt
+
+def find_matching_rows(matrix, target_row):
+    matching_rows = numpy.where((matrix == target_row).all(axis=1))[0]
+    return matching_rows
+
+def str2occ(str0,norb):
+    occ=numpy.zeros(norb)
+    for i in range(norb):
+        if str0 & (1<<i):
+            occ[i]=1
+
+    return occ
+ 
+def num_to_group(groups,number):
+    for i, group in enumerate(groups):
+        if number in group:
+            return i
+    return None
+
+def group_info_list(ncas, nelecas, PO, group = None):
+    stringsa = cistring.make_strings(range(0,ncas), nelecas[0])
+    stringsb = cistring.make_strings(range(0,ncas), nelecas[1])
+    na = len(stringsa)
+    nb = len(stringsb)
+    group_info = numpy.zeros((na,nb))
+    for stra, strsa in enumerate(stringsa):
+        for strb, strsb in enumerate(stringsb):
+            occa = str2occ(stringsa[stra],ncas)
+            occb = str2occ(stringsb[strb],ncas)
+            occ = occa + occb
+            p = find_matching_rows(PO,occ)[0]
+            if group is not None:
+                p = num_to_group(group,p)
+            group_info[stra, strb] = p
+    return group_info.astype(int)
     
 def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
            dump_chk=True, dm0=None, callback=None, conv_check=True, **kwargs):
@@ -468,26 +496,18 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     as_mo_coeff = mo_coeff[:,as_list]
     group_info = group_info_list(ncas, nelecas, po_list, group)
     group_info = group_info.reshape(-1)
-    #print(group_info)
-    #PO_info = SFNOCI.group_info_list(ncas, nelecas, PO, None).reshape[-1]
     target_conf = numpy.where(group_info == target_group)[0]
-    #occ_list = PO_info[target_conf]
     
     
     mol = mf.mol
-    #mf.max_cycle = max_cycle
     as_dm_a = numpy.zeros((N,N))
     as_dm_b = numpy.zeros((N,N))
-    #target_conf = [8]
-    #print(target_conf)
     for conf in target_conf:
         stra = conf // nb
         strb = conf % nb
-        #print(stra, strb)
         mo_occa = str2occ(stringsa[stra], ncas)
         mo_occb = str2occ(stringsb[strb], ncas)
         mo_occ = (mo_occa, mo_occb)
-        #print(mo_occ)
         dm_a, dm_b = mf.make_rdm1(as_mo_coeff, mo_occ)
         as_dm_a += dm_a
         as_dm_b += dm_b
@@ -497,7 +517,6 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         core_mo_coeff = mo_coeff[:,:ncore]
         dm0_core = (core_mo_coeff ).dot(core_mo_coeff.conj().T)
         dm = numpy.asarray((dm0_core  + as_dm_a , dm0_core + as_dm_b))
-        #dm = 2 * dm0_core + AS_dm_a + AS_dm_b
     else: 
         dm = dm0
     
@@ -537,7 +556,6 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         core_mo_coeff = mo_coeff[:,:ncore]
         dm_core = (core_mo_coeff).dot(core_mo_coeff.conj().T)
         dm =numpy.asarray((dm_core + as_dm_a, dm_core + as_dm_b))
-        #dm = mf.make_rdm1(mo_coeff,mo_occ)
         vhf = mf.get_veff(mol, dm, dm_last, vhf)
         e_tot = mf.energy_tot(dm, h1e, vhf)
 
@@ -598,59 +616,17 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     
     return scf_conv, e_tot, mo_energy, mo_coeff
 
-# def optimize_mo(mf,mo_energy,mo_coeff,as_list,core_list,nelecas,mode=0,conv_tol = 1e-10, max_cycle = 100, groupA = None, thres = 0.2):
-#     n_as = len(as_list)
-#     n_ase = nelecas[0] + nelecas[1]
-#     N = mo_coeff.shape[0]
-#     po_list = possible_occ(n_as, n_ase)
-#     p = len(po_list)
-#     group = None
-#     if groupA is None:
-#        optimized_mo=numpy.zeros((p,N,N))
-#        #SF-NOCI
-#        if mode == 0:
-#           for i, occ in enumerate(po_list):
-#               conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
-#               print(conv, et)
-#               optimized_mo[i]=moce
-#               print("occuped pattern index:")
-#               print(i)
-#        #SF-CAS
-#        if mode==1:
-#           for i, occ in enumerate(po_list):
-#               optimized_mo[i]=mo_coeff
-#     else :
-#         if isinstance(groupA, str):
-#             group = grouping_by_lowdin(mf.mol,mo_coeff[:,as_list],po_list, groupA, thres= thres)
-#         elif isinstance(groupA, list):
-#             group = grouping_by_occ(po_list,groupA)
-#         else: NotImplementedError
-#         g = len(group)
-#         optimized_mo = numpy.zeros((g,N,N))
-#         for i in range(0,g):
-#             if mode == 0:
-#                 occ = group_occ(po_list,group[i])
-#                 conv, et, moe, moce, moocc = FASSCF(mf,as_list,core_list,mo_energy,mo_coeff,occ,conv_tol = conv_tol,max_cycle = max_cycle )
-#                 print(conv, et)
-#                 optimized_mo[i]=moce
-#                 print(occ)
-#             if mode == 1:
-#                 optimized_mo[i] = mo_coeff
-#             print("occuped pattern index:")
-#             print(i)
-#     return optimized_mo, po_list, group 
 
 def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = None, groupA = None, debug = False):
       if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
       if ncas is None : ncas = sfnoci.ncas
       if nelecas is None : nelecas = sfnoci.nelecas
       if ncore is None : ncore = sfnoci.ncore
-      if groupA is None : groupA = sfnoci._groupA
       po_list = possible_occ(ncas, nelecas[0] + nelecas[1])
       N=mo_coeff.shape[0]
       p = len(po_list)
       group = None
-      if groupA is None :
+      if not hasattr(sfnoci, '_groupA'):
             optimized_mo=numpy.zeros((p,N,N))
             #SF-CAS
             if debug:
@@ -666,6 +642,7 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
                 print(i)
         
       else:
+          groupA = sfnoci._groupA
           if isinstance(groupA, str):
             group = grouping_by_lowdin(sfnoci.mol,mo_coeff[:,ncore:ncore+ncas],po_list, groupA, thres= sfnoci._thres)
           elif isinstance(groupA, list):
@@ -688,201 +665,18 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
 
       return optimized_mo, po_list, group 
 
-#def _construct_block_hamiltonian(mol,nelec,n_as,po_list,h1c,eri,ov_list,Kc,group):
-#    stringsa = cistring.make_strings(range(n_as),nelec[0])
-#    stringsb = cistring.make_strings(range(n_as),nelec[1])
-#    link_indexa = cistring.gen_linkstr_index(range(n_as),nelec[0])
-#    link_indexb = cistring.gen_linkstr_index(range(n_as),nelec[1])
-#    na = cistring.num_strings(n_as,nelec[0])
-#    nb = cistring.num_strings(n_as,nelec[1])
-#    idx_a = numpy.arange(na)
-#    idx_b = numpy.arange(nb)
-#    mat1 = numpy.zeros((na,nb,na,nb))
-#    matov_list = numpy.zeros((na,nb,na,nb))
-#    for str0a, strs0a in enumerate(stringsa):
-#        for str1a, strsa in enumerate(stringsa):
-#            for str0b, strs0b in enumerate(stringsb):
-#                for str1b, strs1b in enumerate(stringsb):
-#                    w_occa = str2occ(stringsa[str0a],n_as)
-#                    w_occb = str2occ(stringsb[str0b],n_as)
-#                    x_occa = str2occ(stringsa[str1a],n_as)
-#                    x_occb = str2occ(stringsb[str1b],n_as)
-#                    x_occ = numpy.array(x_occa) + numpy.array(x_occb)
-#                    w_occ = numpy.array(w_occa) + numpy.array(w_occb)
-#                    p1=find_matching_rows(po_list,x_occ)[0]
-#                    p2=find_matching_rows(po_list,w_occ)[0]
-#                    if group is not None: 
-#                        p1 = num_to_group(group,p1)
-#                        p2 = num_to_group(group,p2)
-#                    matov_list[str1a,str1b,str0a,str0b] += ov_list[p1,p2]
-#    for str0a, taba in enumerate(link_indexa):
-#        for pa, qa, str1a, signa in taba:
-#            for str0b, strsb in enumerate(stringsb):
-#                 w_occa = str2occ(stringsa[str0a],n_as)
-#                 w_occb = str2occ(stringsb[str0b],n_as)
-#                 x_occa = str2occ(stringsa[str1a],n_as)
-#                 x_occ = numpy.array(x_occa) + numpy.array(w_occb)
-#                 w_occ = numpy.array(w_occa) + numpy.array(w_occb)
-#                 p1=find_matching_rows(po_list,x_occ)[0]
-#                 p2=find_matching_rows(po_list,w_occ)[0]
-#                 if group is not None: 
-#                    p1 = num_to_group(group,p1)
-#                    p2 = num_to_group(group,p2)
-#                 if matov_list[str1a,str0b,str0a,str0b]==0:
-#                    matov_list[str1a,str0b,str0a,str0b] += ov_list[p1,p2]
-#                 mat1[str1a,str0b,str0a,str0b] += signa * h1c[p1,p2,pa,qa]
-#    for str0b, tabb in enumerate(link_indexb):
-#        for pb, qb, str1b, signb in tabb:
-#            for str0a, strsa in enumerate(stringsa):
-#                w_occa = str2occ(stringsa[str0a],n_as)
-#                w_occb = str2occ(stringsb[str0b],n_as)
-#                x_occb = str2occ(stringsb[str1b],n_as)
-#                x_occ = numpy.array(w_occa) + numpy.array(x_occb)
-#                w_occ = numpy.array(w_occa) + numpy.array(w_occb)
-#                p1=find_matching_rows(po_list,x_occ)[0]
-#                p2=find_matching_rows(po_list,w_occ)[0]
-#                if group is not None: 
-#                   p1 = num_to_group(group,p1)
-#                   p2 = num_to_group(group,p2)
-#                if matov_list[str0a,str1b,str0a,str0b]==0:
-#                   matov_list[str0a,str1b,str0a,str0b] += ov_list[p1,p2]
-#                mat1[str0a,str1b,str0a,str0b] += signb * h1c[p1,p2,pb,qb]
-#    #mat1 = mat1.reshape(na*nb,na*nb)
-#    h2 = fci_slow.absorb_h1e(h1c[0,0]*0, eri, n_as, nelec)
-#    t1 = numpy.zeros((n_as,n_as,na,nb,na,nb))
-#    for str0, tab in enumerate(link_indexa):
-#        for a, i, str1, sign in tab:
-#            # alpha spin
-#            t1[a,i,str1,idx_b,str0,idx_b] += sign
-#    for str0, tab in enumerate(link_indexb):
-#        for a, i, str1, sign in tab:
-#            # beta spin
-#            t1[a,i,idx_a,str1,idx_a,str0] += sign
-#    t1 = lib.einsum('psqr,qrABab->psABab', h2, t1)
-#    mat2 = numpy.zeros((na,nb,na,nb))
-#    for str0, tab in enumerate(link_indexa):
-#        for a, i, str1, sign in tab:
-#            # alpha spin
-#            mat2[str1] += sign * t1[a,i,str0]
-#    for str0, tab in enumerate(link_indexb):
-#        for a, i, str1, sign in tab:
-#           # beta spin
-#            mat2[:,str1] += sign * t1[a,i,:,str0]
-#    #mat2 = mat2.reshape(na*nb,na*nb)
-#    ham = (mat1+0.5*mat2)*matov_list
-#    ham = ham.reshape(na*nb,na*nb)
-#    K = numpy.zeros((na,nb))
-#    for i in range(0,na):
-#        for j in range(0,nb):
-#            x_occa = str2occ(stringsa[i],n_as)
-#            x_occb = str2occ(stringsb[j],n_as)        
-#            x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
-#            p1=find_matching_rows(po_list,x_state_occ)[0]
-#            if group is not None: 
-#                p1 = num_to_group(group,p1) 
-#            K[i,j] = Kc[p1]
-#    K = K.reshape(-1)
-#    K = numpy.diag(K)
-#    print("mat1")
-#    print(mat1.reshape(na*nb,na*nb)) 
-#    print("mat2")
-#    print(0.5*mat2.reshape(na*nb,na*nb))
-#    print("K")
-#    print(K)
-#    
-#    hamiltonian = ham + K 
-#    print(hamiltonian)
-#    return hamiltonian
-
-#def construct_block_hamiltonian(mol,nelec,n_as,po_list,h1c,eri,ov_list,Kc,group):
-#    stringsa = cistring.make_strings(range(n_as),nelec[0])
-#    stringsb = cistring.make_strings(range(n_as),nelec[1])
-#    link_indexa = cistring.gen_linkstr_index(range(n_as),nelec[0])
-#    link_indexb = cistring.gen_linkstr_index(range(n_as),nelec[1])
-#    na = cistring.num_strings(n_as,nelec[0])
-#    nb = cistring.num_strings(n_as,nelec[1])
-#    idx_a = numpy.arange(na)
-#    idx_b = numpy.arange(nb)
-#    mat1 = numpy.zeros((na,nb,na,nb))
-#    matov_list = numpy.zeros((na,nb,na,nb))
-#    for str0a, taba in enumerate(link_indexa):
-#        for pa, qa, str1a, signa in taba:
-#            for str0b, tabb in enumerate(link_indexb):
-#                for pb, qb, str1b, signb in tabb:
-#                    w_occa = str2occ(stringsa[str0a],n_as)
-#                    w_occb = str2occ(stringsb[str0b],n_as)
-#                    x_occa = str2occ(stringsa[str1a],n_as)
-#                    x_occb = str2occ(stringsb[str1b],n_as)
-#                    x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
-#                    w_state_occ = numpy.array(w_occa) + numpy.array(w_occb)
-#                    p1=find_matching_rows(po_list,x_state_occ)[0]
-#                    p2=find_matching_rows(po_list,w_state_occ)[0]
-#                    if group is not None: 
-#                       p1 = num_to_group(group,p1)
-#                       p2 = num_to_group(group,p2) 
-#                    if matov_list[str1a,str1b,str0a,str0b]==0:
-#                        matov_list[str1a,str1b,str0a,str0b] += ov_list[p1,p2]    
-#                    if pa==qa and pb ==qb:
-#                        mat1[str1a,str1b,str0a,str0b] += (signa * h1c[p1,p2,pa,qa]/nelec[1]  + signb * h1c[p1,p2,pb,qb]/nelec[0])
-#                    elif pa!=qa and pb == qb:
-#                        mat1[str1a,str1b,str0a,str0b] += signa * h1c[p1,p2,pa,qa]/nelec[1]
-#                    elif pa==qa and pb !=qb:
-#                        mat1[str1a,str1b,str0a,str0b] += signb * h1c[p1,p2,pb,qb]/nelec[0]
-#                    elif pa!=qa and pb !=qb:
-#                        mat1[str1a,str1b,str0a,str0b] += 0
-#                    #mat1[str1a,idx_b,str0a,idx_b] += signa * h1c[g1,g2,pa,qa]
-#                    #mat1[idx_a,str1b,idx_a,str0b] += signb * h1c[g1,g2,pb,qb]
-#
-#    #mat1 = mat1.reshape(na*nb,na*nb)
-#    h2 = fci_slow.absorb_h1e(h1c[0,0]*0, eri, n_as, nelec)
-#    t1 = numpy.zeros((n_as,n_as,na,nb,na,nb))
-#    for str0, tab in enumerate(link_indexa):
-#        for a, i, str1, sign in tab:
-#            # alpha spin
-#            t1[a,i,str1,idx_b,str0,idx_b] += sign
-#    for str0, tab in enumerate(link_indexb):
-#        for a, i, str1, sign in tab:
-#            # beta spin
-#            t1[a,i,idx_a,str1,idx_a,str0] += sign
-#    t1 = lib.einsum('psqr,qrABab->psABab', h2, t1)
-#    mat2 = numpy.zeros((na,nb,na,nb))
-#    for str0, tab in enumerate(link_indexa):
-#        for a, i, str1, sign in tab:
-#            # alpha spin
-#            mat2[str1] += sign * t1[a,i,str0]
-#    for str0, tab in enumerate(link_indexb):
-#        for a, i, str1, sign in tab:
-#           # beta spin
-#            mat2[:,str1] += sign * t1[a,i,:,str0]
-#    #mat2 = mat2.reshape(na*nb,na*nb)
-#    ham = (mat1+0.5*mat2)*matov_list
-#    ham = ham.reshape(na*nb,na*nb)
-#    K = numpy.zeros((na,nb))
-#    for i in range(0,na):
-#        for j in range(0,nb):
-#            x_occa = str2occ(stringsa[i],n_as)
-#            x_occb = str2occ(stringsb[j],n_as)        
-#            x_state_occ = numpy.array(x_occa) + numpy.array(x_occb)
-#            p1=find_matching_rows(po_list,x_state_occ)[0]
-#            if group is not None: 
-#                p1 = num_to_group(group,p1) 
-#            K[i,j] = Kc[p1]
-#    K = K.reshape(-1)
-#    K = numpy.diag(K)
-#    print("mat1")
-#    print(mat1.reshape(na*nb,na*nb)) 
-#    print("mat2")
-#    print(0.5*mat2.reshape(na*nb,na*nb))
-#    print("K")
-#    print(K)
-#    
-#    hamiltonian = ham + K 
-#    print(hamiltonian)
-#    
-#    return hamiltonian
 
 def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None, 
                    ncas=None, ncore=None):
+    ''' SF-NOCI space one-electron hamiltonian
+
+    Args:
+        sfnoci : a SF-NOCI/SF-GNOCI object
+    
+    Returns:
+        A tuple, A tuple, the first is the effective one-electron hamiltonian defined in SF-NOCI space,
+        the second is the list of electronic energy from baths.
+    '''
     if ncas is None : ncas = sfnoci.ncas
     if ncore is None : ncore = sfnoci.ncore
     if mo_list is None:
@@ -920,20 +714,66 @@ def spin_square(sfnoci, rdm1, rdm2ab,rdm2ba):
     
     return M_s**2 + 0.5*lib.einsum('ii ->',rdm1mo) - 0.5*lib.einsum('ijji ->', rdm2mo)       
 
-#def make_diag_precond(hdiag, level_shift=0):
-#    return lib.make_diag_precond(hdiag, level_shift)
 
 class SFNOCI(CASBase):
   '''SF-NOCI
-  dmet_core_list : density matrix of core orbitals between different bath in atomic basis : (ngroup, ngroup, N, N)
-  po_list : Possible occupation pattern. 
-       for example, for (2e, 2o): po_list = [[0,2], [1,1], [2,0]]. It is 2D numpy array.
-  h1e : effective one electron hamiltonian : (ngroup, ngroup, ncas, ncas)
-  ov_list : overlap between different bath : (ngroup, ngroup)
-  dmet_act_list : density matrix between specific two active orbitals in atomic basis : (ncas, ncas, N, N)
-  core_energies : 1D numpy array of core energies for each bath : (ngroup)
+
+    Args:
+        mf : SCF object
+            SCF to define the problem size and SCF type of FASSCF.
+            The ROHF object is recommended.
+        ncas : int
+            Number of active orbitals
+        nelecas : a pair of int
+            Number of electrons in active space
+    
+    Kwargs:
+        ncore : int
+            Number of doubly occupied core orbitals. If not presented, this
+            parameter can be automatically determined.
+
+    Attributes:
+        verbose : int
+            Print level.  Default value equals to :class:`Mole.verbose`.
+        max_memory : float or int
+            Allowed memory in MB.  Default value equals to :class:`Mole.max_memory`.
+        ncas : int
+            Active space size.
+        nelecas : tuple of int
+            Active (nelec_alpha, nelec_beta)
+        ncore : int or tuple of int
+            Core electron number.
+        fcisolver : an instance of :class:`FCISolver`
+            The SFNOCISolver in pyscf.sfnoci.direct_sfnoci module must be used. 
+            Other moldules in pyscf.fci cannot be used.
+            You can control FCIsolver by setting e.g.::
+
+            >>> mc.fcisolver.max_cycle = 30
+            >>> mc.fcisolver.conv_tol = 1e-7
+
+    Key variables :    
+        N : The basis number 
+
+        po_list : A list of possible occupation patterns.
+            for example, for (2e, 2o): po_list = [[0,2], [1,1], [2,0]]. It is 2D numpy array.
+
+        mo_list : ndarray (nbath , N, N)
+            The optimized molecular orbital set by FASSCF. the nbath is equal to length of po_list.
+
+        conf_info_list : ndarray, (nstringsa, nstringsb)
+            The optimized bath orbitals indices for each configuration.
+
+        dmet_core_list : density matrix of core orbitals between different bath in atomic basis : (nbath, nbath, N, N)
+
+        h1e : effective one electron hamiltonian : (nbath, nbath, ncas, ncas)
+
+        ov_list : overlap between different bath : (nbath, nbath)
+
+        dmet_act_list : density matrix between specific two active orbitals in atomic basis : (ncas, ncas, N, N)
+
+        ecore_list : 1D numpy array of core energies for each bath : (ngroup)
   '''
-  def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None): #, spin = None, mo_coeff = None, mo_occ = None, groupA = None, mode = 0, thres = 0.2):  
+  def __init__(self, mf, ncas, nelecas, ncore=None): 
       
       mol = mf.mol
       self.mol = mol
@@ -942,7 +782,6 @@ def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None): #, spin = None,
       self.stdout = mol.stdout
       self.max_memory = mf.max_memory
       self.ncas = ncas
-      self._groupA = groupA 
       if isinstance(nelecas, (int, numpy.integer)):
          raise NotImplementedError 
       else:
@@ -984,22 +823,6 @@ def spin(self,x):
       nelecb = (necas- x)//2
       neleca = necas - nelecb
       self.nelecas = (neleca,nelecb)
-
-  @property
-  def groupA(self):
-      return self._groupA
-  
-  @groupA.setter
-  def groupA(self,x):
-      self._groupA = x
-
-  @property
-  def lowdin_thres(self):
-      return self._thres
-  
-  @lowdin_thres.setter
-  def lowdin_thres(self, x):
-      self._thres = x
                             
   def possible_occ(self):
       po_list = possible_occ(self.ncas, sum(self.nelecas))
@@ -1015,27 +838,21 @@ def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10,
       FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
       return FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ 
 
-  def optimize_mo(self, mo_coeff=None, debug=False, groupA=None, conv_tol=1e-10, max_cycle=100):
+  def optimize_mo(self, mo_coeff=None, debug=False, conv_tol=1e-10, max_cycle=100):
       if mo_coeff is None : mo_coeff = self.mo_coeff
-      if groupA is None : groupA = self._groupA
       mode = 0
       if debug : mode = 1 
       mf = self._scf
-      #mo_energy = mf.mo_energy
       nelecas = self.nelecas
       ncore = self.ncore
       ncas = self.ncas
-      mo_list, po_list, group = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, groupA, debug)       
-    #   as_list = numpy.array(range(ncore,ncore + ncas))       
-    #   core_list = numpy.array(range(0,ncore))
-    #   mo_list, po_list, group = optimize_mo(self._scf,mo_energy,mo_coeff,as_list,core_list,self.nelecas, mode, conv_tol=conv_tol, max_cycle=max_cycle, groupA=groupA, thres=self.lowdin_thres)
-      return mo_list, po_list, group
+      mo_list, po_list, _ = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, debug = debug)       
+      return mo_list, po_list, _
                  
   def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
       if mo_list is None or po_list_or_group is None:
-          mo_list, po_list, group = self.optimize_mo(self.mo_coeff)
-          if group is None : po_list_or_group = po_list
-          else : po_list_or_group = group 
+          mo_list, po_list, _ = self.optimize_mo(self.mo_coeff)
+          po_list_or_group = po_list
       ncore = self.ncore
       ncas = self.ncas  
       s1e = self._scf.get_ovlp(self.mol)
@@ -1081,56 +898,6 @@ def get_h2eff(self, mo_coeff=None):
       '''
       return CASCI.get_h2eff(self,mo_coeff)
 
-#  def construct_reduced_hamiltonian(self, mo_coeff = None, h1e= None, energy_core = None, po_list = None, ov_list = None):
-#      if mo_coeff is None : mo_coeff = self.mo_coeff
-#      if h1e is None and energy_core is None: h1e, energy_core = self.get_h1e()
-#      if po_list is None : po_list = self.possible_occ()
-#      if ov_list is None : ov_list = ov_list          
-#      nelecas = self.nelecas   
-#      ncas = self.ncas  
-#      mol = self.mol
-#      group = self.group
-#      eri = self.get_h2eff(mo_coeff) 
-#      hamiltonian = _construct_block_hamiltonian(mol,nelecas,ncas,po_list,h1e,eri,ov_list,energy_core,group)
-#      return hamiltonian
-             
-#  def kernel_diag(self,mo = None, debug = False):
-#      '''
-#      Solve CI problem by just diagonalizing Hamiltonian matrix
-#      '''
-#      if mo is not None: self.mo_coeff = mo
-#      cput0 = (logger.process_clock(), logger.perf_counter())
-#      mo_list, po_list, group = self.optimize_mo(mo, debug)
-#      cput1 = logger.timer(self, 'core-vir rotation', *cput0)
-#      dmet_act_list = self.get_active_dm(mo)
-#      if group is None :
-#         dmet_core_list, ov_list = self.get_svd_matrices(mo_list , po_list)
-#      else: dmet_core_list, ov_list = self.get_svd_matrices(mo_list , group)
-#      cput1 = logger.timer(self,'SVD and core density matrix calculation', *cput1)          
-#      h1e, energy_core = self.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
-#      cput1 = logger.timer(self,'effective 1e hamiltonians and core energies calculation', *cput1)
-#      #self.mo_eri = self.get_h2eff(mo)
-#      #cput1 = logger.timer(self, 'effective 2e hamiltonian calculation', *cput1)
-#      hamiltonian = self.construct_reduced_hamiltonian(mo, h1e, energy_core, po_list, ov_list)
-#      eigenvalues, eigenvectors = gen_eig(hamiltonian)
-#      self.ci = eigenvectors
-#      logger.timer(self, 'CI solving', *cput1)
-#      logger.timer(self, 'All process', *cput0)
-#      return eigenvalues, eigenvectors
-  
-#  def matrix_kernel(self, mo = None, debug = False):
-#      '''Calculate necessary matrices before constructing hamiltonian.
-#      '''
-#      if mo is not None : self.mo_coeff = mo
-#      mo_list, po_list, group = self.optimize_mo(mo, debug = debug)
-#      dmet_act_list = self.get_active_dm(mo)
-#      if group is None :
-#         dmet_core_list, ov_list = self.get_svd_matrices(mo_list , po_list)
-#      else: dmet_core_list, ov_list = self.get_svd_matrices(mo_list , group)       
-#      h1e, energy_core = self.get_h1cas(dmet_act_list , mo_list , dmet_core_list)
-#      #self.mo_eri = self.get_h2eff(mo)
-#      return mo_list, po_list, group, dmet_core_list, h1e, energy_core, ov_list
-
   def kernel(self, mo_coeff=None, ci0=None, verbose=None):
       '''
       Returns:
@@ -1169,88 +936,228 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
       #self._finalize()
       return self.e_tot, self.e_cas, self.ci   # will provide group info
   
-#  def skip_scf(self, mo = None, ci0=None,
-#               tol=None, lindep=None, max_cycle=None, max_space=None,
-#               nroots=None, davidson_only=None, pspace_size=None,
-#               orbsym=None, wfnsym=None, ecore=0, **kwargs ):
-#      if mo is not None: self.mo_coeff
-#      e, c = kernel_SFNOCI(self, self.h1e, self.mo_eri, self.ncas, self.nelecas, self.po_list, self.group, ov_list, self.core_energies, ci0, link_index=None,
-#                           tol = tol, lindep= lindep, max_cycle=max_cycle, max_space=max_space, nroots=nroots, davidson_only= davidson_only,
-#                           pspace_size= pspace_size, ecore= ecore, verbose=self.verbose, **kwargs)
-#      return e, c
 
   def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None, 
-                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if conf_info_list is None:
+          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+      rdm1a, rdm1b = \
+          self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+                                    ncore, dmet_core_list, conf_info_list, ov_list)
+      return rdm1a, rdm1b
+
+  def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if conf_info_list is None:
+          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+      rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
+                                     ncore, dmet_core_list, conf_info_list, ov_list)
+      return rdm
+
+  def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
+                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if conf_info_list is None:
+          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+      rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
+          self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas, 
+                                    ncore, dmet_core_list, conf_info_list, ov_list)
+      return rdm2aa, rdm2ab, rdm2ba, rdm2bb
+  
+  def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      #if ci is None : ci = self.ci
-      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if po_list is None or group is None:
+      if conf_info_list is None:
+          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list)
+      if dmet_core_list is None or ov_list is None:
+          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+      rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas, 
+                                     ncore, dmet_core_list, conf_info_list, ov_list)
+      return rdm
+
+class SFGNOCI(SFNOCI):
+    '''SF-GNOCI
+        groupA : str or list
+            The critertion of grouping the configurations
+            str : the name of atom to become the critertion of grouping
+            list : Grouping the configurations by occupation number of molecular orbitals
+
+            groupA = 'Li'
+
+            or
+
+            groupA = [[0,1],[2,3]]
+
+        lowdin_thres : float
+            The criterion of grouping the configurations if the lowdin basis is used.
+
+        group : list 
+            The result of grouping.
+
+    '''
+    def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None):
+        super().__init__(mf, ncas, nelecas, ncore)
+        self._groupA = groupA 
+        self._thres = 0.2 
+    
+    @property
+    def groupA(self):
+      return self._groupA
+  
+    @groupA.setter
+    def groupA(self,x):
+      self._groupA = x
+    
+    @property
+    def lowdin_thres(self):
+        return self._thres
+  
+    @lowdin_thres.setter
+    def lowdin_thres(self, x):
+        self._thres = x
+    
+    def kernel(self, mo_coeff=None, ci0=None, verbose=None):
+      '''
+      Returns:
+          Five elements, they are
+          total energy,
+          active space CI energy,
+          the active space FCI wavefunction coefficients,
+          the MCSCF canonical orbital coefficients,
+          the MCSCF canonical orbital coefficients.
+
+      They are attributes of mcscf object, which can be accessed by
+      .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
+      '''
+      if mo_coeff is None:
+          mo_coeff = self.mo_coeff
+      else:
+          self.mo_coeff = mo_coeff
+      if ci0 is None:
+          ci0 = self.ci
+      log = logger.new_logger(self, verbose)
+
+      #self.check_sanity()
+      #self.dump_flags(log)
+
+      self.e_tot, self.e_cas, self.ci = \
+              kernel(self, mo_coeff, ci0=ci0, verbose=log)
+
+      if getattr(self.fcisolver, 'converged', None) is not None:
+          self.converged = numpy.all(self.fcisolver.converged)
+          if self.converged:
+              log.info('SFGNOCI converged')
+          else:
+              log.info('SFGNOCI not converged')
+      else:
+          self.converged = True
+      #self._finalize()
+      return self.e_tot, self.e_cas, self.ci   # will provide group info
+    
+    def optimize_mo(self, mo_coeff=None, debug=False, groupA = None, conv_tol=1e-10, max_cycle=100):
+      if mo_coeff is None : mo_coeff = self.mo_coeff
+      if groupA is None : groupA = self._groupA
+      mode = 0
+      if debug : mode = 1 
+      mf = self._scf
+      nelecas = self.nelecas
+      ncore = self.ncore
+      ncas = self.ncas
+      mo_list, po_list, group = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, groupA, debug)       
+      return mo_list, po_list, group
+
+    def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None, 
+                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
+      if ncas is None : ncas = self.ncas
+      if nelecas is None : nelecas = self.nelecas
+      if ncore is None : ncore = self.ncore
+      if mo_coeff is None : mo_coeff = self.mo_coeff  
+      if conf_info_list is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
       if dmet_core_list is None or ov_list is None:
           dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
 
       rdm1a, rdm1b = \
           self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
-                                    ncore, dmet_core_list, po_list, ov_list, group)
+                                    ncore, dmet_core_list, conf_info_list, ov_list)
       return rdm1a, rdm1b
 
-  def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
-                ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+    def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      #if ci is None : ci = self.ci
-      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if po_list is None or group is None:
+      if conf_info_list is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
       if dmet_core_list is None or ov_list is None:
           dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
 
       rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
-                                     ncore, dmet_core_list, po_list, ov_list, group)
+                                     ncore, dmet_core_list, conf_info_list, ov_list)
       return rdm
 
-  def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
-                 ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+    def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
+                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      #if ci is None : ci = self.ci
-      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if po_list is None or group is None:
+      if conf_info_list is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
       if dmet_core_list is None or ov_list is None:
           dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
 
       rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
           self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas, 
-                                    ncore, dmet_core_list, po_list, ov_list, group)
+                                    ncore, dmet_core_list,conf_info_list, ov_list)
       return rdm2aa, rdm2ab, rdm2ba, rdm2bb
   
-  def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
-                ncore=None, dmet_core_list=None, po_list=None, ov_list=None, group=None):
+    def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
       if ncas is None : ncas = self.ncas
       if nelecas is None : nelecas = self.nelecas
       if ncore is None : ncore = self.ncore
-      #if ci is None : ci = self.ci
-      #if isinstance(ci, numpy.ndarray) and ci.ndim != 1:
-      #    ci = ci[:,0]
       if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if po_list is None or group is None:
+      if conf_info_list is None:
           mo_list, po_list, group = self.optimize_mo(mo_coeff)
+          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
       if dmet_core_list is None or ov_list is None:
           dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
 
       rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas, 
-                                     ncore, dmet_core_list, po_list, ov_list, group)
+                                     ncore, dmet_core_list,conf_info_list, ov_list)
       return rdm
 
 if  __name__ == '__main__':
@@ -1302,13 +1209,15 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     mo=rm.mo_coeff
     as_list=[3,6,7,10]
     s1e = mol.intor('int1e_ovlp')
-    mySFNOCI = SFNOCI(rm,4,(2,2),groupA = 'Li')
+    mySFNOCI = SFGNOCI(rm,4,(2,2),groupA = 'Li')
     mySFNOCI.lowdin_thres= 0.5
+    mySFNOCI = SFNOCI(rm, 4, (2,2))
     mySFNOCI.fcisolver.nroots = 4 
 
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
     reei, _, ci = mySFNOCI.kernel(mo)
+
     i=1
     while i <= 4:
         x_list.append(i)
@@ -1320,7 +1229,7 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         m=scf.addons.mom_occ(m,mo0,setocc)
         m.scf(dm_ro)
 
-        mySFNOCI = SFNOCI(m,4,(2,2), groupA = 'Li')
+        mySFNOCI = SFGNOCI(m,4,(2,2), groupA = 'Li')
         mySFNOCI.spin = 0
         mySFNOCI.fcisolver.nroots = 4 
         mo = addons.sort_mo(mySFNOCI,m.mo_coeff,as_list,1)

From 9823f01d6c0568136681fb3e7be4dce5ababa7c8 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Mon, 24 Feb 2025 17:48:49 +0900
Subject: [PATCH 05/10] sfnoci

---
 pyscf/sfnoci/sfnoci.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index 4da2d7c..03de5ef 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -1021,7 +1021,6 @@ class SFGNOCI(SFNOCI):
 
         group : list 
             The result of grouping.
-
     '''
     def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None):
         super().__init__(mf, ncas, nelecas, ncore)

From 0c3b4093bec9ec333fabfc8c844524ac0f40ae28 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Mon, 24 Feb 2025 17:49:12 +0900
Subject: [PATCH 06/10] sfnoci

---
 pyscf/sfnoci/direct_sfnoci.py | 2 --
 pyscf/sfnoci/sfnoci.py        | 1 +
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
index 1d6e4f9..bd11d0a 100644
--- a/pyscf/sfnoci/direct_sfnoci.py
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -289,8 +289,6 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, conf_info_list, ov_list, ecor
         ecore_list : ndarray (nbath)
             1D numpy array of core energies for each bath 
 
-
-
     Kwargs:
         ci0: ndarray
             Initial guess
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index 03de5ef..bd2395c 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -1021,6 +1021,7 @@ class SFGNOCI(SFNOCI):
 
         group : list 
             The result of grouping.
+            
     '''
     def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None):
         super().__init__(mf, ncas, nelecas, ncore)

From 31c7d3f83117a9d06df3cdc76e84266ff9cdc758 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Tue, 11 Mar 2025 16:48:10 +0900
Subject: [PATCH 07/10] sfnoci

---
 pyscf/sfnoci/sfnoci.py | 2 --
 1 file changed, 2 deletions(-)

diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index bd2395c..0961680 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -1211,8 +1211,6 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     s1e = mol.intor('int1e_ovlp')
     mySFNOCI = SFGNOCI(rm,4,(2,2),groupA = 'Li')
     mySFNOCI.lowdin_thres= 0.5
-    mySFNOCI = SFNOCI(rm, 4, (2,2))
-    mySFNOCI.fcisolver.nroots = 4 
 
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)

From 23baf092d7a71470fd6fe0837649c7c24cceb8d4 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Mon, 17 Mar 2025 17:03:57 +0900
Subject: [PATCH 08/10] Update

---
 pyscf/sfnoci/test/test_sfnoci.py | 111 +++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)
 create mode 100644 pyscf/sfnoci/test/test_sfnoci.py

diff --git a/pyscf/sfnoci/test/test_sfnoci.py b/pyscf/sfnoci/test/test_sfnoci.py
new file mode 100644
index 0000000..2aa60b9
--- /dev/null
+++ b/pyscf/sfnoci/test/test_sfnoci.py
@@ -0,0 +1,111 @@
+import unittest
+import numpy
+from pyscf import gto
+from pyscf import scf
+from pyscf.sfnoci.sfnoci import SFNOCI, SFGNOCI
+
+
+def setUpModule():
+    global mol, mo0, setocc, ro_occ
+    mol = gto.Mole()
+    mol.atom = [['Li', (0, 0, 0)],['F',(0,0,1.3)]]
+    mol.basis = 'ccpvtz'
+    mr=scf.RHF(mol)
+    mr.kernel()
+
+    mo0=mr.mo_coeff
+    occ=mr.mo_occ
+    setocc=numpy.zeros((2,occ.size))
+    setocc[:,occ==2]=1
+    setocc[1][3]=0
+    setocc[0][6]=1
+    ro_occ=setocc[0][:]+setocc[1][:]
+
+
+def tearDownModule():
+    global mol, mo0, setocc, ro_occ
+    mol.stdout.close()
+    del mol, mo0, setocc, ro_occ
+
+class KnownValues(unittest.TestCase):
+    def test_sfnoci(self):
+        mol = gto.Mole()
+        mol.atom = [['Li', (0, 0, 0)],['F',(0,0,1.6)]]
+        mol.basis = 'ccpvtz'
+        mol.spin=2
+        mol.build(0,0)
+        rm=scf.ROHF(mol)
+        dm_ro=rm.make_rdm1(mo0,ro_occ)
+        rm=scf.addons.mom_occ(rm,mo0,setocc)
+        rm.scf(dm_ro)
+        rm.kernel()
+
+        mo=rm.mo_coeff
+        as_list=[3,6,7,10]
+        mySFNOCI = SFNOCI(rm,4,(2,2))
+        mySFNOCI.lowdin_thres= 0.5
+
+        from pyscf.mcscf import addons
+        mo = addons.sort_mo(mySFNOCI,mo, as_list,1)
+        e, _, ci = mySFNOCI.kernel(mo)
+
+        mol.atom = [['Li', (0, 0, 0)],['F',(0,0,4.5)]]
+        mol.build(0,0)
+        rm=scf.ROHF(mol)
+        dm_ro=rm.make_rdm1(mo0,ro_occ)
+        rm=scf.addons.mom_occ(rm,mo0,setocc)
+        rm.scf(dm_ro)
+        rm.kernel()
+
+        mo=rm.mo_coeff
+        mySFNOCI = SFNOCI(rm,4,(2,2))
+        mySFNOCI.lowdin_thres= 0.5
+
+        mo = addons.sort_mo(mySFNOCI,mo, as_list,1)
+        re, _, ci = mySFNOCI.kernel(mo)
+
+        self.assertAlmostEqual(e-re, -0.11279175858597057, 6)
+
+    def test_sfgnoci(self):
+        mol = gto.Mole()
+        mol.atom = [['Li', (0, 0, 0)],['F',(0,0,1.6)]]
+        mol.basis = 'ccpvtz'
+        mol.spin=2
+        mol.build(0,0)
+        rm=scf.ROHF(mol)
+        dm_ro=rm.make_rdm1(mo0,ro_occ)
+        rm=scf.addons.mom_occ(rm,mo0,setocc)
+        rm.scf(dm_ro)
+        rm.kernel()
+
+        mo=rm.mo_coeff
+        as_list=[3,6,7,10]
+        mySFNOCI = SFGNOCI(rm,4,(2,2), groupA = 'Li')
+        mySFNOCI.lowdin_thres= 0.5
+
+        from pyscf.mcscf import addons
+        mo = addons.sort_mo(mySFNOCI,mo, as_list,1)
+        e, _, ci = mySFNOCI.kernel(mo)
+
+        mol.atom = [['Li', (0, 0, 0)],['F',(0,0,4.5)]]
+        mol.build(0,0)
+        rm=scf.ROHF(mol)
+        dm_ro=rm.make_rdm1(mo0,ro_occ)
+        rm=scf.addons.mom_occ(rm,mo0,setocc)
+        rm.scf(dm_ro)
+        rm.kernel()
+
+        mo=rm.mo_coeff
+        mySFNOCI = SFGNOCI(rm,4,(2,2), groupA = 'Li')
+        mySFNOCI.lowdin_thres= 0.5
+
+        mo = addons.sort_mo(mySFNOCI,mo, as_list,1)
+        re, _, ci = mySFNOCI.kernel(mo)
+
+        self.assertAlmostEqual(e-re, -0.1129230696617185, 6)
+
+
+
+if __name__ == "__main__":
+    print("Full Tests for SF(G)NOCI")
+    unittest.main()
\ No newline at end of file

From 050a121ac31bc02372b0fc95b5ee3ad687ef5ff5 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Tue, 18 Mar 2025 16:05:48 +0900
Subject: [PATCH 09/10] Update

---
 pyscf/sfnoci/direct_sfnoci.py | 245 +++++-----
 pyscf/sfnoci/sfnoci.py        | 810 +++++++++++++++++-----------------
 2 files changed, 525 insertions(+), 530 deletions(-)

diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
index bd11d0a..9e402b9 100644
--- a/pyscf/sfnoci/direct_sfnoci.py
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2024 The PySCF Developers. All Rights Reserved.
+# Copyright 2025 The PySCF Developers. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# Author: Jiseong Park <fark4308@snu.ac.kr> 
+# Author: Jiseong Park <fark4308@snu.ac.kr>
 # Edited by: Seunghoon Lee <seunghoonlee@snu.ac.kr>
 
 '''
@@ -22,14 +22,14 @@
 and Grouped-Bath Ansatz for SF-NOCI (SF-GNOCI)
 
 References:
-[1] Spin-flip non-orthogonal configuration interaction: a variational and 
+[1] Spin-flip non-orthogonal configuration interaction: a variational and
     almost black-box method for describing strongly correlated molecules
     Nicholas J. Mayhall, Paul R. Horn, Eric J. Sundstrom and Martin Head-Gordon
     Phys. Chem. Chem. Phys. 2014, 16, 22694
 [2] Efficient grouped-bath ansatz for spin-flip non-orthogonal configuration
-    interaction (SF-GNOCI) in transition-metal charge-transfer complexes 
+    interaction (SF-GNOCI) in transition-metal charge-transfer complexes
     Jiseong Park and Seunghoon Lee
-    J. Chem. Theory Comput. 2025 
+    J. Chem. Theory Comput. 2025
 '''
 
 import sys
@@ -115,17 +115,17 @@ def gen_excitations(ncas, nelecas, na, nb, link_index=None):
     t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na), dtype=numpy.int32)
     t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb), dtype=numpy.int32)
     t1a = numpy.zeros((ncas,ncas,na,na), dtype=numpy.int32)
-    t1b = numpy.zeros((ncas,ncas,nb,nb), dtype=numpy.int32) 
+    t1b = numpy.zeros((ncas,ncas,nb,nb), dtype=numpy.int32)
     for str0a , taba in enumerate(link_indexa):
-          for a1, i1, str1a, signa1 in link_indexa[str0a]:
-              t1a[a1,i1,str1a,str0a] += signa1 
-              for a2 , i2, str2a, signa2 in link_indexa[str1a]:
-                  t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+        for a1, i1, str1a, signa1 in link_indexa[str0a]:
+            t1a[a1,i1,str1a,str0a] += signa1 
+            for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+                t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
     for str0b , tabb in enumerate(link_indexb):
-          for a1, i1, str1b, signb1 in link_indexb[str0b]:
-              t1b[a1,i1,str1b,str0b] += signb1
-              for a2 , i2, str2b, signb2 in link_indexb[str1b]:
-                  t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+        for a1, i1, str1b, signb1 in link_indexb[str0b]:
+            t1b[a1,i1,str1b,str0b] += signb1
+            for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+                t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
     return t1a, t1b, t2aa, t2bb
 
 def gen_nonzero_excitations(t1a, t1b, t2aa, t2bb):
@@ -136,17 +136,17 @@ def gen_nonzero_excitations(t1a, t1b, t2aa, t2bb):
     return t1a_nonzero, t1b_nonzero, t2aa_nonzero, t2bb_nonzero
 
 def python_list_to_c_array(python_list):
-    if python_list is None: return ctypes.c_void_p(None), ctypes.c_void_p(None), 0 
+    if python_list is None: return ctypes.c_void_p(None), ctypes.c_void_p(None), 0
     else:
         num_groups = len(python_list)
         flat_list = sum(python_list, [])
         flat_list = (ctypes.c_int *len(flat_list))(*flat_list)
-        group_sizes = (ctypes.c_int * num_groups)()  
+        group_sizes = (ctypes.c_int * num_groups)()
         for i, group in enumerate(python_list):
             group_size = len(group)
-            group_sizes[i] = group_size  
+            group_sizes[i] = group_size
         return flat_list, group_sizes, num_groups
- 
+
 def contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list,
                link_index=None, ts=None, t_nonzero=None):
     '''Compute H|CI>
@@ -156,7 +156,7 @@ def contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list
         neleca = nelecas - nelecb
     else:
         neleca, nelecb = nelecas
-    
+
     na = cistring.num_strings(ncas,neleca)
     nb = cistring.num_strings(ncas,nelecb)
 
@@ -194,16 +194,16 @@ def contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list
          ctypes.c_int(ncas),
          ctypes.c_int(neleca), ctypes.c_int(nelecb),
          conf_info_list.ctypes.data_as(ctypes.c_void_p),
-         ctypes.c_int(na), stringsa.ctypes.data_as(ctypes.c_void_p), 
+         ctypes.c_int(na), stringsa.ctypes.data_as(ctypes.c_void_p),
          ctypes.c_int(nb), stringsb.ctypes.data_as(ctypes.c_void_p),
          ctypes.c_int(mo_num),
-         t1a.ctypes.data_as(ctypes.c_void_p), 
+         t1a.ctypes.data_as(ctypes.c_void_p),
          t1a_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t1ann),
-         t1b.ctypes.data_as(ctypes.c_void_p), 
+         t1b.ctypes.data_as(ctypes.c_void_p),
          t1b_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t1bnn),
-         t2aa.ctypes.data_as(ctypes.c_void_p), 
+         t2aa.ctypes.data_as(ctypes.c_void_p),
          t2aa_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2aann),
-         t2bb.ctypes.data_as(ctypes.c_void_p), 
+         t2bb.ctypes.data_as(ctypes.c_void_p),
          t2bb_nonzero.ctypes.data_as(ctypes.c_void_p), ctypes.c_int(t2bbnn),
          ov_list.ctypes.data_as(ctypes.c_void_p), ecore_list.ctypes.data_as(ctypes.c_void_p))
     return cinew
@@ -230,17 +230,17 @@ def contract_H_slow(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore
     t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na))
     t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb))
     t1a = numpy.zeros((ncas,ncas,na,na))
-    t1b = numpy.zeros((ncas,ncas,nb,nb)) 
+    t1b = numpy.zeros((ncas,ncas,nb,nb))
     for str0a , taba in enumerate(link_indexa):
-          for a1, i1, str1a, signa1 in link_indexa[str0a]:
-              t1a[a1,i1,str1a,str0a] += signa1 
-              for a2 , i2, str2a, signa2 in link_indexa[str1a]:
-                  t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
+        for a1, i1, str1a, signa1 in link_indexa[str0a]:
+            t1a[a1,i1,str1a,str0a] += signa1
+            for a2 , i2, str2a, signa2 in link_indexa[str1a]:
+                t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
     for str0b , tabb in enumerate(link_indexb):
-          for a1, i1, str1b, signb1 in link_indexb[str0b]:
-              t1b[a1,i1,str1b,str0b] += signb1
-              for a2 , i2, str2b, signb2 in link_indexb[str1b]:
-                  t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
+        for a1, i1, str1b, signb1 in link_indexb[str0b]:
+            t1b[a1,i1,str1b,str0b] += signb1
+            for a2 , i2, str2b, signb2 in link_indexb[str1b]:
+                t2bb[a2, i2, a1, i1, str2b, str0b] += signb1 * signb2
     t1a_nonzero = numpy.array(numpy.nonzero(t1a)).T
     t1b_nonzero = numpy.array(numpy.nonzero(t1b)).T
     t2aa_nonzero = numpy.array(numpy.nonzero(t2aa)).T
@@ -249,17 +249,17 @@ def contract_H_slow(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore
         for ab, ib, str1b, str0b in t1b_nonzero:
             p1 = conf_info_list[str1a, str1b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] *t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] * TSc[p1,p2] *2
+            cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] * t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] * ov_list[p1,p2] *2
     for a1, i1, a2,i2, str1a, str0a in t2aa_nonzero:
         for str0b, stringb in enumerate(stringsb):
             p1 = conf_info_list[str1a, str0b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] *t2aa[a1,i1,a2,i2,str1a,str0a] * TSc[p1,p2]
+            cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] *t2aa[a1,i1,a2,i2,str1a,str0a] * ov_list[p1,p2]
     for a1, i1, a2,i2, str1b, str0b in t2bb_nonzero:
         for str0a, stringa in enumerate(stringsa):
             p1 = conf_info_list[str0a, str1b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2]* t2bb[a1,i1,a2,i2,str1b,str0b] * TSc[p1,p2]
+            cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2]* t2bb[a1,i1,a2,i2,str1b,str0b] * ov_list[p1,p2]
     for str0a, stringa in enumerate(stringsa):
         for str0b, stringb in enumerate(stringsb):
             p = conf_info_list[str0a, str0b]
@@ -268,8 +268,8 @@ def contract_H_slow(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore
     return cinew
 
 def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, conf_info_list, ov_list, ecore_list,
-                  ci0=None, link_index=None, tol=None, lindep=None, 
-                  max_cycle=None, max_space=None, nroots=None, 
+                  ci0=None, link_index=None, tol=None, lindep=None,
+                  max_cycle=None, max_space=None, nroots=None,
                   davidson_only=None, pspace_size=None, hop=None,
                   max_memory=None, verbose=None, **kwargs):
     '''
@@ -287,7 +287,7 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, conf_info_list, ov_list, ecor
         ov_list : ndarray (nbath, nbath)
             overlap matrix between different baths.
         ecore_list : ndarray (nbath)
-            1D numpy array of core energies for each bath 
+            1D numpy array of core energies for each bath
 
     Kwargs:
         ci0: ndarray
@@ -334,18 +334,18 @@ def kernel_sfnoci(sfnoci, h1e, eri, ncas, nelecas, conf_info_list, ov_list, ecor
     na = cistring.num_strings(ncas, nelec[0])
     nb = cistring.num_strings(ncas, nelec[1])
     if link_index is None:
-            link_indexa = cistring.gen_linkstr_index(range(ncas), nelec[0])
-            link_indexb = cistring.gen_linkstr_index(range(ncas), nelec[1])
-            link_index = (link_indexa, link_indexb)
+        link_indexa = cistring.gen_linkstr_index(range(ncas), nelec[0])
+        link_indexb = cistring.gen_linkstr_index(range(ncas), nelec[1])
+        link_index = (link_indexa, link_indexb)
     else:
         link_indexa, link_indexb = link_index
-    
+
     ts = gen_excitations(ncas, nelecas, na, nb, link_index)
     t_nonzero = gen_nonzero_excitations(ts[0], ts[1], ts[2], ts[3])
     if hop is None:
         cpu0 = [logger.process_clock(), logger.perf_counter()]
         def hop(c):
-            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, conf_info_list, 
+            hc = sfnoci.contract_H(erieff, c, ncas, nelecas, conf_info_list,
                                    ov_list, ecore_list,link_index, ts, t_nonzero)
             cpu0[:] = log.timer_debug1('contract_H', *cpu0)
             return hc.ravel()
@@ -387,9 +387,9 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_lis
         for str0b, strsb in enumerate(stringsb):
             p = conf_info_list[str0a, str0b]
             rdm1c += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b]*dmet_core_list[p,p]
-  
+
     rdm1asmoa = numpy.zeros((ncas,ncas))
-    rdm1asmob = numpy.zeros((ncas,ncas))    
+    rdm1asmob = numpy.zeros((ncas,ncas))
     for str0a , taba in enumerate(link_indexa):
         for aa, ia, str1a, signa in link_indexa[str0a]:
             for str0b, strsb in enumerate(stringsb):
@@ -406,7 +406,7 @@ def make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_lis
     rdm1b = lib.einsum('ia,ab,jb-> ij', numpy.conjugate(mo_cas),rdm1asmob,mo_cas)
     rdm1a += rdm1c
     rdm1b += rdm1c
-    
+
     return rdm1a, rdm1b
 
 def make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
@@ -430,15 +430,15 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
     t2aa = numpy.zeros((ncas,ncas,ncas,ncas,na,na))
     t2bb = numpy.zeros((ncas,ncas,ncas,ncas,nb,nb))
     t1a = numpy.zeros((ncas,ncas,na,na))
-    t1b = numpy.zeros((ncas,ncas,nb,nb)) 
-    
+    t1b = numpy.zeros((ncas,ncas,nb,nb))
+
     rdm2aaac = numpy.zeros((ncas,ncas,ncas,ncas))
     rdm2abac = numpy.zeros((ncas,ncas,ncas,ncas))
     rdm2baac = numpy.zeros((ncas,ncas,ncas,ncas))
     rdm2bbac = numpy.zeros((ncas,ncas,ncas,ncas))
     for str0a , taba in enumerate(link_indexa):
         for a1, i1, str1a, signa1 in link_indexa[str0a]:
-            t1a[a1,i1,str1a,str0a] += signa1 
+            t1a[a1,i1,str1a,str0a] += signa1
             for a2 , i2, str2a, signa2 in link_indexa[str1a]:
                 t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
     for str0b , tabb in enumerate(link_indexb):
@@ -449,10 +449,10 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
     for str0a, strs0a in enumerate(stringsa):
         for str0b, strs0b in enumerate(stringsb):
             p2 = conf_info_list[str0a, str0b]
-            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])) 
+            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
             rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
             rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
-            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))  
+            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
             for str1a, strs1a in enumerate(stringsa):
                 p1 = conf_info_list[str1a, str0b]
                 rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*ov_list[p1,p2]
@@ -464,19 +464,19 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
                 for k in range(ncas):
                     rdm2bbac[:,k,k,:] -= numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b] * t1b[:,:,str1b,str0b]*ov_list[p1,p2]
             for str1a, strs1a in enumerate(stringsa):
-                for str1b, strs1b in enumerate(stringsb): 
+                for str1b, strs1b in enumerate(stringsb):
                     p1 = conf_info_list[str1a, str1b]
                     rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*ov_list[p1,p2]
                     rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*ov_list[p1,p2]
-    
+
     rdm2aa += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2aaac)
     rdm2ab += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2abac)
     rdm2ba += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2baac)
     rdm2bb += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2bbac)
     t1aao = lib.einsum('ia,jb,abcd -> ijcd', mo_cas, mo_cas, t1a)
     t1bao = lib.einsum('ia,jb,abcd -> ijcd', mo_cas, mo_cas, t1b)
-    
-    
+
+
     for str0a, taba in enumerate(link_indexa):
         for str1a in numpy.unique(link_indexa[str0a][:,2]):
             for str0b, strsb in enumerate(stringsb):
@@ -485,7 +485,7 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
                 rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-  
+
     for str0b, tabb in enumerate(link_indexb):
         for str1b in numpy.unique(link_indexb[str0b][:,2]):
             for str0a, strsa, in enumerate(stringsa):
@@ -494,7 +494,7 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
                 rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
                 rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-    
+
     return rdm2aa, rdm2ab, rdm2ba, rdm2bb
 
 def make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
@@ -552,30 +552,30 @@ def fix_spin_(fciobj, shift=.1, ss=None):
 
 
 class SFNOCISolver(FCISolver):
-  '''SF-NOCI
-  '''
-  def make_hdiag(self, h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt=None):
-      return make_hdiag(h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt)
-  
-  def make_precond(self, hdiag, level_shift=0):
-      return lib.make_diag_precond(hdiag, level_shift)
-
-  def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
-      return absorb_h1e(h1e, eri, ncas, nelecas, fac)
-
-  def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
+    '''SF-NOCI
+    '''
+    def make_hdiag(self, h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt=None):
+        return make_hdiag(h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt)
+ 
+    def make_precond(self, hdiag, level_shift=0):
+        return lib.make_diag_precond(hdiag, level_shift)
+
+    def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
+        return absorb_h1e(h1e, eri, ncas, nelecas, fac)
+
+    def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list,
                  ecore_list, link_index=None, ts=None, t_nonzero=None):
-      return contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
+        return contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
                         ecore_list ,link_index, ts, t_nonzero)
 
-  def get_init_guess(self, ncas, nelecas, nroots, hdiag):
-      return fci.direct_spin1.get_init_guess(ncas, nelecas, nroots, hdiag)
+    def get_init_guess(self, ncas, nelecas, nroots, hdiag):
+        return fci.direct_spin1.get_init_guess(ncas, nelecas, nroots, hdiag)
 
-  def eig(self, op, x0=None, precond=None, **kwargs):
+    def eig(self, op, x0=None, precond=None, **kwargs):
         if isinstance(op, numpy.ndarray):
             self.converged = True
             return scipy.linalg.eigh(op)
-        
+
         self.converged, e, ci = \
                 lib.davidson1(lambda xs: [op(x) for x in xs],
                               x0, precond, lessio=False, **kwargs)
@@ -585,53 +585,53 @@ def eig(self, op, x0=None, precond=None, **kwargs):
             ci = ci[0]
         return e, ci
 
-  def kernel(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0=None,
+    def kernel(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0=None,
              tol=None, lindep=None, max_cycle=None, max_space=None,
              nroots=None, davidson_only=None, pspace_size=None,
              orbsym=None, wfnsym=None, **kwargs):
-      if nroots is None: nroots = self.nroots
-      if self.verbose >= logger.WARN:
-          self.check_sanity()
-      assert self.spin is None or self.spin == 0
-      self.norb = norb
-      self.nelec = nelec
-      link_indexa = cistring.gen_linkstr_index(range(norb), nelec[0])
-      link_indexb = cistring.gen_linkstr_index(range(norb), nelec[1])
-      link_index = (link_indexa, link_indexb)
-     
-      e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0,
+        if nroots is None: nroots = self.nroots
+        if self.verbose >= logger.WARN:
+            self.check_sanity()
+        assert self.spin is None or self.spin == 0
+        self.norb = norb
+        self.nelec = nelec
+        link_indexa = cistring.gen_linkstr_index(range(norb), nelec[0])
+        link_indexb = cistring.gen_linkstr_index(range(norb), nelec[1])
+        link_index = (link_indexa, link_indexb)
+
+        e, c = kernel_sfnoci(self, h1e, eri, norb, nelec, conf_info_list, ov_list, ecore_list, ci0,
                            link_index, tol, lindep, max_cycle, max_space, nroots,
                            davidson_only, pspace_size, **kwargs)
-      self.eci = e
+        self.eci = e
 
-      na = link_index[0].shape[0]
-      nb = link_index[1].shape[0]
-      if nroots > 1:
-         self.ci = [x.reshape(na,nb).view(FCIvector) for x in c]
-      else:
-        self.ci = c.reshape(na,nb).view(FCIvector)
-    
-      return self.eci, self.ci
+        na = link_index[0].shape[0]
+        nb = link_index[1].shape[0]
+        if nroots > 1:
+            self.ci = [x.reshape(na,nb).view(FCIvector) for x in c]
+        else:
+            self.ci = c.reshape(na,nb).view(FCIvector)
 
-  def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
-      return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
+        return self.eci, self.ci
 
-  def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
-      return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list,conf_info_list, ov_list)
+    def make_rdm1s(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+        return make_rdm1s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
 
-  def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, conf_info_list, ov_list):
-      return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
+    def make_rdm1(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+        return make_rdm1(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list,conf_info_list, ov_list)
 
-  def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
-      return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
- 
-  def contract_ss(self, civec, ncas=None, nelecas=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      return spin_op.contract_ss(civec,ncas,nelecas)
+    def make_rdm2s(self, mo_coeff, ci, ncas, nelecas, ncore,dmet_core_list, conf_info_list, ov_list):
+        return make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
 
-  def fix_spin_(self, shift=PENALTY, ss = None):
-       '''Use level shift to control FCI solver spin.
+    def make_rdm2(self, mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list):
+        return make_rdm2(mo_coeff, ci, ncas, nelecas, ncore, dmet_core_list, conf_info_list, ov_list)
+
+    def contract_ss(self, civec, ncas=None, nelecas=None):
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        return spin_op.contract_ss(civec,ncas,nelecas)
+
+    def fix_spin_(self, shift=PENALTY, ss = None):
+        '''Use level shift to control FCI solver spin.
 
         .. math::
 
@@ -643,14 +643,14 @@ def fix_spin_(self, shift=PENALTY, ss = None):
             ss : number
                 S^2 expection value == s*(s+1)
         '''
-       fix_spin_(self, shift, ss)
-       return self
-  fix_spin = fix_spin_
+        fix_spin_(self, shift, ss)
+        return self
+    fix_spin = fix_spin_
 
-  def spin_square(self, civec, ncas = None, nelecas = None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      return spin_op.spin_square0(civec, ncas, nelecas)  
+    def spin_square(self, civec, ncas = None, nelecas = None):
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        return spin_op.spin_square0(civec, ncas, nelecas)
 
 class SpinPenaltySFNOCISolver:
     __name_mixin__ = 'SpinPenalty'
@@ -669,11 +669,11 @@ def undo_fix_spin(self):
         del obj.ss_value
         del obj.ss_penalty
         return obj
-    
+
     def base_contract_H(self, *args, **kwargs):
         return super().contract_H(*args, **kwargs)
-    
-    def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
+
+    def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list,
                    ecore_list, link_index=None, ts=None, t_nonzero=None, **kwargs):
         if isinstance(nelecas, (int, numpy.number)):
             sz = (nelecas % 2) * .5
@@ -698,5 +698,4 @@ def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list,
         ci1 *= self.ss_penalty
         ci0 = super().contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list, link_index, ts, t_nonzero, **kwargs)
         ci1 += ci0.reshape(civec.shape)
-        return ci1
- 
+        return ci1
\ No newline at end of file
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index 0961680..0003368 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 #
-# Copyright 2024 The PySCF Developers. All Rights Reserved.
+# Copyright 2025 The PySCF Developers. All Rights Reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# Author: Jiseong Park <fark4308@snu.ac.kr> 
+# Author: Jiseong Park <fark4308@snu.ac.kr>
 # Edited by: Seunghoon Lee <seunghoonlee@snu.ac.kr>
 
 '''
@@ -22,14 +22,14 @@
 and Grouped-Bath Ansatz for SF-NOCI (SF-GNOCI)
 
 References:
-[1] Spin-flip non-orthogonal configuration interaction: a variational and 
+[1] Spin-flip non-orthogonal configuration interaction: a variational and
     almost black-box method for describing strongly correlated molecules
     Nicholas J. Mayhall, Paul R. Horn, Eric J. Sundstrom and Martin Head-Gordon
     Phys. Chem. Chem. Phys. 2014, 16, 22694
 [2] Efficient grouped-bath ansatz for spin-flip non-orthogonal configuration
-    interaction (SF-GNOCI) in transition-metal charge-transfer complexes 
+    interaction (SF-GNOCI) in transition-metal charge-transfer complexes
     Jiseong Park and Seunghoon Lee
-    J. Chem. Theory Comput. 2025 
+    J. Chem. Theory Comput. 2025
 '''
 
 import numpy
@@ -76,7 +76,7 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     t0 = (logger.process_clock(), logger.perf_counter())
     if hasattr(sfnoci, '_groupA'): log.debug('Start SFGNOCI')
     else: log.debug('Start SFNOCI')
-    
+
 
     ncas = sfnoci.ncas
     nelecas = sfnoci.nelecas
@@ -86,7 +86,7 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     conf_info_list = group_info_list(ncas, nelecas, po_list, group)
     t1 = log.timer('FASSCF', *t0)
 
-    # SVD and core density matrix 
+    # SVD and core density matrix
     if group is None:
         dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, po_list)
     else:
@@ -115,7 +115,7 @@ def kernel(sfnoci, mo_coeff=None, ci0=None, verbose=logger.NOTE):
     if isinstance(e_tot, (float, numpy.float64)):
         e_cas = e_tot - ecore_list
     else:
-        e_cas = [e - ecore_list for e in e_tot] 
+        e_cas = [e - ecore_list for e in e_tot]
 
     return e_tot, e_cas, fcivec
 
@@ -156,7 +156,7 @@ def group_occ(po_list, group):
     best_row = None
     best_one_count = -1
     best_two_score = numpy.inf
-    best_one_score = numpy.inf
+    #best_one_score = numpy.inf
     best_zero_score = numpy.inf
 
     for idx in group:
@@ -187,7 +187,7 @@ def group_occ(po_list, group):
 def grouping_by_occ(po_list, groupA):
     a = len(groupA)
     p = len(po_list)
-    n = len(po_list[0])
+    #n = len(po_list[0])
     A_occ = numpy.zeros((p,a))
     for index, occ in enumerate(po_list):
         for i in range(a):
@@ -199,7 +199,7 @@ def grouping_by_occ(po_list, groupA):
             grouped_rows[row_tuple]=[]
         grouped_rows[row_tuple].append(i)
     return list(grouped_rows.values())
-  
+
 def grouping_by_lowdin(mol, ac_mo_coeff,po_list, aolabel, thres = 0.2):
     ova = mol.intor_symmetric("cint1e_ovlp_sph")
     e,v = numpy.linalg.eigh(ova)
@@ -207,10 +207,10 @@ def grouping_by_lowdin(mol, ac_mo_coeff,po_list, aolabel, thres = 0.2):
 
     aolist = mol.search_ao_label(aolabel)
     print(aolist)
-    a = len(aolist)
+    #a = len(aolist)
     p = len(po_list)
-    n = len(po_list[0])
-    N = ac_mo_coeff.shape[0]
+    #n = len(po_list[0])
+    #N = ac_mo_coeff.shape[0]
     ao_elecnums = numpy.zeros(p)
     for i in range(p):
         one_list = numpy.where(po_list[i] == 1)[0]
@@ -224,19 +224,19 @@ def grouping_by_lowdin(mol, ac_mo_coeff,po_list, aolabel, thres = 0.2):
 
     print(ao_elecnums)
     groups = []
-    visited = set() 
+    visited = set()
 
     for i in range(len(ao_elecnums)):
         if i in visited:
-            continue  
+            continue 
 
-        current_group = [i]  
-        visited.add(i) 
+        current_group = [i] 
+        visited.add(i)
 
         for j in range(len(ao_elecnums)):
             if i != j and j not in visited and abs(ao_elecnums[i] - ao_elecnums[j]) <= thres:
-                current_group.append(j)  
-                visited.add(j)  
+                current_group.append(j)
+                visited.add(j)
 
         groups.append(current_group)
 
@@ -264,7 +264,7 @@ def str2occ(str0,norb):
             occ[i]=1
 
     return occ
- 
+
 def num_to_group(groups,number):
     for i, group in enumerate(groups):
         if number in group:
@@ -287,7 +287,7 @@ def group_info_list(ncas, nelecas, PO, group = None):
                 p = num_to_group(group,p)
             group_info[stra, strb] = p
     return group_info.astype(int)
-    
+
 def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
            dump_chk=True, dm0=None, callback=None, conv_check=True, **kwargs):
     mf.max_cycle = max_cycle
@@ -360,7 +360,7 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
     mf.pre_kernel(locals())
 
     cput1 = logger.timer(mf, 'initialize scf', *cput0)
-    
+
     mo_energy=highspin_mo_energy
     mo_coeff=highspin_mo_coeff
     mo_occ=highspin_mo_occ
@@ -368,7 +368,7 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
         dm_last = dm
         last_hf_e = e_tot
 
-        
+
         fock = mf.get_fock(h1e, s1e, vhf, dm)
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-n_as)
@@ -376,8 +376,8 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
         new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
-        
- 
+
+
 
         for i in as_list:
             new_mo_coeff=numpy.insert(new_mo_coeff,i,highspin_mo_coeff[:,i],axis=1)
@@ -436,7 +436,7 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
             new_mo_coeff=numpy.insert(new_mo_coeff,i,mo_coeff[:,i],axis=1)
 
         mo_coeff=new_mo_coeff
-            
+
         AS_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
         for i in as_list:
             new_mo_energy=numpy.insert(new_mo_energy,i,AS_fock_energy[i])
@@ -470,12 +470,12 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
     logger.timer(mf, 'scf_cycle', *cput0)
     # A post-processing hook before return
     mf.post_kernel(locals())
-    if scf_conv==False:
+    if not scf_conv:
         mo_coeff=highspin_mo_coeff
 
     return scf_conv, e_tot, mo_energy, mo_coeff, mo_occ
 
-def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None, 
+def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
                         ncas = None, nelecas = None, ncore = None, conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
                         dump_chk=True, dm0=None, callback=None, conv_check=True, **kwarg):
     if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
@@ -488,7 +488,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     cput0 = (logger.process_clock(), logger.perf_counter())
     stringsa = cistring.make_strings(range(ncas),nelecas[0])
     stringsb = cistring.make_strings(range(ncas),nelecas[1])
-    na = len(stringsa)
+    #na = len(stringsa)
     nb = len(stringsb)
     N = mo_coeff.shape[0]
     as_list = numpy.array(range(ncore, ncore + ncas))
@@ -497,8 +497,8 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     group_info = group_info_list(ncas, nelecas, po_list, group)
     group_info = group_info.reshape(-1)
     target_conf = numpy.where(group_info == target_group)[0]
-    
-    
+
+
     mol = mf.mol
     as_dm_a = numpy.zeros((N,N))
     as_dm_b = numpy.zeros((N,N))
@@ -517,9 +517,9 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         core_mo_coeff = mo_coeff[:,:ncore]
         dm0_core = (core_mo_coeff ).dot(core_mo_coeff.conj().T)
         dm = numpy.asarray((dm0_core  + as_dm_a , dm0_core + as_dm_b))
-    else: 
+    else:
         dm = dm0
-    
+
     h1e = mf.get_hcore(mol)
     vhf = mf.get_veff(mol, dm)
     e_tot = mf.energy_tot(dm, h1e, vhf)
@@ -528,11 +528,11 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     scf_conv = False
     s1e = mf.get_ovlp(mol)
     cput1 = logger.timer(mf, 'initialize scf', *cput0)
-    
+
     for cycle in range(max_cycle):
         dm_last = dm
         last_hf_e = e_tot
-       
+
         fock = mf.get_fock(h1e, s1e, vhf, dm)
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-asn)
@@ -540,8 +540,8 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
         new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
-        
- 
+
+
 
         for i in as_list:
             new_mo_coeff=numpy.insert(new_mo_coeff,i, mo_coeff[:,i],axis=1)
@@ -559,7 +559,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         vhf = mf.get_veff(mol, dm, dm_last, vhf)
         e_tot = mf.energy_tot(dm, h1e, vhf)
 
-        fock_last = fock
+        #fock_last = fock
         fock = mf.get_fock(h1e, s1e, vhf, dm)
         norm_ddm = numpy.linalg.norm(dm-dm_last)
         logger.info(mf, 'cycle= %d E= %.15g  delta_E= %4.3g |ddm|= %4.3g',
@@ -589,7 +589,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
             new_mo_coeff=numpy.insert(new_mo_coeff,i,mo_coeff[:,i],axis=1)
 
         mo_coeff=new_mo_coeff
-            
+
         AS_fock_energy=lib.einsum('ai,aj,ij->a',numpy.conjugate(mo_coeff.T),mo_coeff.T,fock)
         for i in as_list:
             new_mo_energy=numpy.insert(new_mo_energy,i,AS_fock_energy[i])
@@ -598,7 +598,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         core_mo_coeff = mo_coeff[:,:ncore]
         dm_core = (core_mo_coeff * 2).dot(core_mo_coeff.conj().T)
         dm = (dm_core / 2 + as_dm_a, dm_core / 2 + as_dm_b)
-        
+
         vhf = mf.get_veff(mol, dm,dm_last,vhf)
         e_tot, last_hf_e = mf.energy_tot(dm, h1e, vhf), e_tot
         fock = mf.get_fock(h1e, s1e, vhf, dm)
@@ -610,7 +610,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
                     e_tot, e_tot-last_hf_e, norm_ddm)
     logger.timer(mf, 'scf_cycle', *cput0)
 
-    if scf_conv==False:
+    if not scf_conv:
         mo_coeff = sfnoci.mo_coeff
 
     
@@ -618,61 +618,61 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
 
 
 def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = None, groupA = None, debug = False):
-      if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
-      if ncas is None : ncas = sfnoci.ncas
-      if nelecas is None : nelecas = sfnoci.nelecas
-      if ncore is None : ncore = sfnoci.ncore
-      po_list = possible_occ(ncas, nelecas[0] + nelecas[1])
-      N=mo_coeff.shape[0]
-      p = len(po_list)
-      group = None
-      if not hasattr(sfnoci, '_groupA'):
-            optimized_mo=numpy.zeros((p,N,N))
-            #SF-CAS
-            if debug:
-                for i, occ in enumerate(po_list):
-                    optimized_mo[i]=mo_coeff
-            #SF-NOCI
-            else:
-               for i, occ in enumerate(po_list):
+    if mo_coeff is None : mo_coeff = sfnoci.mo_coeff
+    if ncas is None : ncas = sfnoci.ncas
+    if nelecas is None : nelecas = sfnoci.nelecas
+    if ncore is None : ncore = sfnoci.ncore
+    po_list = possible_occ(ncas, nelecas[0] + nelecas[1])
+    N=mo_coeff.shape[0]
+    p = len(po_list)
+    group = None
+    if not hasattr(sfnoci, '_groupA'):
+        optimized_mo=numpy.zeros((p,N,N))
+        #SF-CAS
+        if debug:
+            for i, occ in enumerate(po_list):
+                optimized_mo[i]=mo_coeff
+        #SF-NOCI
+        else:
+            for i, occ in enumerate(po_list):
                 conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore, conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
                 print("occuped pattern index:")
                 print(i)
-        
-      else:
-          groupA = sfnoci._groupA
-          if isinstance(groupA, str):
+
+    else:
+        groupA = sfnoci._groupA
+        if isinstance(groupA, str):
             group = grouping_by_lowdin(sfnoci.mol,mo_coeff[:,ncore:ncore+ncas],po_list, groupA, thres= sfnoci._thres)
-          elif isinstance(groupA, list):
+        elif isinstance(groupA, list):
             group = grouping_by_occ(po_list,groupA)
-          else: NotImplementedError
-          g = len(group)
-          optimized_mo = numpy.zeros((g,N,N))
-          for i in range(0,g):
-            #SF-CAS
+        else: NotImplementedError
+        g = len(group)
+        optimized_mo = numpy.zeros((g,N,N))
+        for i in range(0,g):
+        #SF-CAS
             if debug:
                 optimized_mo[i] = mo_coeff
-            #SF-GNOCI
+        #SF-GNOCI
             else:
-                conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff, ncas, nelecas, ncore, 
+                conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff, ncas, nelecas, ncore,
                                                           conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
                 print("occuped pattern index:")
                 print(i)
 
-      return optimized_mo, po_list, group 
+    return optimized_mo, po_list, group
 
 
-def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None, 
+def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None,
                    ncas=None, ncore=None):
     ''' SF-NOCI space one-electron hamiltonian
 
     Args:
         sfnoci : a SF-NOCI/SF-GNOCI object
-    
+
     Returns:
         A tuple, A tuple, the first is the effective one-electron hamiltonian defined in SF-NOCI space,
         the second is the list of electronic energy from baths.
@@ -685,38 +685,38 @@ def h1e_for_sfnoci(sfnoci, dmet_act_list=None, mo_list=None, dmet_core_list=None
         dmet_core_list, ov_list = sfnoci.get_svd_matrices(mo_list, group)
     if dmet_act_list is None:
         dmet_act_list = sfnoci.get_active_dm(sfnoci.mo_coeff)
-    p = dmet_core_list.shape[0]           
+    p = dmet_core_list.shape[0]
     mo_cas = mo_list[0][:,ncore:ncore+ncas]
     hcore = sfnoci.get_hcore()
-    h1e = numpy.zeros((p,p,ncas,ncas))       
+    h1e = numpy.zeros((p,p,ncas,ncas))
     ecore_list = numpy.zeros(p)
-    energy_nuc = sfnoci.energy_nuc()   
-    ha1e = lib.einsum('ai,ab,bj->ij',mo_cas,hcore,mo_cas)      
-    
+    energy_nuc = sfnoci.energy_nuc()
+    ha1e = lib.einsum('ai,ab,bj->ij',mo_cas,hcore,mo_cas)
+
     for i in range(0,p):
         for j in range(0,p):
             corevhf = sfnoci.get_veff(dm = 2 * dmet_core_list[i,j])
             h1e[i,j] = ha1e + lib.einsum('ijab,ab -> ij', dmet_act_list , corevhf)
             if i==j:
-                 ecore_list[i] += lib.einsum('ab,ab -> ', dmet_core_list[i,i],corevhf)
-                 ecore_list[i] += energy_nuc
-                 ecore_list[i] += 2*lib.einsum('ab,ab->', dmet_core_list[i,i], hcore)   
+                ecore_list[i] += lib.einsum('ab,ab -> ', dmet_core_list[i,i],corevhf)
+                ecore_list[i] += energy_nuc
+                ecore_list[i] += 2*lib.einsum('ab,ab->', dmet_core_list[i,i], hcore)
     sfnoci.h1e = h1e
     sfnoci.core_energies = ecore_list
     return h1e, ecore_list
-                         
+
 def spin_square(sfnoci, rdm1, rdm2ab,rdm2ba):
     M_s = sfnoci.spin/2
     mo = sfnoci.mo_coeff
     s1e = sfnoci.mol.intor('int1e_ovlp')
     rdm1mo = lib.einsum('qi,pl,kj,qp,lk->ij', mo, rdm1, mo,s1e,s1e)
     rdm2mo = lib.einsum('ai,bj,ck,dl,ap,bq,cr,ds,pqrs',mo,mo,mo,mo,s1e,s1e,s1e,s1e,rdm2ab+rdm2ba)
-    
-    return M_s**2 + 0.5*lib.einsum('ii ->',rdm1mo) - 0.5*lib.einsum('ijji ->', rdm2mo)       
+
+    return M_s**2 + 0.5*lib.einsum('ii ->',rdm1mo) - 0.5*lib.einsum('ijji ->', rdm2mo)
 
 
 class SFNOCI(CASBase):
-  '''SF-NOCI
+    '''SF-NOCI
 
     Args:
         mf : SCF object
@@ -726,7 +726,7 @@ class SFNOCI(CASBase):
             Number of active orbitals
         nelecas : a pair of int
             Number of electrons in active space
-    
+
     Kwargs:
         ncore : int
             Number of doubly occupied core orbitals. If not presented, this
@@ -744,15 +744,15 @@ class SFNOCI(CASBase):
         ncore : int or tuple of int
             Core electron number.
         fcisolver : an instance of :class:`FCISolver`
-            The SFNOCISolver in pyscf.sfnoci.direct_sfnoci module must be used. 
+            The SFNOCISolver in pyscf.sfnoci.direct_sfnoci module must be used.
             Other moldules in pyscf.fci cannot be used.
             You can control FCIsolver by setting e.g.::
 
             >>> mc.fcisolver.max_cycle = 30
             >>> mc.fcisolver.conv_tol = 1e-7
 
-    Key variables :    
-        N : The basis number 
+    Key variables :
+        N : The basis number
 
         po_list : A list of possible occupation patterns.
             for example, for (2e, 2o): po_list = [[0,2], [1,1], [2,0]]. It is 2D numpy array.
@@ -773,134 +773,134 @@ class SFNOCI(CASBase):
 
         ecore_list : 1D numpy array of core energies for each bath : (ngroup)
   '''
-  def __init__(self, mf, ncas, nelecas, ncore=None): 
-      
-      mol = mf.mol
-      self.mol = mol
-      self._scf = mf
-      self.verbose = mol.verbose
-      self.stdout = mol.stdout
-      self.max_memory = mf.max_memory
-      self.ncas = ncas
-      if isinstance(nelecas, (int, numpy.integer)):
-         raise NotImplementedError 
-      else:
-         self.nelecas = (nelecas[0], nelecas[1])
-         self._spin = nelecas[0] - nelecas[1]
-      self.ncore = ncore
-
-      self.fcisolver = SFNOCISolver(mol)
-      self.fcisolver.lindep = getattr(__config__,
+    def __init__(self, mf, ncas, nelecas, ncore=None):
+ 
+        mol = mf.mol
+        self.mol = mol
+        self._scf = mf
+        self.verbose = mol.verbose
+        self.stdout = mol.stdout
+        self.max_memory = mf.max_memory
+        self.ncas = ncas
+        if isinstance(nelecas, (int, numpy.integer)):
+            raise NotImplementedError
+        else:
+            self.nelecas = (nelecas[0], nelecas[1])
+            self._spin = nelecas[0] - nelecas[1]
+        self.ncore = ncore
+
+        self.fcisolver = SFNOCISolver(mol)
+        self.fcisolver.lindep = getattr(__config__,
                                       'sfnoci_SFNOCI_fcisolver_lindep', 1e-14)
-      self.fcisolver.max_cycle = getattr(__config__,
+        self.fcisolver.max_cycle = getattr(__config__,
                                          'sfnoci_SFNOCI_fcisolver_max_cycle', 100)
-      self.fcisolver.conv_tol = getattr(__config__,
+        self.fcisolver.conv_tol = getattr(__config__,
                                         'sfnoci_SFNOCI_fcisolver_conv_tol', 5e-7)
 
 ################################################## don't modify the following attributes, they are not input options
-      self.e_tot = 0       
-      self.e_cas = None
-      self.ci = None
-      self.mo_coeff = mf.mo_coeff
-      self.mo_energy = mf.mo_energy
-      self.mo_occ = None 
-      self.converged = False
-      self._thres = 0.2 
-
-  @property
-  def spin(self):
-      if self._spin is None:
-         return self.mol.spin
-      else:
-         return self._spin
-      
-  @spin.setter
-  def spin(self,x):
-      assert x is None or isinstance(x, (int, numpy.integer)) 
-      self._spin = x
-      nelecas = self.nelecas
-      necas = nelecas[0] + nelecas[1]
-      nelecb = (necas- x)//2
-      neleca = necas - nelecb
-      self.nelecas = (neleca,nelecb)
-                            
-  def possible_occ(self):
-      po_list = possible_occ(self.ncas, sum(self.nelecas))
-      return po_list
-                 
-  def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100):
-      if mo_coeff is None : mo_coeff = self.mo_coeff
-      if ncas is None : ncas = self.ncas
-      if ncore is None : ncore = self.ncore
-      mf = self._scf
-      as_list = numpy.array(range(ncore,ncore + ncas))  
-      core_list = numpy.array(range(0,ncore))
-      FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
-      return FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ 
-
-  def optimize_mo(self, mo_coeff=None, debug=False, conv_tol=1e-10, max_cycle=100):
-      if mo_coeff is None : mo_coeff = self.mo_coeff
-      mode = 0
-      if debug : mode = 1 
-      mf = self._scf
-      nelecas = self.nelecas
-      ncore = self.ncore
-      ncas = self.ncas
-      mo_list, po_list, _ = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, debug = debug)       
-      return mo_list, po_list, _
-                 
-  def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
-      if mo_list is None or po_list_or_group is None:
-          mo_list, po_list, _ = self.optimize_mo(self.mo_coeff)
-          po_list_or_group = po_list
-      ncore = self.ncore
-      ncas = self.ncas  
-      s1e = self._scf.get_ovlp(self.mol)
-      as_list = numpy.array(range(ncore,ncore+ncas))
-      core_list = numpy.array(range(0,ncore))       
-      N = mo_list.shape[1]
-      p = len(po_list_or_group)
-      dmet_core_list = numpy.zeros((p,p,N,N))
-      ov_list = numpy.zeros((p,p))       
-      for i in range(0,p):
-          xc_mo_coeff = mo_list[i][:,core_list]       
-          for j in range(0,p):
-              wc_mo_coeff = mo_list[j][:,core_list]
-              S, xc_bimo_coeff, wc_bimo_coeff, U, Vt = biorthogonalize(xc_mo_coeff, wc_mo_coeff, s1e)       
-              ov_list[i,j] = numpy.prod(S[numpy.abs(S)>1e-10])*numpy.linalg.det(U)*numpy.linalg.det(Vt)
-              for c in range(0,ncore):
-                  dmet_core_list[i,j] +=numpy.outer(xc_bimo_coeff[:,c],wc_bimo_coeff[:,c])/S[c]
-      return dmet_core_list, ov_list       
-
-  def get_active_dm(self,mo_coeff = None):
-      ncas = self.ncas
-      ncore = self.ncore
-      nocc = ncore + ncas
-      if mo_coeff is None:
-          ncore = self.ncore
-          mo_coeff = self.mo_coeff[:,ncore:nocc]
-      elif mo_coeff.shape[1] != ncas:
-           mo_coeff = mo_coeff[:,ncore:nocc] 
-      N = mo_coeff.shape[0]
-      dmet_act_list = numpy.zeros((ncas,ncas,N,N))
-      for i in range(0,ncas):
-          for j in range(0,ncas):
-              dmet_act_list[i,j] = numpy.outer(mo_coeff[:,i],mo_coeff[:,j])
-      self.dmet_act_list = dmet_act_list
-      return dmet_act_list
+        self.e_tot = 0
+        self.e_cas = None
+        self.ci = None
+        self.mo_coeff = mf.mo_coeff
+        self.mo_energy = mf.mo_energy
+        self.mo_occ = None
+        self.converged = False
+        self._thres = 0.2
+
+    @property
+    def spin(self):
+        if self._spin is None:
+            return self.mol.spin
+        else:
+            return self._spin
+
+    @spin.setter
+    def spin(self,x):
+        assert x is None or isinstance(x, (int, numpy.integer))
+        self._spin = x
+        nelecas = self.nelecas
+        necas = nelecas[0] + nelecas[1]
+        nelecb = (necas- x)//2
+        neleca = necas - nelecb
+        self.nelecas = (neleca,nelecb)
+
+    def possible_occ(self):
+        po_list = possible_occ(self.ncas, sum(self.nelecas))
+        return po_list
+
+    def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100):
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if ncas is None : ncas = self.ncas
+        if ncore is None : ncore = self.ncore
+        mf = self._scf
+        as_list = numpy.array(range(ncore,ncore + ncas))
+        core_list = numpy.array(range(0,ncore))
+        FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
+        return FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ
+
+    def optimize_mo(self, mo_coeff=None, debug=False, conv_tol=1e-10, max_cycle=100):
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        #mode = 0
+        #if debug : mode = 1
+        #mf = self._scf
+        nelecas = self.nelecas
+        ncore = self.ncore
+        ncas = self.ncas
+        mo_list, po_list, _ = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, debug = debug)       
+        return mo_list, po_list, _
+        
+    def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
+        if mo_list is None or po_list_or_group is None:
+            mo_list, po_list, _ = self.optimize_mo(self.mo_coeff)
+            po_list_or_group = po_list
+        ncore = self.ncore
+        ncas = self.ncas  
+        s1e = self._scf.get_ovlp(self.mol)
+        as_list = numpy.array(range(ncore,ncore+ncas))
+        core_list = numpy.array(range(0,ncore))
+        N = mo_list.shape[1]
+        p = len(po_list_or_group)
+        dmet_core_list = numpy.zeros((p,p,N,N))
+        ov_list = numpy.zeros((p,p))
+        for i in range(0,p):
+            xc_mo_coeff = mo_list[i][:,core_list]
+            for j in range(0,p):
+                wc_mo_coeff = mo_list[j][:,core_list]
+                S, xc_bimo_coeff, wc_bimo_coeff, U, Vt = biorthogonalize(xc_mo_coeff, wc_mo_coeff, s1e)
+                ov_list[i,j] = numpy.prod(S[numpy.abs(S)>1e-10])*numpy.linalg.det(U)*numpy.linalg.det(Vt)
+                for c in range(0,ncore):
+                    dmet_core_list[i,j] +=numpy.outer(xc_bimo_coeff[:,c],wc_bimo_coeff[:,c])/S[c]
+        return dmet_core_list, ov_list
+
+    def get_active_dm(self,mo_coeff = None):
+        ncas = self.ncas
+        ncore = self.ncore
+        nocc = ncore + ncas
+        if mo_coeff is None:
+            ncore = self.ncore
+            mo_coeff = self.mo_coeff[:,ncore:nocc]
+        elif mo_coeff.shape[1] != ncas:
+            mo_coeff = mo_coeff[:,ncore:nocc] 
+        N = mo_coeff.shape[0]
+        dmet_act_list = numpy.zeros((ncas,ncas,N,N))
+        for i in range(0,ncas):
+            for j in range(0,ncas):
+                dmet_act_list[i,j] = numpy.outer(mo_coeff[:,i],mo_coeff[:,j])
+        self.dmet_act_list = dmet_act_list
+        return dmet_act_list
            
-  def get_h1cas(self, dmet_act_list = None, mo_list = None, dmet_core_list = None, ncas = None, ncore = None):
-      return self.get_h1e(dmet_act_list, mo_list, dmet_core_list, ncas, ncore)
-  get_h1e = h1e_for_sfnoci
-             
-  def get_h2eff(self, mo_coeff=None):
-      '''Compute the active space two-particle Hamiltonian.
-      '''
-      return CASCI.get_h2eff(self,mo_coeff)
-
-  def kernel(self, mo_coeff=None, ci0=None, verbose=None):
-      '''
-      Returns:
+    def get_h1cas(self, dmet_act_list = None, mo_list = None, dmet_core_list = None, ncas = None, ncore = None):
+        return self.get_h1e(dmet_act_list, mo_list, dmet_core_list, ncas, ncore)
+    get_h1e = h1e_for_sfnoci
+
+    def get_h2eff(self, mo_coeff=None):
+        '''Compute the active space two-particle Hamiltonian.
+        '''
+        return CASCI.get_h2eff(self,mo_coeff)
+
+    def kernel(self, mo_coeff=None, ci0=None, verbose=None):
+        '''
+        Returns:
           Five elements, they are
           total energy,
           active space CI energy,
@@ -908,100 +908,100 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
           the MCSCF canonical orbital coefficients,
           the MCSCF canonical orbital coefficients.
 
-      They are attributes of mcscf object, which can be accessed by
-      .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
-      '''
-      if mo_coeff is None:
-          mo_coeff = self.mo_coeff
-      else:
-          self.mo_coeff = mo_coeff
-      if ci0 is None:
-          ci0 = self.ci
-      log = logger.new_logger(self, verbose)
-
-      #self.check_sanity()
-      #self.dump_flags(log)
-
-      self.e_tot, self.e_cas, self.ci = \
+        They are attributes of mcscf object, which can be accessed by
+        .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
+        '''
+        if mo_coeff is None:
+            mo_coeff = self.mo_coeff
+        else:
+            self.mo_coeff = mo_coeff
+        if ci0 is None:
+            ci0 = self.ci
+        log = logger.new_logger(self, verbose)
+
+        #self.check_sanity()
+        #self.dump_flags(log)
+
+        self.e_tot, self.e_cas, self.ci = \
               kernel(self, mo_coeff, ci0=ci0, verbose=log)
 
-      if getattr(self.fcisolver, 'converged', None) is not None:
-          self.converged = numpy.all(self.fcisolver.converged)
-          if self.converged:
-              log.info('SFNOCI converged')
-          else:
-              log.info('SFNOCI not converged')
-      else:
-          self.converged = True
-      #self._finalize()
-      return self.e_tot, self.e_cas, self.ci   # will provide group info
+        if getattr(self.fcisolver, 'converged', None) is not None:
+            self.converged = numpy.all(self.fcisolver.converged)
+            if self.converged:
+                log.info('SFNOCI converged')
+            else:
+                log.info('SFNOCI not converged')
+        else:
+            self.converged = True
+        #self._finalize()
+        return self.e_tot, self.e_cas, self.ci   # will provide group info
   
 
-  def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None, 
+    def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
           mo_list, po_list, _ = self.optimize_mo(mo_coeff)
           conf_info_list = group_info_list(ncas, nelecas, po_list)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
 
-      rdm1a, rdm1b = \
-          self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+        rdm1a, rdm1b = \
+            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
                                     ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm1a, rdm1b
+        return rdm1a, rdm1b
 
-  def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+    def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
-
-      rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
+            mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+        rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
                                      ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm
+        return rdm
 
-  def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
+    def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
-
-      rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
-          self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas, 
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
+            mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+        rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
+            self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas,
                                     ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm2aa, rdm2ab, rdm2ba, rdm2bb
-  
-  def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
-                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+        return rdm2aa, rdm2ab, rdm2ba, rdm2bb
 
-      rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas, 
+    def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
+                ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff  
+        if conf_info_list is None:
+            mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
+
+        rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas,
                                      ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm
+        return rdm
 
 class SFGNOCI(SFNOCI):
     '''SF-GNOCI
@@ -1019,34 +1019,34 @@ class SFGNOCI(SFNOCI):
         lowdin_thres : float
             The criterion of grouping the configurations if the lowdin basis is used.
 
-        group : list 
+        group : list
             The result of grouping.
-            
+
     '''
     def __init__(self, mf, ncas, nelecas, ncore=None, groupA=None):
         super().__init__(mf, ncas, nelecas, ncore)
-        self._groupA = groupA 
-        self._thres = 0.2 
-    
+        self._groupA = groupA
+        self._thres = 0.2
+
     @property
     def groupA(self):
-      return self._groupA
-  
+        return self._groupA
+
     @groupA.setter
     def groupA(self,x):
-      self._groupA = x
-    
+        self._groupA = x
+
     @property
     def lowdin_thres(self):
         return self._thres
-  
+
     @lowdin_thres.setter
     def lowdin_thres(self, x):
         self._thres = x
-    
+
     def kernel(self, mo_coeff=None, ci0=None, verbose=None):
-      '''
-      Returns:
+        '''
+        Returns:
           Five elements, they are
           total energy,
           active space CI energy,
@@ -1054,123 +1054,123 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
           the MCSCF canonical orbital coefficients,
           the MCSCF canonical orbital coefficients.
 
-      They are attributes of mcscf object, which can be accessed by
-      .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
-      '''
-      if mo_coeff is None:
-          mo_coeff = self.mo_coeff
-      else:
-          self.mo_coeff = mo_coeff
-      if ci0 is None:
-          ci0 = self.ci
-      log = logger.new_logger(self, verbose)
-
-      #self.check_sanity()
-      #self.dump_flags(log)
-
-      self.e_tot, self.e_cas, self.ci = \
+        They are attributes of mcscf object, which can be accessed by
+        .e_tot, .e_cas, .ci, .mo_coeff, .mo_energy
+        '''
+        if mo_coeff is None:
+            mo_coeff = self.mo_coeff
+        else:
+            self.mo_coeff = mo_coeff
+        if ci0 is None:
+            ci0 = self.ci
+        log = logger.new_logger(self, verbose)
+
+        #self.check_sanity()
+        #self.dump_flags(log)
+
+        self.e_tot, self.e_cas, self.ci = \
               kernel(self, mo_coeff, ci0=ci0, verbose=log)
 
-      if getattr(self.fcisolver, 'converged', None) is not None:
-          self.converged = numpy.all(self.fcisolver.converged)
-          if self.converged:
-              log.info('SFGNOCI converged')
-          else:
-              log.info('SFGNOCI not converged')
-      else:
-          self.converged = True
-      #self._finalize()
-      return self.e_tot, self.e_cas, self.ci   # will provide group info
-    
+        if getattr(self.fcisolver, 'converged', None) is not None:
+            self.converged = numpy.all(self.fcisolver.converged)
+            if self.converged:
+                log.info('SFGNOCI converged')
+            else:
+                log.info('SFGNOCI not converged')
+        else:
+            self.converged = True
+        #self._finalize()
+        return self.e_tot, self.e_cas, self.ci   # will provide group info
+
     def optimize_mo(self, mo_coeff=None, debug=False, groupA = None, conv_tol=1e-10, max_cycle=100):
-      if mo_coeff is None : mo_coeff = self.mo_coeff
-      if groupA is None : groupA = self._groupA
-      mode = 0
-      if debug : mode = 1 
-      mf = self._scf
-      nelecas = self.nelecas
-      ncore = self.ncore
-      ncas = self.ncas
-      mo_list, po_list, group = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, groupA, debug)       
-      return mo_list, po_list, group
-
-    def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None, 
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if groupA is None : groupA = self._groupA
+        mode = 0
+        if debug : mode = 1 
+        #mf = self._scf
+        nelecas = self.nelecas
+        ncore = self.ncore
+        ncas = self.ncas
+        mo_list, po_list, group = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, groupA, debug)
+        return mo_list, po_list, group
+
+    def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, group = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
-
-      rdm1a, rdm1b = \
-          self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff  
+        if conf_info_list is None:
+            mo_list, po_list, group = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list, group)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+        rdm1a, rdm1b = \
+            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
                                     ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm1a, rdm1b
+        return rdm1a, rdm1b
 
     def make_rdm1(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, group = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
-
-      rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
+            mo_list, po_list, group = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list, group)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+        rdm = self.fcisolver.make_rdm1(mo_coeff, ci, ncas, nelecas,
                                      ncore, dmet_core_list, conf_info_list, ov_list)
-      return rdm
+        return rdm
 
     def make_rdm2s(self, ci, mo_coeff=None , ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, group = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
-
-      rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
-          self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas, 
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
+            mo_list, po_list, group = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list, group)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+        rdm2aa, rdm2ab, rdm2ba, rdm2bb = \
+            self.fcisolver.make_rdm2s(mo_coeff, ci, ncas, nelecas,
                                     ncore, dmet_core_list,conf_info_list, ov_list)
-      return rdm2aa, rdm2ab, rdm2ba, rdm2bb
-  
+        return rdm2aa, rdm2ab, rdm2ba, rdm2bb
+
     def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                 ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
-      if ncas is None : ncas = self.ncas
-      if nelecas is None : nelecas = self.nelecas
-      if ncore is None : ncore = self.ncore
-      if mo_coeff is None : mo_coeff = self.mo_coeff  
-      if conf_info_list is None:
-          mo_list, po_list, group = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list, group)
-      if dmet_core_list is None or ov_list is None:
-          dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
-
-      rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas, 
+        if ncas is None : ncas = self.ncas
+        if nelecas is None : nelecas = self.nelecas
+        if ncore is None : ncore = self.ncore
+        if mo_coeff is None : mo_coeff = self.mo_coeff
+        if conf_info_list is None:
+            mo_list, po_list, group = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list, group)
+        if dmet_core_list is None or ov_list is None:
+            dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
+
+        rdm = self.fcisolver.make_rdm2(mo_coeff, ci, ncas, nelecas,
                                      ncore, dmet_core_list,conf_info_list, ov_list)
-      return rdm
+        return rdm
 
-if  __name__ == '__main__':
+if __name__ == '__main__':
     from pyscf import gto
     from pyscf import scf
-    from pyscf.fci import cistring
+
     mol = gto.Mole()
     mol.verbose = 5
     mol.output = None
 
     mol.atom = [['Li', (0, 0, 0)],['F',(0,0,1.4)]]
     mol.basis = 'ccpvdz'
-    
+
     x_list=[]
     e1_list=[]
     e2_list=[]
@@ -1182,12 +1182,12 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     e8_list=[]
     ma=[]
     mode=0
-    
+
     mol.spin=2
     mol.build(0,0)
     rm=scf.ROHF(mol)
     rm.kernel()
-  
+
     molr = gto.Mole()
     molr.verbose = 5
     molr.output = None
@@ -1209,13 +1209,12 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     mo=rm.mo_coeff
     as_list=[3,6,7,10]
     s1e = mol.intor('int1e_ovlp')
-    mySFNOCI = SFGNOCI(rm,4,(2,2),groupA = 'Li')
+    mySFNOCI = SFNOCI(rm,4,(2,2))
     mySFNOCI.lowdin_thres= 0.5
 
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
     reei, _, ci = mySFNOCI.kernel(mo)
-
     i=1
     while i <= 4:
         x_list.append(i)
@@ -1227,9 +1226,9 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         m=scf.addons.mom_occ(m,mo0,setocc)
         m.scf(dm_ro)
 
-        mySFNOCI = SFGNOCI(m,4,(2,2), groupA = 'Li')
+        mySFNOCI = SFNOCI(m,4,(2,2))
         mySFNOCI.spin = 0
-        mySFNOCI.fcisolver.nroots = 4 
+        mySFNOCI.fcisolver.nroots = 4
         mo = addons.sort_mo(mySFNOCI,m.mo_coeff,as_list,1)
         eigenvalues, _, eigenvectors = mySFNOCI.kernel(mo)
 
@@ -1237,7 +1236,7 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         e2_list.append(eigenvalues[1])
         e3_list.append(eigenvalues[2])
         e4_list.append(eigenvalues[3])
-        
+
         i+=0.5
 
     print(e1_list)
@@ -1247,12 +1246,12 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
 
     import matplotlib.pyplot as plt
     from pyscf.data.nist import HARTREE2EV
-    ref = e1_list[-1] 
+    ref = e1_list[-1]
     e1_list = (numpy.array(e1_list) - ref) * HARTREE2EV
     e2_list = (numpy.array(e2_list) - ref) * HARTREE2EV
     e3_list = (numpy.array(e3_list) - ref) * HARTREE2EV
     e4_list = (numpy.array(e4_list) - ref) * HARTREE2EV
- 
+
     plt.plot(x_list, e1_list, '-o', label='SF-NOCI $1{}^1\\Sigma^+$')
     plt.plot(x_list, e2_list, '-o', label='SF-NOCI $1{}^3\\Sigma^+$')
     plt.plot(x_list, e3_list, '-o', label='SF-NOCI $2{}^1\\Sigma^+$')
@@ -1262,9 +1261,6 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     plt.xlabel("Li-F distance, A")
     plt.ylabel("Relative Energy, eV")
     plt.legend()
-
     plt.xlim(1, 4)
     plt.ylim(-4, 10)
-
-    plt.show()
- 
\ No newline at end of file
+    plt.show()
\ No newline at end of file

From 6c4bee528958cf2100bd1d2ad632e8f053ed8486 Mon Sep 17 00:00:00 2001
From: jiseong_QClab <fark4308@snu.ac.kr>
Date: Mon, 24 Mar 2025 16:05:46 +0900
Subject: [PATCH 10/10] sfnoci

---
 pyscf/lib/CMakeLists.txt                 |  9 +--
 pyscf/lib/sfnoci/CMakeLists.txt          |  7 ++
 pyscf/{ => lib}/sfnoci/SFNOCI_contract.c |  8 +-
 pyscf/sfnoci/direct_sfnoci.py            | 71 ++++++++++--------
 pyscf/sfnoci/sfnoci.py                   | 96 +++++++++++-------------
 pyscf/sfnoci/test/test_sfnoci.py         |  3 +-
 6 files changed, 95 insertions(+), 99 deletions(-)
 create mode 100644 pyscf/lib/sfnoci/CMakeLists.txt
 rename pyscf/{ => lib}/sfnoci/SFNOCI_contract.c (83%)

diff --git a/pyscf/lib/CMakeLists.txt b/pyscf/lib/CMakeLists.txt
index baa5d92..9f3d5f4 100644
--- a/pyscf/lib/CMakeLists.txt
+++ b/pyscf/lib/CMakeLists.txt
@@ -139,11 +139,4 @@ set_target_properties (clib_dsrg PROPERTIES
     LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
     OUTPUT_NAME "dsrg")
 
-# Build the SFNOCI library
-set (SFNOCI_SOURCE_FILES "../sfnoci/SFNOCI_contract.c")
-add_library (clib_sfnoci SHARED ${SFNOCI_SOURCE_FILES})
-set_target_properties (clib_sfnoci PROPERTIES
-    CLEAN_DIRECT_OUTPUT 1
-    LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
-    OUTPUT_NAME "sfnoci")
-
+add_subdirectory(sfnoci)
diff --git a/pyscf/lib/sfnoci/CMakeLists.txt b/pyscf/lib/sfnoci/CMakeLists.txt
new file mode 100644
index 0000000..6d3139f
--- /dev/null
+++ b/pyscf/lib/sfnoci/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Build the SFNOCI library
+
+add_library (clib_sfnoci SHARED SFNOCI_contract.c)
+set_target_properties (clib_sfnoci PROPERTIES
+    CLEAN_DIRECT_OUTPUT 1
+    LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}
+    OUTPUT_NAME "sfnoci")
\ No newline at end of file
diff --git a/pyscf/sfnoci/SFNOCI_contract.c b/pyscf/lib/sfnoci/SFNOCI_contract.c
similarity index 83%
rename from pyscf/sfnoci/SFNOCI_contract.c
rename to pyscf/lib/sfnoci/SFNOCI_contract.c
index 987d16a..89ce684 100644
--- a/pyscf/sfnoci/SFNOCI_contract.c
+++ b/pyscf/lib/sfnoci/SFNOCI_contract.c
@@ -40,8 +40,8 @@ for (int i = 0; i <t1a_nonzero_size; i++){
         + aa * ncas * ncas * ncas 
         + ia * ncas *ncas 
         + ab * ncas + ib] 
-        * t1a[aa * ncas * na * na + ia * na * na  + str1a * na  + str0a]
-        * t1b[ab * ncas * nb * nb + ib * nb * nb + str1b * nb + str0b]
+        * t1a[aa * ncas * (size_t)na * (size_t)na + ia * (size_t)na * (size_t)na  + str1a * na  + str0a]
+        * t1b[ab * ncas * (size_t)nb * (size_t)nb + ib * (size_t)nb * (size_t)nb + str1b * nb + str0b]
         * TSc[p1 * num + p2] * 2.0;
     }
 }
@@ -63,7 +63,7 @@ for (int i =0; i <t2aa_nonzero_size; i++){
         + i1 * ncas  * ncas 
         + a2 * ncas 
         + i2]
-        * t2aa[a1 * ncas * ncas * ncas * na * na + i1* ncas * ncas * na * na + a2 * ncas* na * na + i2* na * na + str1a * na + str0a]
+        * t2aa[a1 * ncas * ncas * ncas * (size_t)na * (size_t)na + i1* ncas * ncas * (size_t)na * (size_t)na + a2 * ncas* (size_t)na * (size_t)na + i2* (size_t)na * (size_t)na + str1a * na + str0a]
         * TSc[p1 * num + p2];
     }
 }
@@ -86,7 +86,7 @@ for (int i =0; i <t2bb_nonzero_size; i++){
         + i1 * ncas  * ncas 
         + a2 * ncas 
         + i2]
-        * t2bb[a1 * ncas * ncas * ncas * nb * nb + i1* ncas * ncas * nb * nb + a2 * ncas* nb * nb + i2* nb * nb + str1b * nb + str0b]
+        * t2bb[a1 * ncas * ncas * ncas * (size_t)nb * (size_t)nb + i1* ncas * ncas * (size_t)nb * (size_t)nb + a2 * ncas* (size_t)nb * (size_t)nb + i2* (size_t)nb * (size_t)nb + str1b * nb + str0b]
         * TSc[p1 * num + p2];
     }
 }
diff --git a/pyscf/sfnoci/direct_sfnoci.py b/pyscf/sfnoci/direct_sfnoci.py
index 9e402b9..55da292 100644
--- a/pyscf/sfnoci/direct_sfnoci.py
+++ b/pyscf/sfnoci/direct_sfnoci.py
@@ -118,7 +118,7 @@ def gen_excitations(ncas, nelecas, na, nb, link_index=None):
     t1b = numpy.zeros((ncas,ncas,nb,nb), dtype=numpy.int32)
     for str0a , taba in enumerate(link_indexa):
         for a1, i1, str1a, signa1 in link_indexa[str0a]:
-            t1a[a1,i1,str1a,str0a] += signa1 
+            t1a[a1,i1,str1a,str0a] += signa1
             for a2 , i2, str2a, signa2 in link_indexa[str1a]:
                 t2aa[a2, i2, a1, i1, str2a, str0a] += signa1 * signa2
     for str0b , tabb in enumerate(link_indexb):
@@ -135,18 +135,6 @@ def gen_nonzero_excitations(t1a, t1b, t2aa, t2bb):
     t2bb_nonzero = numpy.array(numpy.array(numpy.nonzero(t2bb)).T, order = 'C', dtype = numpy.int32)
     return t1a_nonzero, t1b_nonzero, t2aa_nonzero, t2bb_nonzero
 
-def python_list_to_c_array(python_list):
-    if python_list is None: return ctypes.c_void_p(None), ctypes.c_void_p(None), 0
-    else:
-        num_groups = len(python_list)
-        flat_list = sum(python_list, [])
-        flat_list = (ctypes.c_int *len(flat_list))(*flat_list)
-        group_sizes = (ctypes.c_int * num_groups)()
-        for i, group in enumerate(python_list):
-            group_size = len(group)
-            group_sizes[i] = group_size
-        return flat_list, group_sizes, num_groups
-
 def contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list,
                link_index=None, ts=None, t_nonzero=None):
     '''Compute H|CI>
@@ -249,17 +237,21 @@ def contract_H_slow(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore
         for ab, ib, str1b, str0b in t1b_nonzero:
             p1 = conf_info_list[str1a, str1b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] * t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] * ov_list[p1,p2] *2
+            cinew[str1a,str1b] += civec[str0a,str0b] * erieff[p1,p2,aa,ia,ab,ib] \
+                                    * t1a[aa,ia,str1a,str0a]* t1b[ab,ib,str1b,str0b] \
+                                    * ov_list[p1,p2] *2
     for a1, i1, a2,i2, str1a, str0a in t2aa_nonzero:
         for str0b, stringb in enumerate(stringsb):
             p1 = conf_info_list[str1a, str0b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] *t2aa[a1,i1,a2,i2,str1a,str0a] * ov_list[p1,p2]
+            cinew[str1a,str0b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] \
+                                    *t2aa[a1,i1,a2,i2,str1a,str0a] * ov_list[p1,p2]
     for a1, i1, a2,i2, str1b, str0b in t2bb_nonzero:
         for str0a, stringa in enumerate(stringsa):
             p1 = conf_info_list[str0a, str1b]
             p2 = conf_info_list[str0a, str0b]
-            cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2]* t2bb[a1,i1,a2,i2,str1b,str0b] * ov_list[p1,p2]
+            cinew[str0a,str1b] += civec[str0a,str0b] * erieff[p1,p2,a1,i1,a2,i2] \
+                                    * t2bb[a1,i1,a2,i2,str1b,str0b] * ov_list[p1,p2]
     for str0a, stringa in enumerate(stringsa):
         for str0b, stringb in enumerate(stringsb):
             p = conf_info_list[str0a, str0b]
@@ -449,10 +441,14 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
     for str0a, strs0a in enumerate(stringsa):
         for str0b, strs0b in enumerate(stringsb):
             p2 = conf_info_list[str0a, str0b]
-            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
-            rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
-            rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
-            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
+            rdm2aa += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] \
+                        * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
+            rdm2ab += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] \
+                        * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
+            rdm2ba += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] \
+                        * lib.einsum('pq,rs -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:])
+            rdm2bb += numpy.conjugate(ci[str0a,str0b])*ci[str0a,str0b] \
+                        * (lib.einsum('pq,rs -> pqrs', dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]) - lib.einsum('ps,rq -> pqrs',dmet_core_list[p2,p2,:,:],dmet_core_list[p2,p2,:,:]))
             for str1a, strs1a in enumerate(stringsa):
                 p1 = conf_info_list[str1a, str0b]
                 rdm2aaac[:,:,:,:] += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*t2aa[:,:,:,:,str1a,str0a]*ov_list[p1,p2]
@@ -466,8 +462,10 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
             for str1a, strs1a in enumerate(stringsa):
                 for str1b, strs1b in enumerate(stringsb):
                     p1 = conf_info_list[str1a, str1b]
-                    rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*ov_list[p1,p2]
-                    rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]*lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*ov_list[p1,p2]
+                    rdm2abac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]\
+                                *lib.einsum('pq,rs-> pqrs',t1a[:,:,str1a,str0a],t1b[:,:,str1b,str0b])*ov_list[p1,p2]
+                    rdm2baac += numpy.conjugate(ci[str1a,str1b])*ci[str0a,str0b]\
+                                *lib.einsum('pq,rs-> pqrs',t1b[:,:,str1b,str0b],t1a[:,:,str1a,str0a])*ov_list[p1,p2]
 
     rdm2aa += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2aaac)
     rdm2ab += lib.einsum('pa,qb,rc,sd,abcd -> pqrs',mo_cas,mo_cas,mo_cas,mo_cas,rdm2abac)
@@ -482,18 +480,26 @@ def make_rdm2s(mo_coeff, ci, ncas, nelecas, ncore,  dmet_core_list, conf_info_li
             for str0b, strsb in enumerate(stringsb):
                 p1 = conf_info_list[str1a, str0b]
                 p2 = conf_info_list[str0a, str0b]
-                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-                rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-                rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]*(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2aa += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]\
+                            *(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])\
+                              -lib.einsum('ps,rq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))\
+                                *ov_list[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]\
+                    *(lib.einsum('pq,rs->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str1a,str0b])*ci[str0a,str0b]\
+                    *(lib.einsum('rs,pq->pqrs',t1aao[:,:,str1a,str0a],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
 
     for str0b, tabb in enumerate(link_indexb):
         for str1b in numpy.unique(link_indexb[str0b][:,2]):
             for str0a, strsa, in enumerate(stringsa):
                 p1 = conf_info_list[str0a, str0b]
                 p2 = conf_info_list[str0a, str1b]
-                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-                rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
-                rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]* (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2bb += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]\
+                    * (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])+lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('ps,rq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:])-lib.einsum('rq,ps->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2ab += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]\
+                    * (lib.einsum('rs,pq->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
+                rdm2ba += numpy.conjugate(ci[str0a,str1b])*ci[str0a,str0b]\
+                    * (lib.einsum('pq,rs->pqrs',t1bao[:,:,str1b,str0b],dmet_core_list[p1,p2,:,:]))*ov_list[p1,p2]
 
     return rdm2aa, rdm2ab, rdm2ba, rdm2bb
 
@@ -556,7 +562,7 @@ class SFNOCISolver(FCISolver):
     '''
     def make_hdiag(self, h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt=None):
         return make_hdiag(h1e, eri, ncas, nelecas, conf_info_list, ecore_list, opt)
- 
+
     def make_precond(self, hdiag, level_shift=0):
         return lib.make_diag_precond(hdiag, level_shift)
 
@@ -565,7 +571,7 @@ def absorb_h1e(self, h1e, eri, ncas, nelecas, fac=1):
 
     def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list,
                  ecore_list, link_index=None, ts=None, t_nonzero=None):
-        return contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, 
+        return contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list,
                         ecore_list ,link_index, ts, t_nonzero)
 
     def get_init_guess(self, ncas, nelecas, nroots, hdiag):
@@ -696,6 +702,7 @@ def contract_H(self, erieff, civec, ncas, nelecas, conf_info_list, ov_list,
             ci1 += self.contract_ss(tmp, ncas, nelecas).reshape(civec.shape)
             tmp = None
         ci1 *= self.ss_penalty
-        ci0 = super().contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list, ecore_list, link_index, ts, t_nonzero, **kwargs)
+        ci0 = super().contract_H(erieff, civec, ncas, nelecas, conf_info_list, ov_list,
+                                 ecore_list, link_index, ts, t_nonzero, **kwargs)
         ci1 += ci0.reshape(civec.shape)
-        return ci1
\ No newline at end of file
+        return ci1
diff --git a/pyscf/sfnoci/sfnoci.py b/pyscf/sfnoci/sfnoci.py
index 0003368..227559f 100644
--- a/pyscf/sfnoci/sfnoci.py
+++ b/pyscf/sfnoci/sfnoci.py
@@ -129,29 +129,9 @@ def find_arrays(n_as,n_ase):
                 arrays.append(numpy.array(combination))
         return arrays
     result_arrays=find_arrays(n_as,n_ase)
-    array_list=[]
-    for arr in result_arrays:
-        array_list.append(arr)
-    concantenated_array=numpy.array(array_list, order = 'C', dtype = numpy.int32)
+    concantenated_array=numpy.array(result_arrays, order = 'C', dtype = numpy.int32)
     return concantenated_array
 
-def fill_array_with_sum_N(length, N):
-    result = [0] * length
-    assert N <= length *2
-    if N <= length:
-        for i in range(N):
-            result[i] = 1
-    else:
-        num_ones = length
-        num_twos = N - num_ones
-
-        for i in range(length):
-            result[i] = 1
-        for i in range(num_twos):
-            result[i] = 2
-
-    return result
-
 def group_occ(po_list, group):
     best_row = None
     best_one_count = -1
@@ -228,9 +208,9 @@ def grouping_by_lowdin(mol, ac_mo_coeff,po_list, aolabel, thres = 0.2):
 
     for i in range(len(ao_elecnums)):
         if i in visited:
-            continue 
+            continue
 
-        current_group = [i] 
+        current_group = [i]
         visited.add(i)
 
         for j in range(len(ao_elecnums)):
@@ -260,7 +240,7 @@ def find_matching_rows(matrix, target_row):
 def str2occ(str0,norb):
     occ=numpy.zeros(norb)
     for i in range(norb):
-        if str0 & (1<<i):
+        if str0 & ( 1 << i ):
             occ[i]=1
 
     return occ
@@ -288,7 +268,8 @@ def group_info_list(ncas, nelecas, PO, group = None):
             group_info[stra, strb] = p
     return group_info.astype(int)
 
-def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
+def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,
+           as_occ,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100,
            dump_chk=True, dm0=None, callback=None, conv_check=True, **kwargs):
     mf.max_cycle = max_cycle
     n_as=len(as_list)
@@ -372,7 +353,8 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
         fock = mf.get_fock(h1e, s1e, vhf, dm)
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-n_as)
-        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),
+                                                      ~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
         new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
@@ -424,7 +406,8 @@ def FASSCF(mf,as_list,core_list,highspin_mo_energy,highspin_mo_coeff,as_occ,conv
         #fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-n_as)
-        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),
+                                                      ~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
 
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
@@ -536,7 +519,8 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         fock = mf.get_fock(h1e, s1e, vhf, dm)
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-asn)
-        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),
+                                                      ~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
         new_mo_coeff=reduced_mo_coeff.dot(mo_basis_new_mo_coeff)
@@ -577,7 +561,8 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
         #fock = mf.get_fock(h1e, s1e, vhf, dm)  # = h1e + vhf
         mo_basis_fock=(mo_coeff.T.dot(fock)).dot(mo_coeff)
         I=numpy.identity(N-asn)
-        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
+        reduced_mo_basis_fock=mo_basis_fock[numpy.ix_(~numpy.isin(numpy.arange(mo_basis_fock.shape[0]),as_list),
+                                                      ~numpy.isin(numpy.arange(mo_basis_fock.shape[1]),as_list))]
 
         new_mo_energy, mo_basis_new_mo_coeff=mf.eig(reduced_mo_basis_fock,I)
         reduced_mo_coeff=numpy.delete(mo_coeff,as_list,axis=1)
@@ -613,7 +598,7 @@ def StateAverage_FASSCF(sfnoci, target_group, po_list, group, mo_coeff = None,
     if not scf_conv:
         mo_coeff = sfnoci.mo_coeff
 
-    
+
     return scf_conv, e_tot, mo_energy, mo_coeff
 
 
@@ -635,7 +620,8 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
         #SF-NOCI
         else:
             for i, occ in enumerate(po_list):
-                conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore, conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
+                conv, et, moe, moce, moocc = sfnoci.FASSCF(occ, mo_coeff, ncas, ncore,
+                                                           conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
                 print("occuped pattern index:")
@@ -651,12 +637,13 @@ def optimize_mo(sfnoci, mo_coeff = None, ncas = None, nelecas = None, ncore = No
         g = len(group)
         optimized_mo = numpy.zeros((g,N,N))
         for i in range(0,g):
-        #SF-CAS
+            #SF-CAS
             if debug:
                 optimized_mo[i] = mo_coeff
-        #SF-GNOCI
+            #SF-GNOCI
             else:
-                conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff, ncas, nelecas, ncore,
+                conv, et, moe, moce = StateAverage_FASSCF(sfnoci, i, po_list, group, mo_coeff,
+                                                          ncas, nelecas, ncore,
                                                           conv_tol= sfnoci._scf.conv_tol , max_cycle= sfnoci._scf.max_cycle)
                 print(conv, et)
                 optimized_mo[i]=moce
@@ -774,7 +761,7 @@ class SFNOCI(CASBase):
         ecore_list : 1D numpy array of core energies for each bath : (ngroup)
   '''
     def __init__(self, mf, ncas, nelecas, ncore=None):
- 
+
         mol = mf.mol
         self.mol = mol
         self._scf = mf
@@ -828,14 +815,16 @@ def possible_occ(self):
         po_list = possible_occ(self.ncas, sum(self.nelecas))
         return po_list
 
-    def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100):
+    def FASSCF(self, occ, mo_coeff = None, ncas = None, ncore = None,
+               conv_tol=1e-10, conv_tol_grad=None, max_cycle = 100):
         if mo_coeff is None : mo_coeff = self.mo_coeff
         if ncas is None : ncas = self.ncas
         if ncore is None : ncore = self.ncore
         mf = self._scf
         as_list = numpy.array(range(ncore,ncore + ncas))
         core_list = numpy.array(range(0,ncore))
-        FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
+        FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ \
+            = FASSCF(mf,as_list,core_list,mf.mo_energy,mo_coeff,occ, conv_tol= conv_tol , max_cycle= max_cycle)
         return FAS_scf_conv, FAS_e_tot, FAS_mo_energy, FAS_mo_coeff, FAS_mo_occ
 
     def optimize_mo(self, mo_coeff=None, debug=False, conv_tol=1e-10, max_cycle=100):
@@ -846,17 +835,17 @@ def optimize_mo(self, mo_coeff=None, debug=False, conv_tol=1e-10, max_cycle=100)
         nelecas = self.nelecas
         ncore = self.ncore
         ncas = self.ncas
-        mo_list, po_list, _ = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, debug = debug)       
+        mo_list, po_list, _ = optimize_mo(self, mo_coeff, ncas, nelecas, ncore, debug = debug)
         return mo_list, po_list, _
-        
+
     def get_svd_matrices(self, mo_list=None, po_list_or_group=None):
         if mo_list is None or po_list_or_group is None:
             mo_list, po_list, _ = self.optimize_mo(self.mo_coeff)
             po_list_or_group = po_list
         ncore = self.ncore
-        ncas = self.ncas  
+        ncas = self.ncas 
         s1e = self._scf.get_ovlp(self.mol)
-        as_list = numpy.array(range(ncore,ncore+ncas))
+        #as_list = numpy.array(range(ncore,ncore+ncas))
         core_list = numpy.array(range(0,ncore))
         N = mo_list.shape[1]
         p = len(po_list_or_group)
@@ -880,7 +869,7 @@ def get_active_dm(self,mo_coeff = None):
             ncore = self.ncore
             mo_coeff = self.mo_coeff[:,ncore:nocc]
         elif mo_coeff.shape[1] != ncas:
-            mo_coeff = mo_coeff[:,ncore:nocc] 
+            mo_coeff = mo_coeff[:,ncore:nocc]
         N = mo_coeff.shape[0]
         dmet_act_list = numpy.zeros((ncas,ncas,N,N))
         for i in range(0,ncas):
@@ -888,7 +877,7 @@ def get_active_dm(self,mo_coeff = None):
                 dmet_act_list[i,j] = numpy.outer(mo_coeff[:,i],mo_coeff[:,j])
         self.dmet_act_list = dmet_act_list
         return dmet_act_list
-           
+
     def get_h1cas(self, dmet_act_list = None, mo_list = None, dmet_core_list = None, ncas = None, ncore = None):
         return self.get_h1e(dmet_act_list, mo_list, dmet_core_list, ncas, ncore)
     get_h1e = h1e_for_sfnoci
@@ -935,7 +924,7 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
             self.converged = True
         #self._finalize()
         return self.e_tot, self.e_cas, self.ci   # will provide group info
-  
+
 
     def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
                  ncore=None, dmet_core_list=None, conf_info_list=None, ov_list=None):
@@ -944,13 +933,13 @@ def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         if ncore is None : ncore = self.ncore
         if mo_coeff is None : mo_coeff = self.mo_coeff
         if conf_info_list is None:
-          mo_list, po_list, _ = self.optimize_mo(mo_coeff)
-          conf_info_list = group_info_list(ncas, nelecas, po_list)
+            mo_list, po_list, _ = self.optimize_mo(mo_coeff)
+            conf_info_list = group_info_list(ncas, nelecas, po_list)
         if dmet_core_list is None or ov_list is None:
             dmet_core_list, ov_list = self.get_svd_matrices(mo_list, po_list)
 
         rdm1a, rdm1b = \
-            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas,
                                     ncore, dmet_core_list, conf_info_list, ov_list)
         return rdm1a, rdm1b
 
@@ -992,7 +981,7 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         if ncas is None : ncas = self.ncas
         if nelecas is None : nelecas = self.nelecas
         if ncore is None : ncore = self.ncore
-        if mo_coeff is None : mo_coeff = self.mo_coeff  
+        if mo_coeff is None : mo_coeff = self.mo_coeff
         if conf_info_list is None:
             mo_list, po_list, _ = self.optimize_mo(mo_coeff)
             conf_info_list = group_info_list(ncas, nelecas, po_list)
@@ -1085,8 +1074,8 @@ def kernel(self, mo_coeff=None, ci0=None, verbose=None):
     def optimize_mo(self, mo_coeff=None, debug=False, groupA = None, conv_tol=1e-10, max_cycle=100):
         if mo_coeff is None : mo_coeff = self.mo_coeff
         if groupA is None : groupA = self._groupA
-        mode = 0
-        if debug : mode = 1 
+        #mode = 0
+        #if debug : mode = 1
         #mf = self._scf
         nelecas = self.nelecas
         ncore = self.ncore
@@ -1099,7 +1088,7 @@ def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
         if ncas is None : ncas = self.ncas
         if nelecas is None : nelecas = self.nelecas
         if ncore is None : ncore = self.ncore
-        if mo_coeff is None : mo_coeff = self.mo_coeff  
+        if mo_coeff is None : mo_coeff = self.mo_coeff
         if conf_info_list is None:
             mo_list, po_list, group = self.optimize_mo(mo_coeff)
             conf_info_list = group_info_list(ncas, nelecas, po_list, group)
@@ -1107,7 +1096,7 @@ def make_rdm1s(self, ci, mo_coeff=None, ncas=None, nelecas=None,
             dmet_core_list, ov_list = self.get_svd_matrices(mo_list, group)
 
         rdm1a, rdm1b = \
-            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas, 
+            self.fcisolver.make_rdm1s(mo_coeff, ci, ncas, nelecas,
                                     ncore, dmet_core_list, conf_info_list, ov_list)
         return rdm1a, rdm1b
 
@@ -1215,6 +1204,7 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     from pyscf.mcscf import addons
     mo = addons.sort_mo(mySFNOCI,rm.mo_coeff, as_list,1)
     reei, _, ci = mySFNOCI.kernel(mo)
+
     i=1
     while i <= 4:
         x_list.append(i)
@@ -1263,4 +1253,4 @@ def make_rdm2(self, ci, mo_coeff=None, ncas=None, nelecas=None,
     plt.legend()
     plt.xlim(1, 4)
     plt.ylim(-4, 10)
-    plt.show()
\ No newline at end of file
+    plt.show()
diff --git a/pyscf/sfnoci/test/test_sfnoci.py b/pyscf/sfnoci/test/test_sfnoci.py
index 2aa60b9..d851045 100644
--- a/pyscf/sfnoci/test/test_sfnoci.py
+++ b/pyscf/sfnoci/test/test_sfnoci.py
@@ -24,7 +24,6 @@ def setUpModule():
 
 def tearDownModule():
     global mol, mo0, setocc, ro_occ
-    mol.stdout.close()
     del mol, mo0, setocc, ro_occ
 
 class KnownValues(unittest.TestCase):
@@ -108,4 +107,4 @@ def test_sfgnoci(self):
 
 if __name__ == "__main__":
     print("Full Tests for SF(G)NOCI")
-    unittest.main()
\ No newline at end of file
+    unittest.main()