Repository: pyscf/pyscf — pull request #1638 (python)
Title: DIIS instability due to numerical noises (fix issue #1524)

Close issue #1524 

Resolved issues:
title: DIIS fails to converge triplet O in cc-pVDZ
body: The oxygen 3P wave function cannot be converged even in cc-pVDZ with DIIS. Without DIIS the calculation works.
```
import pyscf

mol = pyscf.M(
    atom = 'O',
    spin = 2,
    basis = 'cc-pVDZ',
    symmetry = True,
    verbose = 4)

# This fails                                                                                                                                                                                  
mf = mol.ROHF()
mf.init_guess = '1e'
mf.irrep_nelec = {'s+0' : (2,2), 'p-1' : (1,0), 'p+0' : (1,0), 'p+1' : (1,1) }
mf.kernel()

# This works                                                                                                                                                                                  
mf = mol.ROHF()
mf.init_guess = '1e'
mf.irrep_nelec = {'s+0' : (2,2), 'p-1' : (1,0), 'p+0' : (1,0), 'p+1' : (1,1) }
mf.diis = None
mf.kernel()
```

Hints:

Fix patch:
diff --git a/pyscf/scf/diis.py b/pyscf/scf/diis.py
index 21dba408b6..27e6e8cc0a 100644
--- a/pyscf/scf/diis.py
+++ b/pyscf/scf/diis.py
@@ -43,6 +43,10 @@ def __init__(self, mf=None, filename=None, Corth=None):
         self.rollback = 0
         self.space = 8
         self.Corth = Corth
+        #?self._scf = mf
+        #?if hasattr(self._scf, 'get_orbsym'): # Symmetry adapted SCF objects
+        #?    self.orbsym = mf.get_orbsym(Corth)
+        #?    sym_forbid = self.orbsym[:,None] != self.orbsym
 
     def update(self, s, d, f, *args, **kwargs):
         errvec = get_err_vec(s, d, f, self.Corth)
@@ -83,16 +87,24 @@ def get_err_vec_orig(s, d, f):
 
 def get_err_vec_orth(s, d, f, Corth):
     '''error vector in orthonormal basis = C.T.conj() (SDF - FDS) C'''
+    # Symmetry information to reduce numerical error in DIIS (issue #1524)
+    orbsym = getattr(Corth, 'orbsym', None)
+    if orbsym is not None:
+        sym_forbid = orbsym[:,None] != orbsym
+
     if isinstance(f, numpy.ndarray) and f.ndim == 2:
-        sdf = reduce(numpy.dot, (s,d,f))
-        errvec = Corth.conj().T.dot(sdf.conj().T - sdf).dot(Corth).ravel()
+        sdf = reduce(numpy.dot, (Corth.conj().T, s, d, f, Corth))
+        if orbsym is not None:
+            sdf[sym_forbid] = 0
+        errvec = (sdf.conj().T - sdf).ravel()
 
     elif isinstance(f, numpy.ndarray) and f.ndim == 3 and s.ndim == 3:
         errvec = []
         for i in range(f.shape[0]):
-            sdf = reduce(numpy.dot, (s[i], d[i], f[i]))
-            errvec.append(
-                Corth[i].conj().T.dot(sdf.conj().T - sdf).dot(Corth[i]).ravel())
+            sdf = reduce(numpy.dot, (Corth[i].conj().T, s[i], d[i], f[i], Corth[i]))
+            if orbsym is not None:
+                sdf[sym_forbid] = 0
+            errvec.append((sdf.conj().T - sdf).ravel())
         errvec = numpy.vstack(errvec).ravel()
 
     elif f.ndim == s.ndim+1 and f.shape[0] == 2:  # for UHF
diff --git a/pyscf/scf/ghf_symm.py b/pyscf/scf/ghf_symm.py
index d0a5709e4b..8ddc0a6c11 100644
--- a/pyscf/scf/ghf_symm.py
+++ b/pyscf/scf/ghf_symm.py
@@ -39,9 +39,6 @@
 def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN,
             **kwargs):
     mol = mf.mol
-    if not mol.symmetry:
-        return ghf.analyze(mf, verbose, **kwargs)
-
     mo_energy = mf.mo_energy
     mo_occ = mf.mo_occ
     mo_coeff = mf.mo_coeff
