Source code for pyscf.mcpdft.mspdft

#!/usr/bin/env python
# Copyright 2014-2022 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.
#
import numpy as np
from scipy import linalg
from pyscf import lib
from pyscf.mcscf.addons import StateAverageMCSCFSolver
from pyscf.mcscf.addons import StateAverageMixFCISolver
from pyscf.mcscf.df import _DFCASSCF
from pyscf.mcscf import mc1step
from pyscf.fci import direct_spin1
from pyscf import mcpdft
from pyscf import __config__

# API for general multi-state MC-PDFT method object
# In principle, various forms can be implemented: CMS, XMS, etc.

# API cleanup desiderata:
# 1. "sipdft", "state_interaction" -> "mspdft", "multi_state"
# 2. Canonicalize function to quickly generate mo_coeff, ci, mo_occ, mo_energy
#    for different choices of intermediate, reference, final states.
# 3. Probably "_finalize" stuff?
# 4. checkpoint stuff

MAX_CYC_DIABATIZE = getattr(__config__, 'mcpdft_mspdft_max_cyc_diabatize', 50)
CONV_TOL_DIABATIZE = getattr(__config__, 'mcpdft_mspdft_conv_tol_diabatize', 1e-8)
SING_TOL_DIABATIZE = getattr(__config__, 'mcpdft_mspdft_sing_tol_diabatize', 1e-8)
NUDGE_TOL_DIABATIZE = getattr(__config__, 'mcpdft_mspdft_nudge_tol_diabatize', 1e-3)

