def get_qm_atoms_opts(mol): """Provides list of coordinates of quantum mechanical atoms from psi4.core.Molecule `mol` to pylibefp.core.efp() `efpobj`. Also converts from `read_options("EFP"` to pylibefp opts dictionary. """ efpobj = mol.EFP ptc = [] coords = [] for iat in range(mol.natom()): ptc.append(mol.charge(iat)) coords.append(mol.x(iat)) coords.append(mol.y(iat)) coords.append(mol.z(iat)) # set options # * 'chtr', 'qm_exch', 'qm_disp', 'qm_chtr' may be enabled in a future libefp release opts = {} for opt in ['elst', 'exch', 'ind', 'disp', 'elst_damping', 'ind_damping', 'disp_damping']: psiopt = 'EFP_' + opt.upper() if core.has_option_changed('EFP', psiopt): opts[opt] = core.get_option('EFP', psiopt) for opt in ['elst', 'ind']: psiopt = 'EFP_QM_' + opt.upper() if core.has_option_changed('EFP', psiopt): opts['qm_' + opt] = core.get_option('EFP', psiopt) return ptc, coords, opts
def get_qm_atoms_opts(mol): """Provides list of coordinates of quantum mechanical atoms from psi4.core.Molecule `mol` to pylibefp.core.efp() `efpobj`. Also converts from `read_options("EFP"` to pylibefp opts dictionary. """ efpobj = mol.EFP ptc = [] coords = [] for iat in range(mol.natom()): ptc.append(mol.charge(iat)) coords.append(mol.x(iat)) coords.append(mol.y(iat)) coords.append(mol.z(iat)) # set options # * 'chtr', 'qm_exch', 'qm_disp', 'qm_chtr' may be enabled in a future libefp release opts = {} for opt in [ 'elst', 'exch', 'ind', 'disp', 'elst_damping', 'ind_damping', 'disp_damping' ]: psiopt = 'EFP_' + opt.upper() if core.has_option_changed('EFP', psiopt): opts[opt] = core.get_option('EFP', psiopt) for opt in ['elst', 'ind']: psiopt = 'EFP_QM_' + opt.upper() if core.has_option_changed('EFP', psiopt): opts['qm_' + opt] = core.get_option('EFP', psiopt) return ptc, coords, opts
def negotiate_convergence_criterion(dermode: Union[Tuple[str, str], Tuple[int, int]], method: str, return_optstash: bool = False): r""" This function will set local SCF and global energy convergence criterion to the defaults listed at: http://www.psicode.org/psi4manual/master/scf.html#convergence-and- algorithm-defaults. SCF will be converged more tightly (pscf_Ec and pscf_Dc) if a post-SCF method is selected. For a final SCF method, the looser (scf_Ec and scf_Dc) convergence criterion will be used. dermode - Tuple of target and means derivative level or ("prop", "prop"). E.g., analytic gradient is (1, 1); frequency by energies is (2, 0). Nearly always test on procedures['energy'] since that's guaranteed to exist for a method. method - Name of the method scf_Ec - E convergence criterion for scf target method pscf_Ec - E convergence criterion for scf of post-scf target method scf_Dc - D convergence criterion for scf target method pscf_Dc - D convergence criterion for scf of post-scf target method gen_Ec - E convergence criterion for post-scf target method """ scf_Ec, pscf_Ec, scf_Dc, pscf_Dc, gen_Ec = { (0, 0): [6, 8, 6, 8, 6], (1, 0): [8, 10, 8, 10, 8], (2, 0): [10, 11, 10, 11, 10], (1, 1): [8, 10, 8, 10, 8], (2, 1): [8, 10, 8, 10, 8], (2, 2): [8, 10, 8, 10, 8], ("prop", "prop"): [6, 10, 6, 10, 8] }[dermode] # Set method-dependent scf convergence criteria, check against energy routines # Set post-scf convergence criteria (global will cover all correlated modules) cc = {} if procedures['energy'][method] in [proc.run_scf, proc.run_tdscf_energy]: if not core.has_option_changed('SCF', 'E_CONVERGENCE'): cc['SCF__E_CONVERGENCE'] = math.pow(10, -scf_Ec) if not core.has_option_changed('SCF', 'D_CONVERGENCE'): cc['SCF__D_CONVERGENCE'] = math.pow(10, -scf_Dc) else: if not core.has_option_changed('SCF', 'E_CONVERGENCE'): cc['SCF__E_CONVERGENCE'] = math.pow(10, -pscf_Ec) if not core.has_option_changed('SCF', 'D_CONVERGENCE'): cc['SCF__D_CONVERGENCE'] = math.pow(10, -pscf_Dc) if not core.has_global_option_changed('E_CONVERGENCE'): cc['E_CONVERGENCE'] = math.pow(10, -gen_Ec) if return_optstash: optstash = p4util.OptionsState(['SCF', 'E_CONVERGENCE'], ['SCF', 'D_CONVERGENCE'], ['E_CONVERGENCE']) p4util.set_options(cc) return optstash else: return cc
def scf_set_reference_local(name): """ Figures out the correct SCF reference to set locally """ optstash = p4util.OptionsState( ['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') if name == 'hf': if core.get_option('SCF','REFERENCE') == 'RKS': core.set_local_option('SCF','REFERENCE','RHF') elif core.get_option('SCF','REFERENCE') == 'UKS': core.set_local_option('SCF','REFERENCE','UHF') elif name == 'scf': if core.get_option('SCF','REFERENCE') == 'RKS': if (len(core.get_option('SCF', 'DFT_FUNCTIONAL')) > 0) or core.get_option('SCF', 'DFT_CUSTOM_FUNCTIONAL') is not None: pass else: core.set_local_option('SCF','REFERENCE','RHF') elif core.get_option('SCF','REFERENCE') == 'UKS': if (len(core.get_option('SCF', 'DFT_FUNCTIONAL')) > 0) or core.get_option('SCF', 'DFT_CUSTOM_FUNCTIONAL') is not None: pass else: core.set_local_option('SCF','REFERENCE','UHF') return optstash
def scf_set_reference_local(name, is_dft=False): """ Figures out the correct SCF reference to set locally """ optstash = p4util.OptionsState( ['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') # Alter reference name if needed user_ref = core.get_option('SCF', 'REFERENCE') if (name not in dft_funcs.superfunctional_noxc_names) or (is_dft): if (user_ref == 'RHF'): core.set_local_option('SCF', 'REFERENCE', 'RKS') elif (user_ref == 'UHF'): core.set_local_option('SCF', 'REFERENCE', 'UKS') elif (user_ref == 'ROHF'): raise ValidationError('ROHF reference for DFT is not available.') elif (user_ref == 'CUHF'): raise ValidationError('CUHF reference for DFT is not available.') # else we are doing HF and nothing needs to be overloaded return optstash
def dft_set_reference_local(name): """ Figures out the correct DFT reference to set locally """ optstash = p4util.OptionsState(['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'REFERENCE'], ['SCF', 'SCF_TYPE'], ['DF_BASIS_MP2'], ['DFMP2', 'MP2_OS_SCALE'], ['DFMP2', 'MP2_SS_SCALE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.set_local_option('SCF', 'DFT_FUNCTIONAL', name) user_ref = core.get_option('SCF', 'REFERENCE') if (user_ref == 'RHF'): core.set_local_option('SCF', 'REFERENCE', 'RKS') elif (user_ref == 'UHF'): core.set_local_option('SCF', 'REFERENCE', 'UKS') elif (user_ref == 'ROHF'): raise ValidationError('ROHF reference for DFT is not available.') elif (user_ref == 'CUHF'): raise ValidationError('CUHF reference for DFT is not available.') return optstash
def scf_set_reference_local(name): """ Figures out the correct SCF reference to set locally """ optstash = p4util.OptionsState(['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') if name == 'hf': if core.get_option('SCF', 'REFERENCE') == 'RKS': core.set_local_option('SCF', 'REFERENCE', 'RHF') elif core.get_option('SCF', 'REFERENCE') == 'UKS': core.set_local_option('SCF', 'REFERENCE', 'UHF') elif name == 'scf': if core.get_option('SCF', 'REFERENCE') == 'RKS': if (len(core.get_option( 'SCF', 'DFT_FUNCTIONAL')) > 0) or core.get_option( 'SCF', 'DFT_CUSTOM_FUNCTIONAL') is not None: pass else: core.set_local_option('SCF', 'REFERENCE', 'RHF') elif core.get_option('SCF', 'REFERENCE') == 'UKS': if (len(core.get_option( 'SCF', 'DFT_FUNCTIONAL')) > 0) or core.get_option( 'SCF', 'DFT_CUSTOM_FUNCTIONAL') is not None: pass else: core.set_local_option('SCF', 'REFERENCE', 'UHF') return optstash
def scf_set_reference_local(name, is_dft=False): """ Figures out the correct SCF reference to set locally """ optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') # Alter reference name if needed user_ref = core.get_option('SCF', 'REFERENCE') sup = build_superfunctional_from_dictionary(functionals[name], 1, 1, True)[0] if sup.needs_xc() or is_dft: if (user_ref == 'RHF'): core.set_local_option('SCF', 'REFERENCE', 'RKS') elif (user_ref == 'UHF'): core.set_local_option('SCF', 'REFERENCE', 'UKS') elif (user_ref == 'ROHF'): raise ValidationError('ROHF reference for DFT is not available.') elif (user_ref == 'CUHF'): raise ValidationError('CUHF reference for DFT is not available.') # else we are doing HF and nothing needs to be overloaded return optstash
def dft_set_reference_local(name): """ Figures out the correct DFT reference to set locally """ optstash = p4util.OptionsState( ['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'REFERENCE'], ['SCF', 'SCF_TYPE'], ['DF_BASIS_MP2'], ['DFMP2', 'MP2_OS_SCALE'], ['DFMP2', 'MP2_SS_SCALE']) # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.set_local_option('SCF', 'DFT_FUNCTIONAL', name) user_ref = core.get_option('SCF', 'REFERENCE') if (user_ref == 'RHF'): core.set_local_option('SCF', 'REFERENCE', 'RKS') elif (user_ref == 'UHF'): core.set_local_option('SCF', 'REFERENCE', 'UKS') elif (user_ref == 'ROHF'): raise ValidationError('ROHF reference for DFT is not available.') elif (user_ref == 'CUHF'): raise ValidationError('CUHF reference for DFT is not available.') return optstash
def prep_findif(mol, irrep, mode, gradient=None): findif_stencil_size = core.get_option("FINDIF", "POINTS") findif_step_size = core.get_option("FINDIF", "DISP_SIZE") translations_projection_sound = (not core.get_option('SCF', 'EXTERN') and not core.get_option('SCF', 'PERTURB_H') and not hasattr(mol, 'EFP')) if gradient is not None: stationary_criterion = 1.e-2 # pulled out of a hat stationary_point = _rms(gradient) < stationary_criterion rotations_projection_sound = translations_projection_sound and stationary_point core.print_out( '\n Based on options and gradient (rms={:.2E}), recommend {}projecting translations and {}projecting rotations.\n' .format(_rms(gradient), '' if translations_projection_sound else 'not ', '' if rotations_projection_sound else 'not ')) else: stationary_point = False # unknown, so F to be safe rotations_projection_sound_grad = translations_projection_sound rotations_projection_sound_hess = translations_projection_sound and stationary_point if core.has_option_changed('FINDIF', 'FD_PROJECT'): r_project_grad = core.get_option('FINDIF', 'FD_PROJECT') r_project_hess = core.get_option('FINDIF', 'FD_PROJECT') else: r_project_grad = rotations_projection_sound_grad r_project_hess = rotations_projection_sound_hess if mode == "1_0": fdargs = { "stencil_size": findif_stencil_size, "step_size": findif_step_size, "t_project": translations_projection_sound, "r_project": r_project_grad, } elif mode == "2_1": fdargs = { "freq_irrep_only": irrep, "stencil_size": findif_stencil_size, "step_size": findif_step_size, "t_project": translations_projection_sound, "r_project": r_project_hess, } elif mode == "2_0": fdargs = { "freq_irrep_only": irrep, "stencil_size": findif_stencil_size, "step_size": findif_step_size, "t_project": translations_projection_sound, "r_project": r_project_hess, } return fdargs
def prepare_options_for_modules(changedOnly=False, commandsInsteadDict=False): """Function to return a string of commands to replicate the current state of user-modified options. Used to capture C++ options information for distributed (sow/reap) input files. .. caution:: Some features are not yet implemented. Buy a developer a coffee. - Need some option to get either all or changed - Need some option to either get dict or set string or psimod command list - command return doesn't revoke has_changed setting for unchanged with changedOnly=False """ options = collections.defaultdict(dict) commands = '' for opt in core.get_global_option_list(): if core.has_global_option_changed(opt) or not changedOnly: if opt in ['DFT_CUSTOM_FUNCTIONAL', 'EXTERN']: # Feb 2017 hack continue val = core.get_global_option(opt) options['GLOBALS'][opt] = { 'value': val, 'has_changed': core.has_global_option_changed(opt) } if isinstance(val, str): commands += """core.set_global_option('%s', '%s')\n""" % (opt, val) else: commands += """core.set_global_option('%s', %s)\n""" % (opt, val) #if changedOnly: # print('Appending module %s option %s value %s has_changed %s.' % \ # ('GLOBALS', opt, core.get_global_option(opt), core.has_global_option_changed(opt))) for module in _modules: if core.option_exists_in_module(module, opt): hoc = core.has_option_changed(module, opt) if hoc or not changedOnly: val = core.get_option(module, opt) options[module][opt] = {'value': val, 'has_changed': hoc} if isinstance(val, str): commands += """core.set_local_option('%s', '%s', '%s')\n""" % ( module, opt, val) else: commands += """core.set_local_option('%s', '%s', %s)\n""" % ( module, opt, val) #if changedOnly: # print('Appending module %s option %s value %s has_changed %s.' % \ # (module, opt, core.get_option(module, opt), hoc)) if commandsInsteadDict: return commands else: return options
def prepare_options_for_modules(changedOnly=False, commandsInsteadDict=False): """Function to return a string of commands to replicate the current state of user-modified options. Used to capture C++ options information for distributed (sow/reap) input files. .. caution:: Some features are not yet implemented. Buy a developer a coffee. - Need some option to get either all or changed - Need some option to either get dict or set string or psimod command list - command return doesn't revoke has_changed setting for unchanged with changedOnly=False """ options = collections.defaultdict(dict) commands = '' for opt in core.get_global_option_list(): if core.has_global_option_changed(opt) or not changedOnly: if opt in ['DFT_CUSTOM_FUNCTIONAL', 'EXTERN']: # Feb 2017 hack continue val = core.get_global_option(opt) options['GLOBALS'][opt] = {'value': val, 'has_changed': core.has_global_option_changed(opt)} if isinstance(val, basestring): commands += """core.set_global_option('%s', '%s')\n""" % (opt, val) else: commands += """core.set_global_option('%s', %s)\n""" % (opt, val) #if changedOnly: # print('Appending module %s option %s value %s has_changed %s.' % \ # ('GLOBALS', opt, core.get_global_option(opt), core.has_global_option_changed(opt))) for module in _modules: if core.option_exists_in_module(module, opt): hoc = core.has_option_changed(module, opt) if hoc or not changedOnly: val = core.get_option(module, opt) options[module][opt] = {'value': val, 'has_changed': hoc} if isinstance(val, str): commands += """core.set_local_option('%s', '%s', '%s')\n""" % (module, opt, val) else: commands += """core.set_local_option('%s', '%s', %s)\n""" % (module, opt, val) #if changedOnly: # print('Appending module %s option %s value %s has_changed %s.' % \ # (module, opt, core.get_option(module, opt), hoc)) if commandsInsteadDict: return commands else: return options
def __init__(self, option, module=None): self.option = option.upper() if module: self.module = module.upper() else: self.module = None self.value_global = core.get_global_option(option) self.haschanged_global = core.has_global_option_changed(option) if self.module: self.value_local = core.get_local_option(self.module, option) self.haschanged_local = core.has_local_option_changed(self.module, option) self.value_used = core.get_option(self.module, option) self.haschanged_used = core.has_option_changed(self.module, option) else: self.value_local = None self.haschanged_local = None self.value_used = None self.haschanged_used = None
def __init__(self, option: str, module: Optional[str] = None): self.option = option.upper() if module: self.module = module.upper() else: self.module = None self.value_global = core.get_global_option(option) self.haschanged_global = core.has_global_option_changed(option) if self.module: self.value_local = core.get_local_option(self.module, option) self.haschanged_local = core.has_local_option_changed(self.module, option) self.value_used = core.get_option(self.module, option) self.haschanged_used = core.has_option_changed(self.module, option) else: self.value_local = None self.haschanged_local = None self.value_used = None self.haschanged_used = None
def build_superfunctional(name, restricted): npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS") deriv = 1 # Default depth for now # We are a XC generating function if hasattr(name, '__call__'): custom_error = "SCF: Custom functional type must either be a SuperFunctional or a tuple of (SuperFunctional, (base_name, dashparam))." sfunc = name("name", npoints, deriv, restricted) # Without Dispersion if isinstance(sfunc, core.SuperFunctional): sup = (sfunc, False) # With Dispersion elif isinstance(sup[0], core.SuperFunctional): sup = sfunc # Can we validate dispersion? else: raise ValidationError(custom_error) # Double check that the SuperFunctional is correctly sized (why dont we always do this?) sup[0].set_max_points(npoints) sup[0].set_deriv(deriv) sup[0].allocate() # Check for dict-based functionals elif name.upper() in dict_builder.functionals.keys(): sup = dict_builder.build_superfunctional_from_dictionary( name.upper(), npoints, deriv, restricted) else: raise ValidationError("SCF: Functional (%s) not found!" % name) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()): raise ValidationError( "INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping." ) # Lock and unlock the functional sup[0].set_lock(False) # Set options if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc(): omega = core.get_option("SCF", "DFT_OMEGA") sup[0].set_x_omega(omega) # We also need to loop through all of the exchange functionals if sup[0].is_libxc_func(): # Full libxc funcs are dropped in c_functionals (smooth move!) sup[0].c_functionals()[0].set_omega(omega) else: for x_func in sup[0].x_functionals(): x_func.set_omega(omega) if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc(): sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C")) if core.has_option_changed("SCF", "DFT_ALPHA"): sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA")) if core.has_option_changed("SCF", "DFT_ALPHA_C"): sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C")) # Check SCF_TYPE if sup[0].is_x_lrc() and (core.get_option("SCF", "SCF_TYPE") not in ["DIRECT", "DF", "OUT_OF_CORE", "PK"]): raise ValidationError( "SCF: SCF_TYPE (%s) not supported for range-separated functionals." % core.get_option("SCF", "SCF_TYPE")) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc()): raise ValidationError( 'INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.' ) sup[0].set_lock(True) return sup
def build_superfunctional(name, restricted, npoints=None, deriv=1): if npoints is None: npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS") # We are a XC generating function if hasattr(name, '__call__'): custom_error = "SCF: Custom functional type must either be a SuperFunctional or a tuple of (SuperFunctional, (base_name, dashparam))." sfunc = name("name", npoints, deriv, restricted) # Without Dispersion if isinstance(sfunc, core.SuperFunctional): sup = (sfunc, False) # With Dispersion elif isinstance(sfunc[0], core.SuperFunctional): sup = sfunc # Can we validate dispersion? else: raise ValidationError(custom_error) # Double check that the SuperFunctional is correctly sized (why dont we always do this?) sup[0].set_max_points(npoints) sup[0].set_deriv(deriv) sup[0].allocate() # Check for supplied dict_func functionals elif isinstance(name, dict): sup = dft_builder.build_superfunctional_from_dictionary( name, npoints, deriv, restricted) # Check for pre-defined dict-based functionals elif name.lower() in dft_builder.functionals: sup = dft_builder.build_superfunctional_from_dictionary( dft_builder.functionals[name.lower()], npoints, deriv, restricted) else: raise ValidationError("SCF: Functional (%s) not found!" % name) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()): raise ValidationError( "INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping." ) # Lock and unlock the functional sup[0].set_lock(False) # Set options if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc(): omega = core.get_option("SCF", "DFT_OMEGA") sup[0].set_x_omega(omega) # We also need to loop through all of the exchange functionals if sup[0].is_libxc_func(): # Full libxc funcs are dropped in c_functionals (smooth move!) sup[0].c_functionals()[0].set_omega(omega) else: for x_func in sup[0].x_functionals(): x_func.set_omega(omega) if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc(): sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C")) if core.has_option_changed("SCF", "DFT_ALPHA"): sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA")) if core.has_option_changed("SCF", "DFT_ALPHA_C"): sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C")) # add VV10 correlation to any functional or modify existing # custom procedures using name 'scf' without any quadrature grid like HF will fail and are not detected if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS") and sup[0].vv10_b() > 0.0): if not isinstance(name, dict): if (name.lower() == 'hf'): raise ValidationError("SCF: HF with -NL not implemented") nl_tuple = core.get_option("SCF", "NL_DISPERSION_PARAMETERS") sup[0].set_vv10_b(nl_tuple[0]) if len(nl_tuple) > 1: sup[0].set_vv10_c(nl_tuple[1]) if len(nl_tuple) > 2: raise ValidationError( "too many entries in NL_DISPERSION_PARAMETERS for DFT-NL") elif core.has_option_changed("SCF", "DFT_VV10_B"): if not isinstance(name, dict): if (name.lower() == 'hf'): raise ValidationError("SCF: HF with -NL not implemented") vv10_b = core.get_option("SCF", "DFT_VV10_B") sup[0].set_vv10_b(vv10_b) if core.has_option_changed("SCF", "DFT_VV10_C"): vv10_c = core.get_option("SCF", "DFT_VV10_C") sup[0].set_vv10_c(vv10_c) if (abs(sup[0].vv10_c() - 0.0) <= 1e-8): core.print_out( "SCF: VV10_C not specified. Using default (C=0.0093)!") sup[0].set_vv10_c(0.0093) if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS") and core.has_option_changed("SCF", "DFT_VV10_B")): raise ValidationError( "SCF: Decide between NL_DISPERSION_PARAMETERS and DFT_VV10_B !!") # Check SCF_TYPE if sup[0].is_x_lrc() and (core.get_global_option("SCF_TYPE") not in [ "DISK_DF", "MEM_DF", "DIRECT", "DF", "OUT_OF_CORE", "PK" ]): raise ValidationError( "SCF: SCF_TYPE (%s) not supported for range-separated functionals, plese use SCF_TYPE = 'DF' to automatically select the correct JK build." % core.get_global_option("SCF_TYPE")) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc()): raise ValidationError( 'INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.' ) sup[0].set_lock(True) return sup
def _set_convergence_criterion(ptype, method_name, scf_Ec, pscf_Ec, scf_Dc, pscf_Dc, gen_Ec, verbose=1): r""" This function will set local SCF and global energy convergence criterion to the defaults listed at: http://www.psicode.org/psi4manual/master/scf.html#convergence-and- algorithm-defaults. SCF will be converged more tightly if a post-SCF method is select (pscf_Ec, and pscf_Dc) else the looser (scf_Ec, and scf_Dc convergence criterion will be used). ptype - Procedure type (energy, gradient, etc). Nearly always test on procedures['energy'] since that's guaranteed to exist for a method. method_name - Name of the method scf_Ec - E convergence criterion for scf target method pscf_Ec - E convergence criterion for scf of post-scf target method scf_Dc - D convergence criterion for scf target method pscf_Dc - D convergence criterion for scf of post-scf target method gen_Ec - E convergence criterion for post-scf target method """ optstash = p4util.OptionsState( ['SCF', 'E_CONVERGENCE'], ['SCF', 'D_CONVERGENCE'], ['E_CONVERGENCE']) # Kind of want to move this out of here _method_exists(ptype, method_name) if verbose >= 2: print(' Setting convergence', end=' ') # Set method-dependent scf convergence criteria, check against energy routines if not core.has_option_changed('SCF', 'E_CONVERGENCE'): if procedures['energy'][method_name] == proc.run_scf: core.set_local_option('SCF', 'E_CONVERGENCE', scf_Ec) if verbose >= 2: print(scf_Ec, end=' ') else: core.set_local_option('SCF', 'E_CONVERGENCE', pscf_Ec) if verbose >= 2: print(pscf_Ec, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'E_CONVERGENCE'), end=' ') if not core.has_option_changed('SCF', 'D_CONVERGENCE'): if procedures['energy'][method_name] == proc.run_scf: core.set_local_option('SCF', 'D_CONVERGENCE', scf_Dc) if verbose >= 2: print(scf_Dc, end=' ') else: core.set_local_option('SCF', 'D_CONVERGENCE', pscf_Dc) if verbose >= 2: print(pscf_Dc, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'D_CONVERGENCE'), end=' ') # Set post-scf convergence criteria (global will cover all correlated modules) if not core.has_global_option_changed('E_CONVERGENCE'): if procedures['energy'][method_name] != proc.run_scf: core.set_global_option('E_CONVERGENCE', gen_Ec) if verbose >= 2: print(gen_Ec, end=' ') else: if procedures['energy'][method_name] != proc.run_scf: if verbose >= 2: print('CUSTOM', core.get_global_option('E_CONVERGENCE'), end=' ') if verbose >= 2: print('') return optstash
def _set_convergence_criterion(ptype, method_name, scf_Ec, pscf_Ec, scf_Dc, pscf_Dc, gen_Ec, verbose=1): r""" This function will set local SCF and global energy convergence criterion to the defaults listed at: http://www.psicode.org/psi4manual/master/scf.html#convergence-and- algorithm-defaults. SCF will be converged more tightly if a post-SCF method is select (pscf_Ec, and pscf_Dc) else the looser (scf_Ec, and scf_Dc convergence criterion will be used). ptype - Procedure type (energy, gradient, etc). Nearly always test on procedures['energy'] since that's guaranteed to exist for a method. method_name - Name of the method scf_Ec - E convergence criterion for scf target method pscf_Ec - E convergence criterion for scf of post-scf target method scf_Dc - D convergence criterion for scf target method pscf_Dc - D convergence criterion for scf of post-scf target method gen_Ec - E convergence criterion for post-scf target method """ optstash = p4util.OptionsState(['SCF', 'E_CONVERGENCE'], ['SCF', 'D_CONVERGENCE'], ['E_CONVERGENCE']) # Kind of want to move this out of here _method_exists(ptype, method_name) if verbose >= 2: print(' Setting convergence', end=' ') # Set method-dependent scf convergence criteria, check against energy routines if not core.has_option_changed('SCF', 'E_CONVERGENCE'): if procedures['energy'][method_name] == proc.run_scf: core.set_local_option('SCF', 'E_CONVERGENCE', scf_Ec) if verbose >= 2: print(scf_Ec, end=' ') else: core.set_local_option('SCF', 'E_CONVERGENCE', pscf_Ec) if verbose >= 2: print(pscf_Ec, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'E_CONVERGENCE'), end=' ') if not core.has_option_changed('SCF', 'D_CONVERGENCE'): if procedures['energy'][method_name] == proc.run_scf: core.set_local_option('SCF', 'D_CONVERGENCE', scf_Dc) if verbose >= 2: print(scf_Dc, end=' ') else: core.set_local_option('SCF', 'D_CONVERGENCE', pscf_Dc) if verbose >= 2: print(pscf_Dc, end=' ') else: if verbose >= 2: print('CUSTOM', core.get_option('SCF', 'D_CONVERGENCE'), end=' ') # Set post-scf convergence criteria (global will cover all correlated modules) if not core.has_global_option_changed('E_CONVERGENCE'): if procedures['energy'][method_name] != proc.run_scf: core.set_global_option('E_CONVERGENCE', gen_Ec) if verbose >= 2: print(gen_Ec, end=' ') else: if procedures['energy'][method_name] != proc.run_scf: if verbose >= 2: print('CUSTOM', core.get_global_option('E_CONVERGENCE'), end=' ') if verbose >= 2: print('') return optstash
def scf_iterate(self, e_conv=None, d_conv=None): is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF') verbose = core.get_option('SCF', "PRINT") reference = core.get_option('SCF', "REFERENCE") # self.member_data_ signals are non-local, used internally by c-side fns self.diis_enabled_ = self.validate_diis() self.MOM_excited_ = _validate_MOM() self.diis_start_ = core.get_option('SCF', 'DIIS_START') damping_enabled = _validate_damping() soscf_enabled = _validate_soscf() frac_enabled = _validate_frac() efp_enabled = hasattr(self.molecule(), 'EFP') # SCF iterations! SCFE_old = 0.0 Dnorm = 0.0 while True: self.iteration_ += 1 diis_performed = False soscf_performed = False self.frac_performed_ = False #self.MOM_performed_ = False # redundant from common_init() self.save_density_and_energy() if efp_enabled: # EFP: Add efp contribution to Fock matrix self.H().copy(self.Horig) global mints_psi4_yo mints_psi4_yo = core.MintsHelper(self.basisset()) Vefp = modify_Fock_induced(self.molecule().EFP, mints_psi4_yo, verbose=verbose - 1) Vefp = core.Matrix.from_array(Vefp) self.H().add(Vefp) SCFE = 0.0 self.clear_external_potentials() core.timer_on("HF: Form G") self.form_G() core.timer_off("HF: Form G") incfock_performed = hasattr( self.jk(), "do_incfock_iter") and self.jk().do_incfock_iter() upcm = 0.0 if core.get_option('SCF', 'PCM'): calc_type = core.PCM.CalcType.Total if core.get_option("PCM", "PCM_SCF_TYPE") == "SEPARATE": calc_type = core.PCM.CalcType.NucAndEle Dt = self.Da().clone() Dt.add(self.Db()) upcm, Vpcm = self.get_PCM().compute_PCM_terms(Dt, calc_type) SCFE += upcm self.push_back_external_potential(Vpcm) self.set_variable("PCM POLARIZATION ENERGY", upcm) # P::e PCM self.set_energies("PCM Polarization", upcm) upe = 0.0 if core.get_option('SCF', 'PE'): Dt = self.Da().clone() Dt.add(self.Db()) upe, Vpe = self.pe_state.get_pe_contribution(Dt, elec_only=False) SCFE += upe self.push_back_external_potential(Vpe) self.set_variable("PE ENERGY", upe) # P::e PE self.set_energies("PE Energy", upe) core.timer_on("HF: Form F") # SAD: since we don't have orbitals yet, we might not be able # to form the real Fock matrix. Instead, build an initial one if (self.iteration_ == 0) and self.sad_: self.form_initial_F() else: self.form_F() core.timer_off("HF: Form F") if verbose > 3: self.Fa().print_out() self.Fb().print_out() SCFE += self.compute_E() if efp_enabled: global efp_Dt_psi4_yo # EFP: Add efp contribution to energy efp_Dt_psi4_yo = self.Da().clone() efp_Dt_psi4_yo.add(self.Db()) SCFE += self.molecule().EFP.get_wavefunction_dependent_energy() self.set_energies("Total Energy", SCFE) core.set_variable("SCF ITERATION ENERGY", SCFE) Ediff = SCFE - SCFE_old SCFE_old = SCFE status = [] # Check if we are doing SOSCF if (soscf_enabled and (self.iteration_ >= 3) and (Dnorm < core.get_option('SCF', 'SOSCF_START_CONVERGENCE'))): Dnorm = self.compute_orbital_gradient( False, core.get_option('SCF', 'DIIS_MAX_VECS')) diis_performed = False if self.functional().needs_xc(): base_name = "SOKS, nmicro=" else: base_name = "SOSCF, nmicro=" if not _converged(Ediff, Dnorm, e_conv=e_conv, d_conv=d_conv): nmicro = self.soscf_update( core.get_option('SCF', 'SOSCF_CONV'), core.get_option('SCF', 'SOSCF_MIN_ITER'), core.get_option('SCF', 'SOSCF_MAX_ITER'), core.get_option('SCF', 'SOSCF_PRINT')) # if zero, the soscf call bounced for some reason soscf_performed = (nmicro > 0) if soscf_performed: self.find_occupation() status.append(base_name + str(nmicro)) else: if verbose > 0: core.print_out( "Did not take a SOSCF step, using normal convergence methods\n" ) else: # need to ensure orthogonal orbitals and set epsilon status.append(base_name + "conv") core.timer_on("HF: Form C") self.form_C() core.timer_off("HF: Form C") soscf_performed = True # Stops DIIS if not soscf_performed: # Normal convergence procedures if we do not do SOSCF # SAD: form initial orbitals from the initial Fock matrix, and # reset the occupations. The reset is necessary because SAD # nalpha_ and nbeta_ are not guaranteed physical. # From here on, the density matrices are correct. if (self.iteration_ == 0) and self.sad_: self.form_initial_C() self.reset_occupation() self.find_occupation() else: # Run DIIS core.timer_on("HF: DIIS") diis_performed = False add_to_diis_subspace = self.diis_enabled_ and self.iteration_ >= self.diis_start_ Dnorm = self.compute_orbital_gradient( add_to_diis_subspace, core.get_option('SCF', 'DIIS_MAX_VECS')) if add_to_diis_subspace: for engine_used in self.diis(Dnorm): status.append(engine_used) core.timer_off("HF: DIIS") if verbose > 4 and diis_performed: core.print_out(" After DIIS:\n") self.Fa().print_out() self.Fb().print_out() # frac, MOM invoked here from Wfn::HF::find_occupation core.timer_on("HF: Form C") level_shift = core.get_option("SCF", "LEVEL_SHIFT") if level_shift > 0 and Dnorm > core.get_option( 'SCF', 'LEVEL_SHIFT_CUTOFF'): status.append("SHIFT") self.form_C(level_shift) else: self.form_C() core.timer_off("HF: Form C") if self.MOM_performed_: status.append("MOM") if self.frac_performed_: status.append("FRAC") if incfock_performed: status.append("INCFOCK") # Reset occupations if necessary if (self.iteration_ == 0) and self.reset_occ_: self.reset_occupation() self.find_occupation() # Form new density matrix core.timer_on("HF: Form D") self.form_D() core.timer_off("HF: Form D") self.set_variable("SCF ITERATION ENERGY", SCFE) core.set_variable("SCF D NORM", Dnorm) # After we've built the new D, damp the update if (damping_enabled and self.iteration_ > 1 and Dnorm > core.get_option('SCF', 'DAMPING_CONVERGENCE')): damping_percentage = core.get_option('SCF', "DAMPING_PERCENTAGE") self.damping_update(damping_percentage * 0.01) status.append("DAMP={}%".format(round(damping_percentage))) if core.has_option_changed("SCF", "ORBITALS_WRITE"): filename = core.get_option("SCF", "ORBITALS_WRITE") self.to_file(filename) if verbose > 3: self.Ca().print_out() self.Cb().print_out() self.Da().print_out() self.Db().print_out() # Print out the iteration core.print_out( " @%s%s iter %3s: %20.14f %12.5e %-11.5e %s\n" % ("DF-" if is_dfjk else "", reference, "SAD" if ((self.iteration_ == 0) and self.sad_) else self.iteration_, SCFE, Ediff, Dnorm, '/'.join(status))) # if a an excited MOM is requested but not started, don't stop yet # Note that MOM_performed_ just checks initialization, and our convergence measures used the pre-MOM orbitals if self.MOM_excited_ and ((not self.MOM_performed_) or self.iteration_ == core.get_option('SCF', "MOM_START")): continue # if a fractional occupation is requested but not started, don't stop yet if frac_enabled and not self.frac_performed_: continue # Call any postiteration callbacks if not ((self.iteration_ == 0) and self.sad_) and _converged( Ediff, Dnorm, e_conv=e_conv, d_conv=d_conv): break if self.iteration_ >= core.get_option('SCF', 'MAXITER'): raise SCFConvergenceError("""SCF iterations""", self.iteration_, self, Ediff, Dnorm)
def run_cfour(name, **kwargs): """Function that prepares environment and input files for a calculation calling Stanton and Gauss's CFOUR code. Also processes results back into Psi4 format. This function is not called directly but is instead called by :py:func:`~psi4.driver.energy` or :py:func:`~psi4.driver.optimize` when a Cfour method is requested (through *name* argument). In order to function correctly, the Cfour executable ``xcfour`` must be present in :envvar:`PATH` or :envvar:`PSIPATH`. .. hlist:: :columns: 1 * Many :ref:`PSI Variables <apdx:cfour_psivar>` extracted from the Cfour output * Python dictionary of associated file constants accessible as ``P4C4_INFO['zmat']``, ``P4C4_INFO['output']``, ``P4C4_INFO['grd']``, *etc.* :type name: str :param name: ``'c4-scf'`` || ``'c4-ccsd(t)'`` || ``'cfour'`` || etc. First argument, usually unlabeled. Indicates the computational method to be applied to the system. :type keep: :ref:`boolean <op_py_boolean>` :param keep: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether to delete the Cfour scratch directory upon completion of the Cfour job. :type path: str :param path: Indicates path to Cfour scratch directory (with respect to Psi4 scratch directory). Otherwise, the default is a subdirectory within the Psi4 scratch directory. If specified, GENBAS and/or ZMAT within will be used. :type genbas: str :param genbas: Indicates that contents should be used for GENBAS file. GENBAS is a complicated topic. It is quite unnecessary if the molecule is from a molecule {...} block and basis is set through |Psifours| BASIS keyword. In that case, a GENBAS is written from LibMints and all is well. Otherwise, a GENBAS is looked for in the usual places: PSIPATH, PATH, PSIDATADIR/basis. If path kwarg is specified, also looks there preferentially for a GENBAS. Can also specify GENBAS within an input file through a string and setting the genbas kwarg. Note that due to the input parser's aggression, blank lines need to be replaced by the text blankline. """ lowername = name.lower() internal_p4c4_info = {} return_wfn = kwargs.pop('return_wfn', False) # Make sure the molecule the user provided is the active one molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() optstash = p4util.OptionsState(['CFOUR', 'TRANSLATE_PSI4']) # Determine calling function and hence dertype calledby = inspect.stack()[1][3] dertype = ['energy', 'gradient', 'hessian'].index(calledby) #print('I am %s called by %s called by %s.\n' % # (inspect.stack()[0][3], inspect.stack()[1][3], inspect.stack()[2][3])) # Save submission directory current_directory = os.getcwd() # Move into job scratch directory psioh = core.IOManager.shared_object() psio = core.IO.shared_object() os.chdir(psioh.get_default_path()) # Construct and move into cfour subdirectory of job scratch directory cfour_tmpdir = kwargs['path'] if 'path' in kwargs else \ 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \ '.cfour.' + str(uuid.uuid4())[:8] if not os.path.exists(cfour_tmpdir): os.mkdir(cfour_tmpdir) os.chdir(cfour_tmpdir) # Find environment by merging PSIPATH and PATH environment variables lenv = { 'PATH': ':'.join([ os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '' ]) + ':' + os.environ.get('PATH') + ':' + core.get_datadir() + '/basis', 'GENBAS_PATH': core.get_datadir() + '/basis', 'CFOUR_NUM_CORES': os.environ.get('CFOUR_NUM_CORES'), 'MKL_NUM_THREADS': os.environ.get('MKL_NUM_THREADS'), 'OMP_NUM_THREADS': os.environ.get('OMP_NUM_THREADS'), 'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH') } if 'path' in kwargs: lenv['PATH'] = kwargs['path'] + ':' + lenv['PATH'] # Filter out None values as subprocess will fault on them lenv = {k: v for k, v in lenv.items() if v is not None} # Load the GENBAS file genbas_path = qcdb.search_file('GENBAS', lenv['GENBAS_PATH']) if genbas_path: try: shutil.copy2(genbas_path, psioh.get_default_path() + cfour_tmpdir) except shutil.Error: # should only fail if src and dest equivalent pass core.print_out("\n GENBAS loaded from %s\n" % (genbas_path)) core.print_out(" CFOUR to be run from %s\n" % (psioh.get_default_path() + cfour_tmpdir)) else: message = """ GENBAS file for CFOUR interface not found. Either: [1] Supply a GENBAS by placing it in PATH or PSIPATH [1a] Use cfour {} block with molecule and basis directives. [1b] Use molecule {} block and CFOUR_BASIS keyword. [2] Allow Psi4's internal basis sets to convert to GENBAS [2a] Use molecule {} block and BASIS keyword. """ core.print_out(message) core.print_out(' Search path that was tried:\n') core.print_out(lenv['PATH'].replace(':', ', ')) # Generate the ZMAT input file in scratch if 'path' in kwargs and os.path.isfile('ZMAT'): core.print_out(" ZMAT loaded from %s\n" % (psioh.get_default_path() + kwargs['path'] + '/ZMAT')) else: with open('ZMAT', 'w') as cfour_infile: cfour_infile.write(write_zmat(lowername, dertype, molecule)) internal_p4c4_info['zmat'] = open('ZMAT', 'r').read() #core.print_out('\n====== Begin ZMAT input for CFOUR ======\n') #core.print_out(open('ZMAT', 'r').read()) #core.print_out('======= End ZMAT input for CFOUR =======\n\n') #print('\n====== Begin ZMAT input for CFOUR ======') #print(open('ZMAT', 'r').read()) #print('======= End ZMAT input for CFOUR =======\n') if 'genbas' in kwargs: with open('GENBAS', 'w') as cfour_basfile: cfour_basfile.write(kwargs['genbas'].replace( '\nblankline\n', '\n\n')) core.print_out(' GENBAS loaded from kwargs string\n') # Close psi4 output file and reopen with filehandle print('output in', current_directory + '/' + core.outfile_name()) pathfill = '' if os.path.isabs( core.outfile_name()) else current_directory + os.path.sep # Handle threading # OMP_NUM_THREADS from env is in lenv from above # threads from psi4 -n (core.get_num_threads()) is ignored # CFOUR_OMP_NUM_THREADS psi4 option takes precedence, handled below if core.has_option_changed('CFOUR', 'CFOUR_OMP_NUM_THREADS'): lenv['OMP_NUM_THREADS'] = str( core.get_option('CFOUR', 'CFOUR_OMP_NUM_THREADS')) #print("""\n\n<<<<< RUNNING CFOUR ... >>>>>\n\n""") # Call executable xcfour, directing cfour output to the psi4 output file cfour_executable = kwargs['c4exec'] if 'c4exec' in kwargs else 'xcfour' try: retcode = subprocess.Popen([cfour_executable], bufsize=0, stdout=subprocess.PIPE, env=lenv) except OSError as e: sys.stderr.write( 'Program %s not found in path or execution failed: %s\n' % (cfour_executable, e.strerror)) message = ('Program %s not found in path or execution failed: %s\n' % (cfour_executable, e.strerror)) raise ValidationError(message) c4out = '' while True: data = retcode.stdout.readline() data = data.decode('utf-8') if not data: break core.print_out(data) c4out += data internal_p4c4_info['output'] = c4out c4files = {} core.print_out('\n') for item in ['GRD', 'FCMFINAL', 'DIPOL']: try: with open(psioh.get_default_path() + cfour_tmpdir + '/' + item, 'r') as handle: c4files[item] = handle.read() core.print_out(' CFOUR scratch file %s has been read\n' % (item)) core.print_out('%s\n' % c4files[item]) internal_p4c4_info[item.lower()] = c4files[item] except IOError: pass core.print_out('\n') if molecule.name() == 'blank_molecule_psi4_yo': qcdbmolecule = None else: molecule.update_geometry() qcdbmolecule = qcdb.Molecule( molecule.create_psi4_string_from_molecule()) qcdbmolecule.update_geometry() # c4mol, if it exists, is dinky, just a clue to geometry of cfour results psivar, c4grad, c4mol = qcdb.cfour.harvest(qcdbmolecule, c4out, **c4files) # Absorb results into psi4 data structures for key in psivar.keys(): core.set_variable(key.upper(), float(psivar[key])) if qcdbmolecule is None and c4mol is not None: molrec = qcel.molparse.from_string( c4mol.create_psi4_string_from_molecule(), enable_qm=True, missing_enabled_return_qm='minimal', enable_efp=False, missing_enabled_return_efp='none', ) molecule = core.Molecule.from_dict(molrec['qm']) molecule.set_name('blank_molecule_psi4_yo') core.set_active_molecule(molecule) molecule.update_geometry() # This case arises when no Molecule going into calc (cfour {} block) but want # to know the orientation at which grad, properties, etc. are returned (c4mol). # c4mol is dinky, w/o chg, mult, dummies and retains name # blank_molecule_psi4_yo so as to not interfere with future cfour {} blocks if c4grad is not None: mat = core.Matrix.from_list(c4grad) core.set_gradient(mat) #print ' <<< [3] C4-GRD-GRAD >>>' #mat.print() # exit(1) # # Things needed core.so module to do # collect c4out string # read GRD # read FCMFINAL # see if theres an active molecule # # Things delegatable to qcdb # parsing c4out # reading GRD and FCMFINAL strings # reconciling p4 and c4 molecules (orient) # reconciling c4out and GRD and FCMFINAL results # transforming frame of results back to p4 # # Things run_cfour needs to have back # psivar # qcdb.Molecule of c4? # coordinates? # gradient in p4 frame # # Process the cfour output # psivar, c4coord, c4grad = qcdb.cfour.cfour_harvest(c4out) # for key in psivar.keys(): # core.set_variable(key.upper(), float(psivar[key])) # # # Awful Hack - Go Away TODO # if c4grad: # molecule = core.get_active_molecule() # molecule.update_geometry() # # if molecule.name() == 'blank_molecule_psi4_yo': # p4grad = c4grad # p4coord = c4coord # else: # qcdbmolecule = qcdb.Molecule(molecule.create_psi4_string_from_molecule()) # #p4grad = qcdbmolecule.deorient_array_from_cfour(c4coord, c4grad) # #p4coord = qcdbmolecule.deorient_array_from_cfour(c4coord, c4coord) # # with open(psioh.get_default_path() + cfour_tmpdir + '/GRD', 'r') as cfour_grdfile: # c4outgrd = cfour_grdfile.read() # print('GRD\n',c4outgrd) # c4coordGRD, c4gradGRD = qcdb.cfour.cfour_harvest_files(qcdbmolecule, grd=c4outgrd) # # p4mat = core.Matrix.from_list(p4grad) # core.set_gradient(p4mat) # print(' <<< P4 PSIVAR >>>') # for item in psivar: # print(' %30s %16.8f' % (item, psivar[item])) #print(' <<< P4 COORD >>>') #for item in p4coord: # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) # print(' <<< P4 GRAD >>>') # for item in c4grad: # print(' %16.8f %16.8f %16.8f' % (item[0], item[1], item[2])) # Clean up cfour scratch directory unless user instructs otherwise keep = yes.match(str(kwargs['keep'])) if 'keep' in kwargs else False os.chdir('..') try: if keep or ('path' in kwargs): core.print_out('\n CFOUR scratch files have been kept in %s\n' % (psioh.get_default_path() + cfour_tmpdir)) else: shutil.rmtree(cfour_tmpdir) except OSError as e: print('Unable to remove CFOUR temporary directory %s' % e, file=sys.stderr) exit(1) # Return to submission directory and reopen output file os.chdir(current_directory) core.print_out('\n') p4util.banner(' Cfour %s %s Results ' % (name.lower(), calledby.capitalize())) core.print_variables() if c4grad is not None: core.get_gradient().print_out() core.print_out('\n') p4util.banner(' Cfour %s %s Results ' % (name.lower(), calledby.capitalize())) core.print_variables() if c4grad is not None: core.get_gradient().print_out() # Quit if Cfour threw error if 'CFOUR ERROR CODE' in core.variables(): raise ValidationError("""Cfour exited abnormally.""") P4C4_INFO.clear() P4C4_INFO.update(internal_p4c4_info) optstash.restore() # new skeleton wavefunction w/mol, highest-SCF basis (just to choose one), & not energy # Feb 2017 hack. Could get proper basis in skel wfn even if not through p4 basis kw if core.get_global_option('BASIS') in ["", "(AUTO)"]: gobas = "sto-3g" else: gobas = core.get_global_option('BASIS') basis = core.BasisSet.build(molecule, "ORBITAL", gobas) if basis.has_ECP(): raise ValidationError("""ECPs not hooked up for Cfour""") wfn = core.Wavefunction(molecule, basis) for k, v in psivar.items(): wfn.set_variable(k.upper(), float(v)) optstash.restore() if dertype == 0: finalquantity = psivar['CURRENT ENERGY'] elif dertype == 1: finalquantity = core.get_gradient() wfn.set_gradient(finalquantity) if finalquantity.rows(0) < 20: core.print_out('CURRENT GRADIENT') finalquantity.print_out() elif dertype == 2: pass #finalquantity = finalhessian #wfn.set_hessian(finalquantity) #if finalquantity.rows(0) < 20: # core.print_out('CURRENT HESSIAN') # finalquantity.print_out() return wfn
def build_superfunctional(name, restricted): npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS") deriv = 1 # Default depth for now # We are a XC generating function if hasattr(name, '__call__'): custom_error = "SCF: Custom functional type must either be a SuperFunctional or a tuple of (SuperFunctional, (base_name, dashparam))." sfunc = name("name", npoints, deriv, restricted) # Without Dispersion if isinstance(sfunc, core.SuperFunctional): sup = (sfunc, False) # With Dispersion elif isinstance(sup[0], core.SuperFunctional): sup = sfunc # Can we validate dispersion? else: raise ValidationError(custom_error) # Double check that the SuperFunctional is correctly sized (why dont we always do this?) sup[0].set_max_points(npoints) sup[0].set_deriv(deriv) sup[0].allocate() # Normal string based data elif name.lower() in superfunctionals.keys(): sup = superfunctionals[name.lower()](name, npoints, deriv, restricted) elif name.upper() in superfunctionals.keys(): sup = superfunctionals[name.upper()](name, npoints, deriv, restricted) # Check if we are dispersion elif any(name.lower().endswith(al) for al in dftd3.full_dash_keys): # Odd hack for b97-d if 'b97-d' in name: name = name.replace('b97', 'b97-d') dashparam = [x for x in dftd3.full_dash_keys if name.endswith(x)] if len(dashparam) > 1: raise Exception("Dashparam %s is ambiguous.") else: dashparam = dashparam[0] base_name = name.replace('-' + dashparam, '') if dashparam in ['d2', 'd']: dashparam = 'd2p4' if dashparam == 'd3': dashparam = 'd3zero' if dashparam == 'd3m': dashparam = 'd3mzero' if base_name not in superfunctionals.keys(): raise ValidationError( "SCF: Functional (%s) with base (%s) not found!" % (name, base_name)) func = superfunctionals[base_name](base_name, npoints, deriv, restricted)[0] base_name = base_name.replace('wpbe', 'lcwpbe') sup = (func, (base_name, dashparam)) else: raise ValidationError("SCF: Functional (%s) not found!" % name) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()): raise ValidationError( "INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping." ) # Lock and unlock the functional sup[0].set_lock(False) # Set options if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc(): omega = core.get_option("SCF", "DFT_OMEGA") sup[0].set_x_omega(omega) # We also need to loop through all of the exchange functionals if sup[0].is_libxc_func(): # Full libxc funcs are dropped in c_functionals (smooth move!) sup[0].c_functionals()[0].set_omega(omega) else: for x_func in sup[0].x_functionals(): x_func.set_omega(omega) if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc(): sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C")) if core.has_option_changed("SCF", "DFT_ALPHA"): sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA")) if core.has_option_changed("SCF", "DFT_ALPHA_C"): sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C")) # Check SCF_TYPE if sup[0].is_x_lrc() and (core.get_option("SCF", "SCF_TYPE") not in ["DIRECT", "DF", "OUT_OF_CORE", "PK"]): raise ValidationError( "SCF: SCF_TYPE (%s) not supported for range-seperated functionals." % core.get_option("SCF", "SCF_TYPE")) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc()): raise ValidationError( 'INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.' ) sup[0].set_lock(True) return sup
def build_superfunctional(alias, restricted): name = alias.lower() npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS") deriv = 1 # Default depth for now # Grab out superfunctional if name in ["gen", ""]: sup = (core.get_option("DFT_CUSTOM_FUNCTIONAL"), False) if not isinstance(sup[0], core.SuperFunctional): raise KeyError( "SCF: Custom Functional requested, but nothing provided in DFT_CUSTOM_FUNCTIONAL" ) elif name in superfunctionals.keys(): sup = superfunctionals[name](name, npoints, deriv, restricted) elif name.upper() in superfunctionals.keys(): sup = superfunctionals[name.upper()](name, npoints, deriv, restricted) elif any(name.endswith(al) for al in dftd3.full_dash_keys): # Odd hack for b97-d if 'b97-d' in name: name = name.replace('b97', 'b97-d') dashparam = [x for x in dftd3.full_dash_keys if name.endswith(x)] if len(dashparam) > 1: raise Exception("Dashparam %s is ambiguous.") else: dashparam = dashparam[0] base_name = name.replace('-' + dashparam, '') if dashparam in ['d2', 'd']: dashparam = 'd2p4' if dashparam == 'd3': dashparam = 'd3zero' if dashparam == 'd3m': dashparam = 'd3mzero' if base_name not in superfunctionals.keys(): raise KeyError("SCF: Functional (%s) with base (%s) not found!" % (alias, base_name)) func = superfunctionals[base_name](base_name, npoints, deriv, restricted)[0] base_name = base_name.replace('wpbe', 'lcwpbe') sup = (func, (base_name, dashparam)) else: raise KeyError("SCF: Functional (%s) not found!" % alias) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()): raise ValidationError( "INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping." ) # Set options if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc(): sup[0].set_x_omega(core.get_option("SCF", "DFT_OMEGA")) if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc(): sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C")) if core.has_option_changed("SCF", "DFT_ALPHA"): sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA")) if core.has_option_changed("SCF", "DFT_ALPHA_C"): sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C")) # Check SCF_TYPE if sup[0].is_x_lrc() and (core.get_option("SCF", "SCF_TYPE") not in ["DIRECT", "DF", "OUT_OF_CORE", "PK"]): raise KeyError( "SCF: SCF_TYPE (%s) not supported for range-seperated functionals." % core.get_option("SCF", "SCF_TYPE")) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc()): raise ValidationError( 'INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.' ) return sup
def sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=None, sapt_jk_B=None, data=None, print_header=True, cleanup_jk=True): """ The primary SAPT(DFT) algorithm to compute the interaction energy once the wavefunctions have been built. Example ------- dimer = psi4.geometry(''' Ne -- Ar 1 6.5 units bohr ''') psi4.set_options({"BASIS": "aug-cc-pVDZ"}) # Prepare the fragments sapt_dimer, monomerA, monomerB = psi4.proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Run the first monomer set DFT_GRAC_SHIFT 0.203293 wfnA, energyA = psi4.energy("PBE0", monomer=monomerA, return_wfn=True) # Run the second monomer set DFT_GRAC_SHIFT 0.138264 wfnB, energyB = psi4.energy("PBE0", monomer=monomerB, return_wfn=True) # Build the dimer wavefunction wfnD = psi4.core.Wavefunction.build(sapt_dimer) # Compute SAPT(DFT) from the provided wavefunctions data = psi4.procrouting.sapt.sapt_dft(wfnD, wfnA, wfnB) """ # Handle the input options core.timer_on("SAPT(DFT):SAPT(DFT):Build JK") if print_header: sapt_dft_header() if sapt_jk is None: core.print_out("\n => Building SAPT JK object <= \n\n") sapt_jk = core.JK.build(dimer_wfn.basisset()) sapt_jk.set_do_J(True) sapt_jk.set_do_K(True) if wfn_A.functional().is_x_lrc(): sapt_jk.set_do_wK(True) sapt_jk.set_omega(wfn_A.functional().x_omega()) sapt_jk.initialize() sapt_jk.print_header() if wfn_B.functional().is_x_lrc() and (wfn_A.functional().x_omega() != wfn_B.functional().x_omega()): core.print_out(" => Monomer B: Building SAPT JK object <= \n\n") core.print_out( " Reason: MonomerA Omega != MonomerB Omega\n\n") sapt_jk_B = core.JK.build(dimer_wfn.basisset()) sapt_jk_B.set_do_J(True) sapt_jk_B.set_do_K(True) sapt_jk_B.set_do_wK(True) sapt_jk_B.set_omega(wfn_B.functional().x_omega()) sapt_jk_B.initialize() sapt_jk_B.print_header() else: sapt_jk.set_do_K(True) if data is None: data = {} # Build SAPT cache cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) core.timer_off("SAPT(DFT):SAPT(DFT):Build JK") # Electrostatics core.timer_on("SAPT(DFT):SAPT(DFT):elst") elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) core.timer_off("SAPT(DFT):SAPT(DFT):elst") # Exchange core.timer_on("SAPT(DFT):SAPT(DFT):exch") exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) core.timer_off("SAPT(DFT):SAPT(DFT):exch") # Induction core.timer_on("SAPT(DFT):SAPT(DFT):ind") ind = sapt_jk_terms.induction(cache, sapt_jk, True, sapt_jk_B=sapt_jk_B, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE"), Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF")) data.update(ind) core.timer_off("SAPT(DFT):SAPT(DFT):ind") # Blow away JK object before doing MP2 for memory considerations if cleanup_jk: sapt_jk.finalize() # Hybrid xc kernel check do_hybrid = core.get_option("SAPT", "SAPT_DFT_DO_HYBRID") is_x_hybrid = wfn_B.functional().is_x_hybrid() is_x_lrc = wfn_B.functional().is_x_lrc() hybrid_specified = core.has_option_changed("SAPT", "SAPT_DFT_DO_HYBRID") if is_x_lrc: if do_hybrid: if hybrid_specified: raise ValidationError( "SAPT(DFT): Hybrid xc kernel not yet implemented for range-separated funtionals." ) else: core.print_out( "Warning: Hybrid xc kernel not yet implemented for range-separated funtionals; hybrid kernel capability is turned off.\n" ) is_hybrid = False else: if do_hybrid: is_hybrid = is_x_hybrid else: is_hybrid = False # Dispersion core.timer_on("SAPT(DFT):SAPT(DFT):disp") primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(dimer_wfn.molecule(), "DF_BASIS_MP2", core.get_option("DFMP2", "DF_BASIS_MP2"), "RIFIT", core.get_global_option('BASIS')) x_alpha = wfn_B.functional().x_alpha() if not is_hybrid: x_alpha = 0.0 fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis, cache, is_hybrid, x_alpha) data.update(fdds_disp) if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT": mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A, primary_basis, aux_basis, cache, do_print=True) else: mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion(dimer_wfn, wfn_A, wfn_B, primary_basis, aux_basis, cache, do_print=True) data.update(mp2_disp) core.timer_off("SAPT(DFT):SAPT(DFT):disp") # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) return data
def run_sapt_dft(name, **kwargs): optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out('Warning! SAPT argument "ref_wfn" is only able to use molecule information.') sapt_dimer = ref_wfn.molecule() sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Grab overall settings mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A") mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B") do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF") sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL") # Print out the title and some information core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT) Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") if (do_delta_hf): core.print_out(" HF (Dimer)\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out(" DFT (Monomer A)\n") core.print_out(" DFT (Monomer B)\n") core.print_out("\n") if (sapt_dft_functional != "HF") and ((mon_a_shift == 0.0) or (mon_b_shift == 0.0)): raise ValidationError('SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".') if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError('SAPT(DFT) currently only supports restricted references.') core.IO.set_default_namespace('dimer') data = {} core.set_global_option("SAVE_JK", True) if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_cache = {} hf_wfn_dimer = None if do_delta_hf: if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') hf_data = {} hf_wfn_dimer = scf_helper( "SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') hf_wfn_A = scf_helper( "SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs) hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') hf_wfn_B = scf_helper( "SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs) hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY") # Move it back to monomer A if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerB', 'dimer') core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT): delta HF Segement".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") # Build cache and JK sapt_jk = hf_wfn_B.jk() hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) # Exchange exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) # Induction ind = sapt_jk_terms.induction( hf_cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) hf_data.update(ind) dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data["HF MONOMER B"] core.print_out("\n") core.print_out(print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value)) data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF") if hf_wfn_dimer is None: dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS")) else: dimer_wfn = hf_wfn_dimer # Set the primary functional core.set_local_option('SCF', 'REFERENCE', 'RKS') # Compute Monomer A wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') if mon_a_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift) # Save the JK object core.IO.set_default_namespace('monomerA') wfn_A = scf_helper(sapt_dft_functional, post_scf=False, molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs) data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Compute Monomer B wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') if mon_b_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift) core.IO.set_default_namespace('monomerB') wfn_B = scf_helper(sapt_dft_functional, post_scf=False, molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs) data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Print out the title and some information core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT): Intermolecular Interaction Segment".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) # Build cache and JK sapt_jk = wfn_B.jk() cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) # Exchange exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) # Induction ind = sapt_jk_terms.induction( cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) data.update(ind) # Dispersion primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(sapt_dimer, "DF_BASIS_MP2", core.get_option("DFMP2", "DF_BASIS_MP2"), "RIFIT", core.get_global_option('BASIS')) fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis, cache) data.update(fdds_disp) if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT": mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A, primary_basis, aux_basis, cache, do_print=True) else: mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion( dimer_wfn, wfn_A, wfn_B, primary_basis, aux_basis, cache, do_print=True) data.update(mp2_disp) # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) # Copy data back into globals for k, v in data.items(): core.set_variable(k, v) core.tstop() return dimer_wfn
def run_sapt_dft(name, **kwargs): optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out('Warning! SAPT argument "ref_wfn" is only able to use molecule information.') sapt_dimer = ref_wfn.molecule() sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule(sapt_dimer, "dimer") # Grab overall settings mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A") mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B") do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF") sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL") # Print out the title and some information core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT) Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" !!! WARNING: SAPT(DFT) capability is in beta. Please use with caution. !!!\n\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") if (do_delta_hf): core.print_out(" HF (Dimer)\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out(" DFT (Monomer A)\n") core.print_out(" DFT (Monomer B)\n") core.print_out("\n") if (sapt_dft_functional != "HF") and ((mon_a_shift == 0.0) or (mon_b_shift == 0.0)): raise ValidationError('SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".') if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError('SAPT(DFT) currently only supports restricted references.') core.IO.set_default_namespace('dimer') data = {} if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_cache = {} hf_wfn_dimer = None if do_delta_hf: if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') hf_data = {} hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') hf_wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs) hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY") core.set_global_option("SAVE_JK", True) if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') hf_wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs) hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY") core.set_global_option("SAVE_JK", False) # Move it back to monomer A if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerB', 'dimer') core.print_out("\n") core.print_out(" ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT): delta HF Segement".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out(" ---------------------------------------------------------\n") core.print_out("\n") # Build cache and JK sapt_jk = hf_wfn_B.jk() hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) # Exchange exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) # Induction ind = sapt_jk_terms.induction( hf_cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE"), Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF")) hf_data.update(ind) dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data["HF MONOMER B"] core.print_out("\n") core.print_out(print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value)) data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF") sapt_jk.finalize() if hf_wfn_dimer is None: dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS")) else: dimer_wfn = hf_wfn_dimer # Set the primary functional core.set_local_option('SCF', 'REFERENCE', 'RKS') # Compute Monomer A wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') if mon_a_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift) # Save the JK object core.IO.set_default_namespace('monomerA') wfn_A = scf_helper( sapt_dft_functional, post_scf=False, molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs) data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Compute Monomer B wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') if mon_b_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift) core.set_global_option("SAVE_JK", True) core.IO.set_default_namespace('monomerB') wfn_B = scf_helper( sapt_dft_functional, post_scf=False, molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs) data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Write out header scf_alg = core.get_option("SCF", "SCF_TYPE") sapt_dft_header(sapt_dft_functional, mon_a_shift, mon_b_shift, bool(do_delta_hf), scf_alg) # Call SAPT(DFT) sapt_jk = wfn_B.jk() sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=sapt_jk, data=data, print_header=False) # Copy data back into globals for k, v in data.items(): core.set_variable(k, v) core.tstop() return dimer_wfn
def run_sapt_dft(name, **kwargs): optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_FUNCTIONAL'], ['SCF', 'DFT_GRAC_SHIFT'], ['SCF', 'SAVE_JK']) core.tstart() # Alter default algorithm if not core.has_option_changed('SCF', 'SCF_TYPE'): core.set_local_option('SCF', 'SCF_TYPE', 'DF') core.prepare_options_for_module("SAPT") # Get the molecule of interest ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: sapt_dimer = kwargs.pop('molecule', core.get_active_molecule()) else: core.print_out( 'Warning! SAPT argument "ref_wfn" is only able to use molecule information.' ) sapt_dimer = ref_wfn.molecule() # Shifting to C1 so we need to copy the active molecule if sapt_dimer.schoenflies_symbol() != 'c1': core.print_out( ' SAPT does not make use of molecular symmetry, further calculations in C1 point group.\n' ) # Make sure the geometry doesnt shift or rotate sapt_dimer = sapt_dimer.clone() sapt_dimer.reset_point_group('c1') sapt_dimer.fix_orientation(True) sapt_dimer.fix_com(True) sapt_dimer.update_geometry() # Grab overall settings mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A") mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B") do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF") sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL") # Print out the title and some information core.print_out("\n") core.print_out( " ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT) Procedure".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) core.print_out("\n") core.print_out(" Required computations:\n") if (do_delta_hf): core.print_out(" HF (Dimer)\n") core.print_out(" HF (Monomer A)\n") core.print_out(" HF (Monomer B)\n") core.print_out(" DFT (Monomer A)\n") core.print_out(" DFT (Monomer B)\n") core.print_out("\n") if (mon_a_shift == 0.0) or (mon_b_shift == 0.0): raise ValidationError( 'SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".') if (core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError( 'SAPT(DFT) currently only supports restricted references.') nfrag = sapt_dimer.nfragments() if nfrag != 2: raise ValidationError( 'SAPT requires active molecule to have 2 fragments, not %s.' % (nfrag)) monomerA = sapt_dimer.extract_subsets(1, 2) monomerA.set_name('monomerA') monomerB = sapt_dimer.extract_subsets(2, 1) monomerB.set_name('monomerB') core.IO.set_default_namespace('dimer') data = {} core.set_global_option("SAVE_JK", True) if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): # core.set_global_option('DF_INTS_IO', 'LOAD') core.set_global_option('DF_INTS_IO', 'SAVE') # # Compute dimer wavefunction hf_cache = {} hf_wfn_dimer = None if do_delta_hf: if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.set_global_option('DF_INTS_IO', 'SAVE') hf_data = {} hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs) hf_data["HF DIMER"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') hf_wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs) hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY") if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') hf_wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs) hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY") # Move it back to monomer A if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerB', 'dimer') core.print_out("\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out(" " + "SAPT(DFT): delta HF Segement".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n" ) core.print_out("\n") # Build cache and JK sapt_jk = hf_wfn_B.jk() hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(hf_cache, True) hf_data.update(elst) # Exchange exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True) hf_data.update(exch) # Induction ind = sapt_jk_terms.induction( hf_cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) hf_data.update(ind) dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data[ "HF MONOMER B"] core.print_out("\n") core.print_out( print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value)) data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF") if hf_wfn_dimer is None: dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS")) else: dimer_wfn = hf_wfn_dimer # Set the primary functional core.set_global_option("DFT_FUNCTIONAL", core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL")) core.set_local_option('SCF', 'REFERENCE', 'RKS') # Compute Monomer A wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'dimer', 'monomerA') if mon_a_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift) # Save the JK object core.IO.set_default_namespace('monomerA') wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs) data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Compute Monomer B wavefunction if (core.get_option('SCF', 'SCF_TYPE') == 'DF'): core.IO.change_file_namespace(97, 'monomerA', 'monomerB') if mon_b_shift: core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift) core.IO.set_default_namespace('monomerB') wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs) data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY") core.set_global_option("DFT_GRAC_SHIFT", 0.0) # Print out the title and some information core.print_out("\n") core.print_out( " ---------------------------------------------------------\n") core.print_out(" " + "SAPT(DFT): Intermolecular Interaction Segment".center(58) + "\n") core.print_out("\n") core.print_out(" " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n") core.print_out( " ---------------------------------------------------------\n") core.print_out("\n") core.print_out(" ==> Algorithm <==\n\n") core.print_out(" SAPT DFT Functional %12s\n" % str(sapt_dft_functional)) core.print_out(" Monomer A GRAC Shift %12.6f\n" % mon_a_shift) core.print_out(" Monomer B GRAC Shift %12.6f\n" % mon_b_shift) core.print_out(" Delta HF %12s\n" % ("True" if do_delta_hf else "False")) core.print_out(" JK Algorithm %12s\n" % core.get_option("SCF", "SCF_TYPE")) # Build cache and JK sapt_jk = wfn_B.jk() cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True) # Electostatics elst = sapt_jk_terms.electrostatics(cache, True) data.update(elst) # Exchange exch = sapt_jk_terms.exchange(cache, sapt_jk, True) data.update(exch) # Induction ind = sapt_jk_terms.induction(cache, sapt_jk, True, maxiter=core.get_option("SAPT", "MAXITER"), conv=core.get_option("SAPT", "D_CONVERGENCE")) data.update(ind) # Dispersion primary_basis = wfn_A.basisset() core.print_out("\n") aux_basis = core.BasisSet.build(sapt_dimer, "DF_BASIS_MP2", core.get_option("DFMP2", "DF_BASIS_MP2"), "RIFIT", core.get_global_option('BASIS')) fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis, cache) data.update(fdds_disp) if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT": mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A, primary_basis, aux_basis, cache, do_print=True) else: mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion(dimer_wfn, wfn_A, wfn_B, primary_basis, aux_basis, cache, do_print=True) data.update(mp2_disp) # Print out final data core.print_out("\n") core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)")) core.tstop() return dimer_wfn
def build_superfunctional(name, restricted): npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS") deriv = 1 # Default depth for now # We are a XC generating function if hasattr(name, '__call__'): custom_error = "SCF: Custom functional type must either be a SuperFunctional or a tuple of (SuperFunctional, (base_name, dashparam))." sfunc = name("name", npoints, deriv, restricted) # Without Dispersion if isinstance(sfunc, core.SuperFunctional): sup = (sfunc, False) # With Dispersion elif isinstance(sup[0], core.SuperFunctional): sup = sfunc # Can we validate dispersion? else: raise ValidationError(custom_error) # Double check that the SuperFunctional is correctly sized (why dont we always do this?) sup[0].set_max_points(npoints) sup[0].set_deriv(deriv) sup[0].allocate() # Check for supplied dict_func functionals elif isinstance(name, dict): sup = dft_builder.build_superfunctional_from_dictionary(name, npoints, deriv, restricted) # Check for pre-defined dict-based functionals elif name.lower() in dft_builder.functionals: sup = dft_builder.build_superfunctional_from_dictionary(dft_builder.functionals[name.lower()], npoints, deriv, restricted) else: raise ValidationError("SCF: Functional (%s) not found!" % name) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()): raise ValidationError("INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping.") # Lock and unlock the functional sup[0].set_lock(False) # Set options if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc(): omega = core.get_option("SCF", "DFT_OMEGA") sup[0].set_x_omega(omega) # We also need to loop through all of the exchange functionals if sup[0].is_libxc_func(): # Full libxc funcs are dropped in c_functionals (smooth move!) sup[0].c_functionals()[0].set_omega(omega) else: for x_func in sup[0].x_functionals(): x_func.set_omega(omega) if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc(): sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C")) if core.has_option_changed("SCF", "DFT_ALPHA"): sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA")) if core.has_option_changed("SCF", "DFT_ALPHA_C"): sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C")) # add VV10 correlation to any functional or modify existing # custom procedures using name 'scf' without any quadrature grid like HF will fail and are not detected if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS") and sup[0].vv10_b() > 0.0): if not isinstance(name, dict): if (name.lower() == 'hf'): raise ValidationError("SCF: HF with -NL not implemented") nl_tuple = core.get_option("SCF", "NL_DISPERSION_PARAMETERS") sup[0].set_vv10_b(nl_tuple[0]) if len(nl_tuple) > 1: sup[0].set_vv10_c(nl_tuple[1]) if len(nl_tuple) > 2: raise ValidationError("too many entries in NL_DISPERSION_PARAMETERS for DFT-NL") elif core.has_option_changed("SCF", "DFT_VV10_B"): if not isinstance(name, dict): if (name.lower() == 'hf'): raise ValidationError("SCF: HF with -NL not implemented") vv10_b = core.get_option("SCF", "DFT_VV10_B") sup[0].set_vv10_b(vv10_b) if core.has_option_changed("SCF", "DFT_VV10_C"): vv10_c = core.get_option("SCF", "DFT_VV10_C") sup[0].set_vv10_c(vv10_c) if (abs(sup[0].vv10_c() - 0.0) <= 1e-8): core.print_out("SCF: VV10_C not specified. Using default (C=0.0093)!") sup[0].set_vv10_c(0.0093) if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS") and core.has_option_changed("SCF", "DFT_VV10_B")): raise ValidationError("SCF: Decide between NL_DISPERSION_PARAMETERS and DFT_VV10_B !!") # Check SCF_TYPE if sup[0].is_x_lrc() and (core.get_global_option("SCF_TYPE") not in ["DIRECT", "DF", "OUT_OF_CORE", "PK"]): raise ValidationError( "SCF: SCF_TYPE (%s) not supported for range-separated functionals, plese use SCF_TYPE = 'DF' to automatically select the correct JK build." % core.get_global_option("SCF_TYPE")) if (core.get_global_option('INTEGRAL_PACKAGE') == 'ERD') and (sup[0].is_x_lrc()): raise ValidationError('INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.') sup[0].set_lock(True) return sup