@@ -83,7 +80,7 @@ def canonicalize(mf, mo_coeff, mo_occ, fock=None):
     '''
     mol = mf.mol
     if not mol.symmetry:
-        return ghf.canonicalize(mf, mo_coeff, mo_occ, fock)
+        raise RuntimeError('mol.symmetry not enabled')
 
     if getattr(mo_coeff, 'orbsym', None) is not None:
         return hf_symm.canonicalize(mf, mo_coeff, mo_occ, fock)
@@ -113,42 +110,42 @@ def dump_flags(self, verbose=None):
 
     def build(self, mol=None):
         if mol is None: mol = self.mol
-        if mol.symmetry:
-            for irname in self.irrep_nelec:
-                if irname not in mol.irrep_name:
-                    logger.warn(self, 'Molecule does not have irrep %s', irname)
-
-            nelec_fix = self.irrep_nelec.values()
-            if any(isinstance(x, (tuple, list)) for x in nelec_fix):
-                msg =('Number of alpha/beta electrons cannot be assigned '
-                      'separately in GHF.  irrep_nelec = %s' % self.irrep_nelec)
-                raise ValueError(msg)
-            nelec_fix = sum(nelec_fix)
-            float_irname = set(mol.irrep_name) - set(self.irrep_nelec)
-            if nelec_fix > mol.nelectron:
-                msg =('More electrons defined by irrep_nelec than total num electrons. '
-                      'mol.nelectron = %d  irrep_nelec = %s' %
-                      (mol.nelectron, self.irrep_nelec))
-                raise ValueError(msg)
-            else:
-                logger.info(mol, 'Freeze %d electrons in irreps %s',
-                            nelec_fix, self.irrep_nelec.keys())
-
-            if len(float_irname) == 0 and nelec_fix != mol.nelectron:
-                msg =('Num electrons defined by irrep_nelec != total num electrons. '
-                      'mol.nelectron = %d  irrep_nelec = %s' %
-                      (mol.nelectron, self.irrep_nelec))
-                raise ValueError(msg)
-            else:
-                logger.info(mol, '    %d free electrons in irreps %s',
-                            mol.nelectron-nelec_fix, ' '.join(float_irname))
+        if not mol.symmetry:
+            raise RuntimeError('mol.symmetry not enabled')
+
+        for irname in self.irrep_nelec:
+            if irname not in mol.irrep_name:
+                logger.warn(self, 'Molecule does not have irrep %s', irname)
+
+        nelec_fix = self.irrep_nelec.values()
+        if any(isinstance(x, (tuple, list)) for x in nelec_fix):
+            msg =('Number of alpha/beta electrons cannot be assigned '
+                  'separately in GHF.  irrep_nelec = %s' % self.irrep_nelec)
+            raise ValueError(msg)
+        nelec_fix = sum(nelec_fix)
+        float_irname = set(mol.irrep_name) - set(self.irrep_nelec)
+        if nelec_fix > mol.nelectron:
+            msg =('More electrons defined by irrep_nelec than total num electrons. '
+                  'mol.nelectron = %d  irrep_nelec = %s' %
+                  (mol.nelectron, self.irrep_nelec))
+            raise ValueError(msg)
+        else:
+            logger.info(mol, 'Freeze %d electrons in irreps %s',
+                        nelec_fix, self.irrep_nelec.keys())
+
+        if len(float_irname) == 0 and nelec_fix != mol.nelectron:
+            msg =('Num electrons defined by irrep_nelec != total num electrons. '
+                  'mol.nelectron = %d  irrep_nelec = %s' %
+                  (mol.nelectron, self.irrep_nelec))
+            raise ValueError(msg)
+        else:
+            logger.info(mol, '    %d free electrons in irreps %s',
+                        mol.nelectron-nelec_fix, ' '.join(float_irname))
         return ghf.GHF.build(self, mol)
 
     def eig(self, h, s, symm_orb=None, irrep_id=None):
         if symm_orb is None or irrep_id is None:
             mol = self.mol
-            if not mol.symmetry:
-                return self._eigh(h, s)
             symm_orb = mol.symm_orb
             irrep_id = mol.irrep_id
 
@@ -171,12 +168,11 @@ def eig(self, h, s, symm_orb=None, irrep_id=None):
 
     def get_grad(self, mo_coeff, mo_occ, fock=None):
         g = ghf.GHF.get_grad(self, mo_coeff, mo_occ, fock)
-        if self.mol.symmetry:
-            occidx = mo_occ > 0
-            viridx = ~occidx
-            orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
-            sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx]
-            g[sym_forbid.ravel()] = 0
+        occidx = mo_occ > 0
+        viridx = ~occidx
+        orbsym = self.get_orbsym(mo_coeff)
+        sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx]
+        g[sym_forbid.ravel()] = 0
         return g
 
     def get_occ(self, mo_energy=None, mo_coeff=None):
@@ -186,9 +182,9 @@ def get_occ(self, mo_energy=None, mo_coeff=None):
         if mo_energy is None: mo_energy = self.mo_energy
         mol = self.mol
         if not mol.symmetry:
-            return ghf.GHF.get_occ(self, mo_energy, mo_coeff)
+            raise RuntimeError('mol.symmetry not enabled')
 
-        orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(mo_coeff)
         mo_occ = numpy.zeros_like(mo_energy)
         rest_idx = numpy.ones(mo_occ.size, dtype=bool)
         nelec_fix = 0
@@ -243,7 +239,7 @@ def _finalize(self):
         # ordering of the symmetry labels when two orbitals are degenerated.
         o_sort = numpy.argsort(self.mo_energy[self.mo_occ> 0].round(9), kind='mergesort')
         v_sort = numpy.argsort(self.mo_energy[self.mo_occ==0].round(9), kind='mergesort')
-        orbsym = self.get_orbsym(self.mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(self.mo_coeff)
         self.mo_energy = numpy.hstack((self.mo_energy[self.mo_occ> 0][o_sort],
                                        self.mo_energy[self.mo_occ==0][v_sort]))
         self.mo_coeff = numpy.hstack((self.mo_coeff[:,self.mo_occ> 0].take(o_sort, axis=1),
@@ -276,6 +272,8 @@ def get_irrep_nelec(self, mol=None, mo_coeff=None, mo_occ=None, s=None):
     def get_orbsym(self, mo_coeff=None, s=None):
         if mo_coeff is None:
             mo_coeff = self.mo_coeff
+        if getattr(mo_coeff, 'orbsym', None) is not None:
+            return mo_coeff.orbsym
         if s is None:
             s = self.get_ovlp()
         return numpy.asarray(get_orbsym(self.mol, mo_coeff, s))
diff --git a/pyscf/scf/hf_symm.py b/pyscf/scf/hf_symm.py
index 2e5d577bd5..40eab05874 100644
--- a/pyscf/scf/hf_symm.py
+++ b/pyscf/scf/hf_symm.py
@@ -53,9 +53,6 @@ def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN,
     from pyscf.lo import orth
     from pyscf.tools import dump_mat
     mol = mf.mol
-    if not mol.symmetry:
-        return hf.analyze(mf, verbose, with_meta_lowdin, **kwargs)
-
     mo_energy = mf.mo_energy
     mo_occ = mf.mo_occ
     mo_coeff = mf.mo_coeff
@@ -153,8 +150,7 @@ def canonicalize(mf, mo_coeff, mo_occ, fock=None):
     '''
     mol = mf.mol
     if not mol.symmetry:
