Repository: pyscf/pyscf — pull request #2290 (python)
Title: Fix addons.smearing RHF electron number

Fixes #2289.

Resolved issues:
title: Smearing electron number bug
body: Smeared RHF with odd nelec gives incorrect electron number.

```
from pyscf import gto, scf
from pyscf.scf import addons

mol = gto.Mole()
mol.atom = '''
    7      0.   0  -0.7
    7      0.   0   0.7'''
mol.basis = 'cc-pvdz'
mol.charge = -1
mol.spin = 1
mol.build()
mf = scf.RHF(mol)
mf = addons.smearing(mf, sigma=0.1)
mf.kernel()
print(mf.mol.nelectron)
print(mf.mo_occ.sum())
```

```
converged SCF energy = -42.2391465363651
15
16.0
```

Hints:

Fix patch:
diff --git a/pyscf/scf/addons.py b/pyscf/scf/addons.py
index a120087f33..4f26a5367b 100644
--- a/pyscf/scf/addons.py
+++ b/pyscf/scf/addons.py
@@ -83,7 +83,7 @@ def nelec_cost_fn(m, mo_es, nocc):
 
 def _get_fermi(mo_energy, nocc):
     mo_e_sorted = numpy.sort(mo_energy)
-    return mo_e_sorted[nocc-1]
+    return mo_e_sorted[numpy.ceil(nocc).astype(int) - 1]
 
 class _SmearingSCF:
 
@@ -174,7 +174,7 @@ def get_occ(self, mo_energy=None, mo_coeff=None):
             else:
                 mo_es = mo_energy
             if is_rhf:
-                nocc = (nelectron + 1) // 2
+                nocc = nelectron / 2
 
             if self.mu0 is None:
                 mu, mo_occs = _smearing_optimize(f_occ, mo_es, nocc, sigma)


Test patch:
diff --git a/pyscf/scf/test/test_addons.py b/pyscf/scf/test/test_addons.py
index 88595191cd..76ed2a5cba 100644
--- a/pyscf/scf/test/test_addons.py
+++ b/pyscf/scf/test/test_addons.py
@@ -459,6 +459,27 @@ def test_rohf_smearing(self):
         self.assertAlmostEqual(myhf_s.e_tot, -243.086989253, 5)
         self.assertAlmostEqual(myhf_s.entropy, 17.11431, 4)
 
+    def test_rhf_smearing_nelec(self):
+        mol = gto.Mole()
+        mol.verbose = 5
+        mol.output = '/dev/null'
+        mol.atom = '''
+            7      0.   0  -0.7
+            7      0.   0   0.7'''
+        mol.basis = 'cc-pvdz'
+        mol.charge = +1
+        mol.spin = 1
+        mol.build()
+        mf = scf.RHF(mol)
+        mf = addons.frac_occ(mf)
+        e_frac = mf.kernel()
+
+        mf_smear = scf.RHF(mol)
+        mf_smear = addons.smearing(mf_smear, sigma=1e-5, method='fermi')
+        e_smear = mf_smear.kernel()
+        self.assertAlmostEqual(mf.mo_occ.sum(), mf_smear.mo_occ.sum(), 5)
+        self.assertAlmostEqual(e_frac, e_smear, 9)
+
     def test_smearing_mu0(self):
         def _hubbard_hamilts_pbc(L, U):
             h1e = numpy.zeros((L, L))