[docs] def make_heff_mcscf (mc, mo_coeff=None, ci=None): '''Build Hamiltonian matrix in basis of ci vector Args: mc : an instance of MCPDFT class Kwargs: mo_coeff : ndarray of shape (nao, nmo) MO coefficients ci : ndarray or list of len (nroots) CI vectors describing the model space, presumed to be in the optimized intermediate-state basis Returns: heff_mcscf : ndarray of shape (nroots, nroots) Effective MC-SCF hamiltonian matrix in the basis of the provided CI vectors ''' if mo_coeff is None: mo_coeff = mc.mo_coeff if ci is None: ci = mc.ci h1, h0 = mc.get_h1eff (mo_coeff) h2 = mc.get_h2eff (mo_coeff) h2eff = direct_spin1.absorb_h1e (h1, h2, mc.ncas, mc.nelecas, 0.5) def construct_ham_slice(solver, slice, nelecas): ci_irrep = ci[slice] if hasattr(solver, "orbsym"): solver.orbsym = mc.fcisolver.orbsym hc_all_irrep = [solver.contract_2e(h2eff, c, mc.ncas, nelecas) for c in ci_irrep] heff_irrep = np.tensordot(ci_irrep, hc_all_irrep, axes=((1, 2), (1, 2))) diag_idx = np.diag_indices_from(heff_irrep) heff_irrep[diag_idx] += h0 return heff_irrep if not isinstance(mc.fcisolver, StateAverageMixFCISolver): return construct_ham_slice(direct_spin1, slice(0, len(ci)), mc.nelecas) irrep_slices = [] start = 0 for solver in mc.fcisolver.fcisolvers: end = start+solver.nroots irrep_slices.append(slice(start, end)) start = end return [construct_ham_slice(s, irrep, mc.fcisolver._get_nelec(s, mc.nelecas)) for s, irrep in zip(mc.fcisolver.fcisolvers, irrep_slices)]
[docs] def si_newton (mc, ci=None, objfn=None, max_cyc=None, conv_tol=None, sing_tol=None, nudge_tol=None): '''Optimize the intermediate states describing the model space of an MS-PDFT calculation by maximizing the provided objective function using a gradient-ascent algorithm Args: mc : an instance of MSPDFT class Kwargs: ci : ndarray or list of len (nroots) CI vectors spanning the model space objfn : callable Takes CI vectors as a kwarg and returns the value, gradient, and Hessian of a chosen objective function wrt rotation between pairs of CI vectors max_cyc : integer Maximum number of cycles of the gradient-ascent algorithm conv_tol : float Maximum value of both gradient and step vectors at convergence sing_tol : float Tolerance for determining when normal coordinate belongs to the null space (df = d2f = 0) or when the Hessian is singular (df != 0, d2f = 0). nudge_tol : float Minimum step size along a normal coordinate when the surface is locally concave. Returns: conv : logical True if the optimization is converged ci : list of len (nroots) Optimized CI vectors describing intermediate states ''' if ci is None: ci = mc.ci if objfn is None: objfn = mc.diabatizer if max_cyc is None: max_cyc = getattr (mc, 'max_cyc_diabatize', MAX_CYC_DIABATIZE) if conv_tol is None: conv_tol = getattr (mc, 'conv_tol_diabatize', CONV_TOL_DIABATIZE) if sing_tol is None: sing_tol = getattr (mc, 'sing_tol_diabatize', SING_TOL_DIABATIZE) if nudge_tol is None: nudge_tol = getattr (mc, 'nudge_tol_diabatize', NUDGE_TOL_DIABATIZE) ci = np.array (ci) # copy log = lib.logger.new_logger (mc, mc.verbose) nroots = mc.fcisolver.nroots rows,col = np.tril_indices(nroots,k=-1) u = np.eye (nroots) t = np.zeros((nroots,nroots)) conv = False hdr = '{} intermediate-state'.format (mc.__class__.__name__) f, df, d2f, f_update = objfn (ci=ci) for it in range(max_cyc): log.info ("****iter {} ***********".format (it)) log.info ("{} objective function value = {}".format (hdr, f)) # Analyze Hessian d2f, evecs = linalg.eigh (d2f) evecs = np.array(evecs) df = np.dot (df, evecs) d2f_zero = np.abs (d2f) < sing_tol df_zero = np.abs (df) < sing_tol if np.any (d2f_zero & (~df_zero)): log.warn ("{} Hess is singular!".format (hdr)) idx_null = d2f_zero & df_zero df[idx_null] = 0.0 d2f[idx_null] = -1e-16 pos_idx = d2f > 0 neg_def = np.all (d2f < 0) log.info ("{} Hessian is negative-definite? {}".format (hdr, neg_def)) # Analyze gradient grad_norm = np.linalg.norm(df) log.info ("{} grad norm = %f".format (hdr), grad_norm) log.info ("{} grad (normal modes) = {}".format (hdr, df)) # Take step df[pos_idx & (np.abs (df/d2f) < nudge_tol)] = nudge_tol Dt = df/np.abs (d2f) step_norm = np.linalg.norm (Dt) log.info ("{} Hessian eigenvalues: {}".format (hdr, d2f)) log.info ("{} step vector (normal modes): {}".format (hdr, Dt)) t[:] = 0 t[np.tril_indices(t.shape[0], k = -1)] = np.dot (Dt, evecs.T) t = t - t.T if grad_norm < conv_tol and step_norm < conv_tol and neg_def: conv = True break # I want the states we come from on the rows and the states we # are going to on the columns: |f> = |i>.Umat. However, the # antihermitian parameterization of a unitary operator always # puts it the other way around: |f> = Uop|i>, ~no matter how you # choose the generator indices~. So I have to transpose here. # Flipping the sign of t does the same thing, but don't get # confused: this isn't related to the choice of variables! u = np.dot (u, linalg.expm (t).T) f, df, d2f = f_update (u) try: ci = np.tensordot(u.T, ci, 1) except ValueError as e: print (u.shape, ci.shape) raise (e) if mc.verbose >= lib.logger.DEBUG: fmt_str = ' ' + ' '.join (['{:5.2f}',]*nroots) log.debug ("{} final overlap matrix:".format (hdr)) for row in u: log.debug (fmt_str.format (*row)) if conv: log.note ("{} optimization CONVERGED".format (hdr)) else: log.note ("{} optimization did not converge after {} " "cycles".format (hdr, it)) return conv, list (ci)
class _MSPDFT (mcpdft.MultiStateMCPDFTSolver): '''MS-PDFT Extra attributes for MS-PDFT: diabatization: string The name describing the type of diabatization for I/O. Currently, only ``CMS'' is available. max_cyc_diabatize : integer Maximum cycles of the diabatization iteration. Default is 50. conv_tol_diabatize : float Convergence threshold of the diabatization algorithm. Default is 1e-8. sing_tol_diabatize : float Numerical tolerance for null state-rotation modes and singularities within the diabatization algorithm. Null modes (e.g., rotation between E1x and E1y states in a linear molecule) are ignored. Singularities (zero Hessian and non-zero gradient in the same mode) print a warning. Default is 1e-8. nudge_tol_diabatize : float Minimum step size along modes with positive curvature during the diabatization algorithm, so as to push away from saddle points and minima. Default is 1e-3. Saved results e_tot : float Weighted-average MS-PDFT final energy e_states : ndarray of shape (nroots) MS-PDFT final energies of the adiabatic states ci : list of length (nroots) of ndarrays CI vectors in the optimized diabatic basis. Related to the MC-SCF and MS-PDFT adiabat CI vectors by the expansion coefficients ``si_mcscf'' and ``si_pdft''. Either set of adiabat CI vectors can be obtained quickly via ``get_ci_adiabats'' si : ndarray of shape (nroots, nroots) Expansion coefficients for the MS-PDFT adiabats in terms of the optimized diabatic states si_pdft : ndarray of shape (nroots, nroots) Synonym of si e_mcscf : ndarray of shape (nroots) Energies of the MC-SCF adiabatic states si_mcscf : ndarray of shape (nroots, nroots) Expansion coefficients for the MC-SCF adiabats in terms of the optimized diabatic states heff_mcscf : ndarray of shape (nroots, nroots) Molecular Hamiltonian in the diabatic basis hdiag_pdft : ndarray of shape (nroots) MC-PDFT total energies of the optimized diabatic states ''' # Metaclass parent def __init__(self, mc, diabatizer, diabatize, diabatization): self.__dict__.update (mc.__dict__) keys = set (('diabatizer', 'diabatize', 'diabatization', 'heff_mcscf', 'hdiag_pdft', 'get_heff_offdiag', 'get_heff_pdft', 'si', 'si_mcscf', 'si_pdft', 'max_cyc_diabatize', 'conv_tol_diabatize', 'sing_tol_diabatize', 'nudge_tol_diabatize')) self._diabatizer = diabatizer self._diabatize = diabatize self._e_states = None self.max_cyc_diabatize = MAX_CYC_DIABATIZE self.conv_tol_diabatize = CONV_TOL_DIABATIZE self.sing_tol_diabatize = SING_TOL_DIABATIZE self.nudge_tol_diabatize = CONV_TOL_DIABATIZE self.diabatization = diabatization self.si_mcscf = None self.si_pdft = None self._keys = set (self.__dict__.keys ()).union (keys) @property def e_states (self): if self._in_mcscf_env: return self.fcisolver.e_states else: return self._e_states @e_states.setter def e_states (self, x): self._e_states = x # Unfixed to FCIsolver since MS-PDFT state energies are no longer # CI solutions @property def si (self): return self.si_pdft @si.setter def si (self, x): self.si_pdft = x def get_heff_offdiag (self): '''The off-diagonal elements of the effective Hamiltonian matrix = heff_mcscf - np.diag (heff_mcscf.diagonal ()) = ( 0 H_10^* ... ) ( H_10 0 ... ) ( ... ... ... ) Returns: heff_offdiag : ndarray of shape (nroots, nroots) Contains molecular Hamiltonian elements on the off-diagonals and zero on the diagonals ''' idx = np.diag_indices_from (self.heff_mcscf) heff_offdiag = self.heff_mcscf.copy () heff_offdiag[idx] = 0.0 return heff_offdiag def get_heff_pdft (self): '''The MS-PDFT effective Hamiltonian matrix = get_heff_offdiag () + np.diag (hdiag_pdft) = ( EPDFT_0 H_10^* ... ) ( H_10 EPDFT_1 ... ) ( ... ... ... ) Returns: heff_pdft : ndarray of shape (nroots, nroots) Contains molecular Hamiltonian elements on the off-diagonals and PDFT energies on the diagonals ''' idx = np.diag_indices_from (self.heff_mcscf) heff_pdft = self.heff_mcscf.copy () heff_pdft[idx] = self.hdiag_pdft return heff_pdft def get_ci_adiabats (self, ci=None, uci='MSPDFT'): ''' Get the CI vectors in an alternate basis (usually one of the two adiabatic bases: MCSCF or MSPDFT) Kwargs: ci : list of length nroots Diabatic ci vectors; defaults to self.ci uci : 'MSPDFT', 'MCSCF', or square array of length nroots (String indicating) unitary matrix for transforming ci vectors Returns: ci : list of length nroots CI vectors for adiabats ''' si_dict = {'MCSCF': self.si_mcscf, 'MSPDFT': self.si_pdft} if isinstance (uci, (str,np.bytes_)): if uci.upper () in si_dict: uci = si_dict[uci.upper ()] else: raise RuntimeError ("valid uci : 'MCSCF', 'MSPDFT', or ndarray") if ci is None: ci = self.ci return list (np.tensordot (uci.T, np.asarray (ci), axes=1)) get_ci_basis=get_ci_adiabats def kernel (self, mo_coeff=None, ci0=None, otxc=None, grids_level=None, grids_attr=None, **kwargs): self.otfnal.reset (mol=self.mol) # scanner mode safety if ci0 is None and isinstance (getattr (self, 'ci', None), list): ci0 = [c.copy () for c in self.ci] self.optimize_mcscf_(mo_coeff=mo_coeff, ci0=ci0) diab_conv, self.ci = self.diabatize (ci=self.ci, ci0=ci0, **kwargs) self.converged = self.converged and diab_conv self.heff_mcscf = self.make_heff_mcscf () e_mcscf, self.si_mcscf = self._eig_si (self.heff_mcscf) if abs (linalg.norm (self.e_mcscf-e_mcscf)) > 1e-9: raise RuntimeError (("Sanity fault: e_mcscf ({}) != " "self.e_mcscf ({})").format (e_mcscf, self.e_mcscf)) self.hdiag_pdft = self.compute_pdft_energy_( otxc=otxc, grids_level=grids_level, grids_attr=grids_attr)[-1] self.e_states, self.si_pdft = self._eig_si (self.get_heff_pdft ()) self.e_tot = np.dot (self.e_states, self.weights) self._log_diabats () self._log_adiabats () return (self.e_tot, self.e_ot, self.e_mcscf, self.e_cas, self.ci, self.mo_coeff, self.mo_energy) def optimize_mcscf_(self, mo_coeff=None, ci0=None, **kwargs): # Initialize in an adiabatic basis if ci0 is not None: if mo_coeff is None: mo_coeff = self.mo_coeff heff_mcscf = self.make_heff_mcscf (mo_coeff, ci0) e, self.si_mcscf = self._eig_si (heff_mcscf) ci1 = self.get_ci_adiabats (ci=ci0, uci='MCSCF') else: ci1 = None return super().optimize_mcscf_(mo_coeff=mo_coeff, ci0=ci1, **kwargs) # All of the below probably need to be wrapped over solvers in # multi-state-mix metaclass def diabatize (self, ci=None, ci0=None, **kwargs): '''Optimize the ``intermediate'' diabatic states of an MS-PDFT calculation in the space defined by the MC-SCF ``reference'' adiabatic states. Kwargs ci : list of ndarrays of length nroots CI vectors defining the model space, usually from a or CASSCF kernel call ci0 : list of ndarrays of length nroots Initial guess for optimized diabatic CI vectors, possibly spanning a slightly different space, usually at a different geometry (i.e., during a geometry optimization or dynamics trajectory) Returns conv : logical Reports whether the diabatization algorithm converged successfully ci : list of ndarrays of length = nroots CI vectors of the optimized diabatic states ''' if ci is None: ci = self.ci if ci0 is not None: ovlp = np.tensordot (np.asarray (ci).conj (), np.asarray (ci0), axes=((1,2),(1,2))) u, svals, vh = linalg.svd (ovlp) ci = self.get_ci_basis (ci=ci, uci=np.dot (u,vh)) return self._diabatize (self, ci=ci, **kwargs) def diabatizer (self, mo_coeff=None, ci=None): '''Computes the value, gradient vector, and Hessian matrix with respect to pairwise state rotations of the objective function maximized by the optimized diabatic (``intermediate'') states. Kwargs mo_coeff : ndarray of shape (nao,nmo) MO coefficients ci : list of ndarrays of length nroots CI vectors Returns: f : float Objective function value for states df : ndarray of shape (npairs = nroots*(nroots-1)/2) Gradient vector of objective function with respect to pairwise rotations between states d2f : ndarray of shape (npairs,npairs) Hessian matrix of objective function with respect to pairwise rotations between states f_update : callable Takes a unitary matrix and returns f, df, and d2f as above. Some kinds of MS-PDFT can be sped up using intermediates. If so, the _diabatizer function can provide f_update. Otherwise, it defaults to running _diabatizer itself on a rotated set of CI vectors. ''' if mo_coeff is None: mo_coeff = self.mo_coeff if ci is None: ci = self.ci rets = self._diabatizer (self, mo_coeff=mo_coeff, ci=ci) f, df, d2f = rets[:3] if len (rets) > 3 and callable (rets[3]): f_update = rets[3] else: def f_update (u=1): ci1 = self.get_ci_basis(ci=ci, uci=u) f1, df1, d2f1 = self._diabatizer ( self, mo_coeff=mo_coeff, ci=ci1) return f1, df1, d2f1 return f, df, d2f, f_update def _eig_si (self, heff): return linalg.eigh (heff) make_heff_mcscf = make_heff_mcscf def _log_diabats (self): # Information about the intermediate states hdiag_mcscf = self.heff_mcscf.diagonal () hdiag_pdft = self.hdiag_pdft nroots = len (hdiag_pdft) log = lib.logger.new_logger (self, self.verbose) f, df, d2f = self.diabatizer ()[:3] hdr = '{} diabatic (intermediate)'.format (self.__class__.__name__) log.note ('%s objective function value = %.15g |grad| = %.7g', hdr, f, linalg.norm (df)) log.note ('%s average energy EPDFT = %.15g EMCSCF = %.15g', hdr, np.dot (self.weights, hdiag_pdft), np.dot (self.weights, hdiag_mcscf)) log.note ('%s states:', hdr) if getattr (self.fcisolver, 'spin_square', None): ss = self.fcisolver.states_spin_square (self.ci, self.ncas, self.nelecas)[0] for i in range (nroots): log.note (' State %d EPDFT = %.15g EMCSCF = %.15g' ' S^2 = %.7f', i, hdiag_pdft[i], hdiag_mcscf[i], ss[i]) else: for i in range (nroots): log.note (' State %d EPDFT = %.15g EMCSCF = ' '%.15g', i, hdiag_pdft[i], hdiag_mcscf[i]) log.info ('MS-PDFT effective Hamiltonian matrix in diabatic basis:') fmt_str = ' '.join (['{:9.5f}',]*nroots) for row in self.get_heff_pdft(): log.info (fmt_str.format (*row)) log.info ('Diabatic states (columns) in terms of reference states ' '(rows):') for row in self.si_mcscf.T: log.info (fmt_str.format (*row)) def _log_adiabats (self): # Information about the final states log = lib.logger.new_logger (self, self.verbose) nroots = len (self.e_states) log.note ('%s adiabatic (final) states:', self.__class__.__name__) if getattr (self.fcisolver, 'spin_square', None): ci = np.tensordot (self.si.T, np.asarray (self.ci), axes=1) ss = self.fcisolver.states_spin_square (ci, self.ncas, self.nelecas)[0] for i in range (nroots): log.note (' State %d weight %g EMSPDFT = %.15g S^2 = %.7f', i, self.weights[i], self.e_states[i], ss[i]) else: for i in range (nroots): log.note (' State %d weight %g EMSPDFT = %.15g', i, self.weights[i], self.e_states[i]) def nuc_grad_method (self): if not isinstance (self, mc1step.CASSCF): raise NotImplementedError ("CASCI-based PDFT nuclear gradients") elif getattr (self, 'frozen', None) is not None: raise NotImplementedError ("PDFT nuclear gradients with frozen orbitals") elif isinstance (self, _DFCASSCF): from pyscf.df.grad.mspdft import Gradients else: from pyscf.grad.mspdft import Gradients return Gradients (self) def nac_method(self): if not isinstance(self, mc1step.CASSCF): raise NotImplementedError("CASCI-based PDFT NACs") elif getattr(self, 'frozen', None) is not None: raise NotImplementedError("PDFT NACs with frozen orbitals") elif isinstance(self, _DFCASSCF): raise NotImplementedError("PDFT NACs with density fitting") else: from pyscf.nac.mspdft import NonAdiabaticCouplings return NonAdiabaticCouplings(self) def dip_moment (self, unit='Debye', origin='Coord_Center', state=None): if not isinstance (self, mc1step.CASSCF): raise NotImplementedError ("CASCI-based PDFT dipole moments") elif getattr (self, 'frozen', None) is not None: raise NotImplementedError ("PDFT dipole moments with frozen orbitals") elif isinstance (self, _DFCASSCF): raise NotImplementedError ("PDFT dipole moments with density-fitting ERIs") # Monkeypatch for double prop folders # TODO: more elegant solution import os mypath = os.path.dirname (os.path.dirname (os.path.abspath (__file__))) myproppath = os.path.join (mypath, 'prop') # suppress irrelevant warnings when 'properties' ext mod installed import warnings with warnings.catch_warnings (): warnings.filterwarnings ( "ignore", message="Module.*is under testing") from pyscf import prop prop.__path__.append (myproppath) prop.__path__=list(set(prop.__path__)) from pyscf.prop.dip_moment.mspdft import ElectricDipole if not lib.isinteger (state): raise RuntimeError ('Permanent dipole requires a single state') dip_obj = ElectricDipole(self) mol_dipole = dip_obj.kernel (state=state, unit=unit, origin=origin) return mol_dipole def trans_moment (self, unit='Debye', origin='Coord_Center', state=None): if not isinstance (self, mc1step.CASSCF): raise NotImplementedError ("CASCI-based PDFT dipole moments") elif getattr (self, 'frozen', None) is not None: raise NotImplementedError ("PDFT dipole moments with frozen orbitals") elif isinstance (self, _DFCASSCF): raise NotImplementedError ("PDFT dipole moments with density-fitting ERIs") # Monkeypatch for double prop folders # TODO: more elegant solution import os mypath = os.path.dirname (os.path.dirname (os.path.abspath (__file__))) myproppath = os.path.join (mypath, 'prop') # suppress irrelevant warnings when 'properties' ext mod installed import warnings with warnings.catch_warnings (): warnings.filterwarnings ( "ignore", message="Module.*is under testing") from pyscf import prop prop.__path__.append (myproppath) prop.__path__=list(set(prop.__path__)) from pyscf.prop.trans_dip_moment.mspdft import TransitionDipole if not hasattr(state, '__len__') or len(state) !=2: raise RuntimeError ('Transition dipole requires two states') tran_dip_obj = TransitionDipole(self) mol_trans_dipole = tran_dip_obj.kernel (state=state, unit=unit, origin=origin) return mol_trans_dipole
[docs] def get_diabfns (obj): '''Interpret the name of the MS-PDFT method as a pair of functions which optimize the intermediate states and calculate the power series in the corresponding objective function to second order. Args: obj : string Specify particular MS-PDFT method. Currently, only "CMS" is supported. Not case-sensitive. Returns: diabatizer : callable Takes model-space CI vectors in a trial intermediate-state basis and returns the value and first and second derivatives of the objective function specified by obj diabatize : callable Takes model-space CI vectors and returns CI vectors in the optimized intermediate-state basis ''' if obj.upper () == 'CMS': from pyscf.mcpdft.cmspdft import e_coul as diabatizer diabatize = si_newton elif obj.upper() == "XMS": from pyscf.mcpdft.xmspdft import safock_energy as diabatizer from pyscf.mcpdft.xmspdft import solve_safock as diabatize else: raise RuntimeError ('MS-PDFT type not supported') return diabatizer, diabatize
[docs] def multi_state (mc, weights=(0.5,0.5), diabatization='CMS', **kwargs): ''' Build multi-state MC-PDFT method object Args: mc : instance of class _PDFT Kwargs: weights : sequence of floats diabatization : objective-function type Currently supports only 'cms' Returns: si : instance of class _MSPDFT ''' if isinstance (mc, mcpdft.MultiStateMCPDFTSolver): raise RuntimeError ('already a multi-state PDFT solver') if isinstance (mc.fcisolver, StateAverageMixFCISolver): raise RuntimeError ('state-average mix type') if not isinstance (mc, StateAverageMCSCFSolver): base_name = mc.__class__.__name__ mc = mc.state_average (weights=weights, **kwargs) else: base_name = mc.__class__.__bases__[0].__name__ mcbase_class = mc.__class__ diabatizer, diabatize = get_diabfns (diabatization) class MSPDFT (_MSPDFT, mcbase_class): pass MSPDFT.__name__ = diabatization.upper () + base_name return MSPDFT (mc, diabatizer, diabatize, diabatization)
if __name__ == '__main__': # This ^ is a convenient way to debug code that you are working on. The # code in this block will only execute if you run this python script as the # input directly: "python mspdft.py". from pyscf import scf, gto from pyscf.tools import molden # My version is better for MC-SCF from pyscf.fci import csf_solver xyz = '''O 0.00000000 0.08111156 0.00000000 H 0.78620605 0.66349738 0.00000000 H -0.78620605 0.66349738 0.00000000''' mol = gto.M (atom=xyz, basis='sto-3g', symmetry=False, output='mspdft.log', verbose=lib.logger.DEBUG) mf = scf.RHF (mol).run () mc = mcpdft.CASSCF (mf, 'tPBE', 4, 4).set (fcisolver = csf_solver (mol, 1)) mc = mc.multi_state ([1.0/3,]*3, 'cms').run ()