-        return hf.canonicalize(mf, mo_coeff, mo_occ, fock)
-
+        raise RuntimeError('mol.symmetry not enabled')
     if fock is None:
         dm = mf.make_rdm1(mo_coeff, mo_occ)
         fock = mf.get_hcore() + mf.get_veff(mf.mol, dm)
@@ -305,7 +301,7 @@ def eig(mf, h, s, symm_orb=None, irrep_id=None):
     mol = mf.mol
     if symm_orb is None or irrep_id is None:
         if not mol.symmetry:
-            return mf._eigh(h, s)
+            raise RuntimeError('mol.symmetry not enabled')
         symm_orb = mol.symm_orb
         irrep_id = mol.irrep_id
 
@@ -344,14 +340,15 @@ def eig(mf, h, s, symm_orb=None, irrep_id=None):
     return e, c
 
 def get_orbsym(mol, mo_coeff, s=None, check=False, symm_orb=None, irrep_id=None):
+    if getattr(mo_coeff, 'orbsym', None) is not None:
+        return mo_coeff.orbsym
+
     if symm_orb is None or irrep_id is None:
         symm_orb = mol.symm_orb
         irrep_id = mol.irrep_id
     if mo_coeff is None:
         orbsym = numpy.hstack([[ir] * symm_orb[i].shape[1]
                                for i, ir in enumerate(irrep_id)])
-    elif getattr(mo_coeff, 'orbsym', None) is not None:
-        orbsym = mo_coeff.orbsym
     else:
         orbsym = symm.label_orb_symm(mol, irrep_id, symm_orb,
                                      mo_coeff, s, check)
@@ -445,33 +442,32 @@ def __init__(self, mol):
 
     def build(self, mol=None):
         if mol is None: mol = self.mol
-        if mol.symmetry:
-            check_irrep_nelec(mol, self.irrep_nelec, self.mol.nelectron)
+        if not mol.symmetry:
+            raise RuntimeError('mol.symmetry not enabled')
+        check_irrep_nelec(mol, self.irrep_nelec, self.mol.nelectron)
         return hf.RHF.build(self, mol)
 
     eig = eig
 
     def get_grad(self, mo_coeff, mo_occ, fock=None):
         g = hf.RHF.get_grad(self, mo_coeff, mo_occ, fock)
-        if self.mol.symmetry:
-            occidx = mo_occ > 0
-            viridx = ~occidx
-            orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
-            sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx]
-            g[sym_forbid.ravel()] = 0
+        occidx = mo_occ > 0
+        viridx = ~occidx
+        orbsym = self.get_orbsym(mo_coeff)
+        sym_forbid = orbsym[viridx].reshape(-1,1) != orbsym[occidx]
+        g[sym_forbid.ravel()] = 0
         return g
 
     def get_occ(self, mo_energy=None, mo_coeff=None):
         ''' We assumed mo_energy are grouped by symmetry irreps, (see function
         self.eig). The orbitals are sorted after SCF.
         '''
-
         if mo_energy is None: mo_energy = self.mo_energy
         mol = self.mol
         if not mol.symmetry:
-            return hf.RHF.get_occ(self, mo_energy, mo_coeff)
+            raise RuntimeError('mol.symmetry not enabled')
 
-        orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(mo_coeff)
         mo_occ = numpy.zeros_like(mo_energy)
         rest_idx = numpy.ones(mo_occ.size, dtype=bool)
         nelec_fix = 0
@@ -528,7 +524,7 @@ def _finalize(self):
                             idx[self.mo_occ==0][v_sort]))
         self.mo_energy = self.mo_energy[idx]
         self.mo_occ = self.mo_occ[idx]
-        orbsym = self.get_orbsym(self.mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(self.mo_coeff)
         orbsym = orbsym[idx]
         degen_mapping = None
         if self.mol.groupname in ('Dooh', 'Coov'):
@@ -563,6 +559,8 @@ def get_irrep_nelec(self, mol=None, mo_coeff=None, mo_occ=None, s=None):
     def get_orbsym(self, mo_coeff=None, s=None):
         if mo_coeff is None:
             mo_coeff = self.mo_coeff
+        if getattr(mo_coeff, 'orbsym', None) is not None:
+            return mo_coeff.orbsym
         if s is None:
             s = self.get_ovlp()
         return numpy.asarray(get_orbsym(self.mol, mo_coeff, s))
@@ -615,21 +613,22 @@ def dump_flags(self, verbose=None):
 
     def build(self, mol=None):
         if mol is None: mol = self.mol
-        if mol.symmetry:
-            fix_na, fix_nb = check_irrep_nelec(mol, self.irrep_nelec, self.nelec)[:2]
-            alpha_open = beta_open = False
-            for ne in self.irrep_nelec.values():
-                if not isinstance(ne, (int, numpy.integer)):
-                    alpha_open |= ne[0] > ne[1]
-                    beta_open  |= ne[0] < ne[1]
-
-            frozen_spin = fix_na - fix_nb
-            if ((alpha_open and beta_open) or
-                (0 < mol.spin < frozen_spin) or (frozen_spin < 0 < mol.spin) or
-                (frozen_spin < mol.spin < 0) or (mol.spin < 0 < frozen_spin)):
-                raise ValueError('Low-spin configuration was found in '
-                                 'the irrep_nelec input. ROHF does not '
-                                 'support low-spin configuration.')
+        if not mol.symmetry:
+            raise RuntimeError('mol.symmetry not enabled')
+        fix_na, fix_nb = check_irrep_nelec(mol, self.irrep_nelec, self.nelec)[:2]
+        alpha_open = beta_open = False
+        for ne in self.irrep_nelec.values():
+            if not isinstance(ne, (int, numpy.integer)):
+                alpha_open |= ne[0] > ne[1]
+                beta_open  |= ne[0] < ne[1]
+
+        frozen_spin = fix_na - fix_nb
+        if ((alpha_open and beta_open) or
+            (0 < mol.spin < frozen_spin) or (frozen_spin < 0 < mol.spin) or
+            (frozen_spin < mol.spin < 0) or (mol.spin < 0 < frozen_spin)):
+            raise ValueError('Low-spin configuration was found in '
+                             'the irrep_nelec input. ROHF does not '
+                             'support low-spin configuration.')
         return hf.RHF.build(self, mol)
 
     @lib.with_doc(eig.__doc__)
@@ -643,25 +642,24 @@ def eig(self, fock, s):
 
     def get_grad(self, mo_coeff, mo_occ, fock=None):
         g = rohf.ROHF.get_grad(self, mo_coeff, mo_occ, fock)
-        if self.mol.symmetry:
-            occidxa = mo_occ > 0
-            occidxb = mo_occ == 2
-            viridxa = ~occidxa
-            viridxb = ~occidxb
-            uniq_var_a = viridxa.reshape(-1,1) & occidxa
-            uniq_var_b = viridxb.reshape(-1,1) & occidxb
-
-            orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
-            sym_forbid = orbsym.reshape(-1,1) != orbsym
-            sym_forbid = sym_forbid[uniq_var_a | uniq_var_b]
-            g[sym_forbid.ravel()] = 0
+        occidxa = mo_occ > 0
+        occidxb = mo_occ == 2
+        viridxa = ~occidxa
+        viridxb = ~occidxb
+        uniq_var_a = viridxa.reshape(-1,1) & occidxa
+        uniq_var_b = viridxb.reshape(-1,1) & occidxb
+
+        orbsym = self.get_orbsym(mo_coeff)
+        sym_forbid = orbsym.reshape(-1,1) != orbsym
+        sym_forbid = sym_forbid[uniq_var_a | uniq_var_b]
+        g[sym_forbid.ravel()] = 0
         return g
 
     def get_occ(self, mo_energy=None, mo_coeff=None):
         if mo_energy is None: mo_energy = self.mo_energy
         mol = self.mol
         if not mol.symmetry:
-            return rohf.ROHF.get_occ(self, mo_energy, mo_coeff)
+            raise RuntimeError('mol.symmetry not enabled')
 
         if getattr(mo_energy, 'mo_ea', None) is not None:
             mo_ea = mo_energy.mo_ea
@@ -670,8 +668,7 @@ def get_occ(self, mo_energy=None, mo_coeff=None):
             mo_ea = mo_eb = mo_energy
         nmo = mo_ea.size
         mo_occ = numpy.zeros(nmo)
-
-        orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(mo_coeff)
 
         rest_idx = numpy.ones(mo_occ.size, dtype=bool)
         neleca_fix = 0
@@ -790,7 +787,7 @@ def _finalize(self):
         else:
             self.mo_energy = self.mo_energy[idx]
         self.mo_occ = self.mo_occ[idx]
-        orbsym = self.get_orbsym(self.mo_coeff, self.get_ovlp())
+        orbsym = self.get_orbsym(self.mo_coeff)
         orbsym = orbsym[idx]
         degen_mapping = None
         if self.mol.groupname in ('Dooh', 'Coov'):
@@ -813,9 +810,6 @@ def analyze(self, verbose=None, with_meta_lowdin=WITH_META_LOWDIN,
         if verbose is None: verbose = self.verbose
         from pyscf.lo import orth
         from pyscf.tools import dump_mat
-        if not self.mol.symmetry:
-            return rohf.ROHF.analyze(self, verbose, with_meta_lowdin, **kwargs)
-
         mol = self.mol
         mo_energy = self.mo_energy
         mo_occ = self.mo_occ
@@ -826,7 +820,7 @@ def analyze(self, verbose=None, with_meta_lowdin=WITH_META_LOWDIN,
             self.dump_scf_summary(log)
 
             nirrep = len(mol.irrep_id)
-            orbsym = self.get_orbsym(mo_coeff, self.get_ovlp())
+            orbsym = self.get_orbsym(mo_coeff)
             irreps = numpy.asarray(mol.irrep_id)
             ndoccs = numpy.count_nonzero(irreps[:,None] == orbsym[mo_occ==2], axis=1)
             nsoccs = numpy.count_nonzero(irreps[:,None] == orbsym[mo_occ==1], axis=1)
@@ -926,12 +920,7 @@ def canonicalize(self, mo_coeff, mo_occ, fock=None):
         mo_e = lib.tag_array(mo_e, mo_ea=mo_ea, mo_eb=mo_eb)
         return mo_e, mo_coeff
 
-    def get_orbsym(self, mo_coeff=None, s=None):
-        if mo_coeff is None:
-            mo_coeff = self.mo_coeff
-        if s is None:
-            s = self.get_ovlp()
-        return numpy.asarray(get_orbsym(self.mol, mo_coeff, s))
+    get_orbsym = SymAdaptedRHF.get_orbsym
     orbsym = property(get_orbsym)
 
     get_wfnsym = get_wfnsym
diff --git a/pyscf/scf/uhf_symm.py b/pyscf/scf/uhf_symm.py
index 447457d371..5acbe23d62 100644
--- a/pyscf/scf/uhf_symm.py
+++ b/pyscf/scf/uhf_symm.py
@@ -41,9 +41,6 @@ def analyze(mf, verbose=logger.DEBUG, with_meta_lowdin=WITH_META_LOWDIN,
     from pyscf.lo import orth
     from pyscf.tools import dump_mat
     mol = mf.mol
-    if not mol.symmetry:
-        return uhf.analyze(mf, verbose, with_meta_lowdin, **kwargs)
-
     mo_energy = mf.mo_energy
     mo_occ = mf.mo_occ
     mo_coeff = mf.mo_coeff
@@ -193,8 +190,7 @@ def canonicalize(mf, mo_coeff, mo_occ, fock=None):
     '''
     mol = mf.mol
     if not mol.symmetry:
-        return uhf.canonicalize(mf, mo_coeff, mo_occ, fock)
-
+        raise RuntimeError('mol.symmetry not enabled')
     mo_occ = numpy.asarray(mo_occ)
     assert (mo_occ.ndim == 2)
     if fock is None:
@@ -326,15 +322,13 @@ def dump_flags(self, verbose=None):
 
     def build(self, mol=None):
         if mol is None: mol = self.mol
-        if mol.symmetry:
-            hf_symm.check_irrep_nelec(mol, self.irrep_nelec, self.nelec)
+        if not mol.symmetry:
+            raise RuntimeError('mol.symmetry not enabled')
+        hf_symm.check_irrep_nelec(mol, self.irrep_nelec, self.nelec)
         return uhf.UHF.build(self, mol)
 
     def eig(self, h, s):
         mol = self.mol
-        if not mol.symmetry:
-            return self._eigh(h, s)
-
         nirrep = mol.symm_orb.__len__()
         s = symm.symmetrize_matrix(s, mol.symm_orb)
         ha = symm.symmetrize_matrix(h[0], mol.symm_orb)
@@ -391,17 +385,16 @@ def eig(self, h, s):
 
     def get_grad(self, mo_coeff, mo_occ, fock=None):
         g = uhf.UHF.get_grad(self, mo_coeff, mo_occ, fock)
-        if self.mol.symmetry:
-            occidxa = mo_occ[0] > 0
-            occidxb = mo_occ[1] > 0
-            viridxa = ~occidxa
-            viridxb = ~occidxb
-            orbsyma, orbsymb = self.get_orbsym(mo_coeff, self.get_ovlp())
-            sym_forbida = orbsyma[viridxa].reshape(-1,1) != orbsyma[occidxa]
-            sym_forbidb = orbsymb[viridxb].reshape(-1,1) != orbsymb[occidxb]
-            sym_forbid = numpy.hstack((sym_forbida.ravel(),
-                                       sym_forbidb.ravel()))
-            g[sym_forbid] = 0
+        occidxa = mo_occ[0] > 0
+        occidxb = mo_occ[1] > 0
+        viridxa = ~occidxa
+        viridxb = ~occidxb
+        orbsyma, orbsymb = self.get_orbsym(mo_coeff)
+        sym_forbida = orbsyma[viridxa].reshape(-1,1) != orbsyma[occidxa]
+        sym_forbidb = orbsymb[viridxb].reshape(-1,1) != orbsymb[occidxb]
+        sym_forbid = numpy.hstack((sym_forbida.ravel(),
+                                   sym_forbidb.ravel()))
+        g[sym_forbid] = 0
         return g
 
     def get_occ(self, mo_energy=None, mo_coeff=None):
@@ -411,9 +404,9 @@ def get_occ(self, mo_energy=None, mo_coeff=None):
         if mo_energy is None: mo_energy = self.mo_energy
         mol = self.mol
         if not mol.symmetry:
-            return uhf.UHF.get_occ(self, mo_energy, mo_coeff)
+            raise RuntimeError('mol.symmetry not enabled')
 
-        orbsyma, orbsymb = self.get_orbsym(mo_coeff, self.get_ovlp())
+        orbsyma, orbsymb = self.get_orbsym(mo_coeff)
         mo_occ = numpy.zeros_like(mo_energy)
         idx_ea_left = []
         idx_eb_left = []
@@ -516,7 +509,7 @@ def _finalize(self):
         idxb = numpy.hstack((idxb[self.mo_occ[1]> 0][ob_sort],
                              idxb[self.mo_occ[1]==0][vb_sort]))
         self.mo_energy = (ea[idxa], eb[idxb])
-        orbsyma, orbsymb = self.get_orbsym(self.mo_coeff, self.get_ovlp())
+        orbsyma, orbsymb = self.get_orbsym(self.mo_coeff)
         orbsyma = orbsyma[idxa]
         orbsymb = orbsymb[idxb]
         degen_a = degen_b = None
@@ -558,6 +551,8 @@ def get_irrep_nelec(self, mol=None, mo_coeff=None, mo_occ=None, s=None):
     def get_orbsym(self, mo_coeff=None, s=None):
         if mo_coeff is None:
             mo_coeff = self.mo_coeff
+        if getattr(mo_coeff, 'orbsym', None) is not None:
+            return mo_coeff.orbsym
         if s is None:
             s = self.get_ovlp()
         return get_orbsym(self.mol, mo_coeff, s)


Test patch:
diff --git a/pyscf/scf/test/test_diis.py b/pyscf/scf/test/test_diis.py
index 442c5a716a..afa53d989b 100644
--- a/pyscf/scf/test/test_diis.py
+++ b/pyscf/scf/test/test_diis.py
@@ -94,6 +94,17 @@ def test_diis_restart(self):
         e = mf.kernel()
         self.assertAlmostEqual(e, eref, 9)
 
+    # issue 1524
+    def test_diis_for_symmetry_adapted_scf(self):
+        mol = gto.M(atom='O', spin=2, basis='ccpvdz', symmetry=True)
+        mf = mol.ROHF()
+        mf.init_guess = '1e'
+        mf.irrep_nelec = {'s+0' : (2,2), 'p-1' : (1,0), 'p+0' : (1,0), 'p+1' : (1,1) }
+        mf.kernel()
+        self.assertTrue(mf.converged)
+        self.assertAlmostEqual(mf.e_tot, -74.7874921601008, 9)
+
 
 if __name__ == "__main__":
     print("Full Tests for DIIS")
diff --git a/pyscf/scf/test/test_rhf.py b/pyscf/scf/test/test_rhf.py
index 1109b7ff7e..ffd4876fef 100644
--- a/pyscf/scf/test/test_rhf.py
+++ b/pyscf/scf/test/test_rhf.py
@@ -590,8 +590,7 @@ def test_rohf_symm_get_occ(self):
                 [0, 2, 0, 0, 0, 1, 2, 0, 1, 2, 0, 0, 2, 0, 2, 0, 0, 0, 0, 2]))
 
         mf1 = scf.RHF(mol).set(verbose=0).view(scf.hf_symm.ROHF)
-        self.assertTrue(numpy.allclose(mf1.get_occ(energy, mo_coeff),
-                [0 ,2 ,0 ,0 ,0 ,0 ,2 ,0 ,0 ,0 ,0 ,0 ,2 ,0 ,2 ,0 ,0 ,0 ,0 ,2]))
+        self.assertRaises(RuntimeError, mf1.get_occ, energy, mo_coeff)
 
     def test_get_occ_extreme_case(self):
         mol = gto.M(atom='He', verbose=7, output='/dev/null')