def run_v2rdm_casscf(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that v2rdm_casscf can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('v2rdm_casscf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState( ['SCF', 'DF_INTS_IO']) psi4.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # Your plugin's psi4 run sequence goes here scf_wfn = scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.get_option("V2RDM_CASSCF","RESTART_FROM_CHECKPOINT_FILE") # todo PSIF_V2RDM_CHECKPOINT should be definied in psifiles.h if ( filename != "" ): molname = psi4.wavefunction().molecule().name() p4util.copy_file_to_scratch(filename,'psi',molname,269,False) returnvalue = psi4.plugin('v2rdm_casscf.so',scf_wfn) #psi4.set_variable('CURRENT ENERGY', returnvalue) return psi4.get_variable('CURRENT ENERGY')
def run_plugin_mp2(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that mollerplesset2 can be called via :py:func:`~driver.energy`. >>> energy('mollerplesset2') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.set_local_option('MOLLERPLESSET2', 'PRINT', 1) scf_wfn = scf_helper(lowername) # Need to semicanonicalize the ROHF orbitals if psi4.get_global_option('REFERENCE') == 'ROHF': scf_wfn.semicanonicalize() # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type(psi4.get_option('SCF', 'SCF_TYPE'), scf_wfn) #psi4.set_legacy_wavefunction(scf_wfn) returnvalue = psi4.plugin('mollerplesset2.so', scf_wfn) return returnvalue
def run_paralleldf(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that paralleldf can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('paralleldf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here #psi4.set_global_option('BASIS', 'sto-3g') psi4.set_local_option('MYPLUGIN', 'PRINT', 1) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = driver.scf_helper(name, **kwargs) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins paralleldf_wfn = psi4.plugin('paralleldf.so', ref_wfn) return paralleldf_wfn
def run_v2rdm_casscf(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that v2rdm_casscf can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('v2rdm_casscf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['SCF', 'DF_INTS_IO']) psi4.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # Your plugin's psi4 run sequence goes here scf_wfn = scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.get_option("V2RDM_CASSCF", "RESTART_FROM_CHECKPOINT_FILE") # todo PSIF_V2RDM_CHECKPOINT should be definied in psifiles.h if (filename != ""): molname = psi4.wavefunction().molecule().name() p4util.copy_file_to_scratch(filename, 'psi', molname, 269, False) returnvalue = psi4.plugin('v2rdm_casscf.so', scf_wfn) #psi4.set_variable('CURRENT ENERGY', returnvalue) return psi4.get_variable('CURRENT ENERGY')
def run_plugin_fragment(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that plugin_fragment can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('plugin_fragment') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.plugin('plugin_fragment.so')
def run_cis(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that cis can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('cis') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.set_local_option('CIS', 'PRINT', 1) scf_helper(name, **kwargs) returnvalue = psi4.plugin('cis.so') psi4.set_variable('CURRENT ENERGY', returnvalue)
def run_fasnocis(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that OCDFT can be called via :py:func:`~driver.energy`. >>> energy('fasnocis') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Run OCDFT psi4.set_local_option('CDFT','METHOD','FASNOCIS') returnvalue = psi4.plugin('cdft.so') return returnvalue
def run_plugin_mp2(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that mollerplesset2 can be called via :py:func:`~driver.energy`. >>> energy('mollerplesset2') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.set_local_option('MOLLERPLESSET2', 'PRINT', 1) psi4.scf() returnvalue = psi4.plugin('mollerplesset2.so') return returnvalue
def run_dpd_unit_test(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that dpd_unit_test can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('dpd_unit_test') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.set_global_option('BASIS', 'sto-3g') psi4.set_local_option('DPD_UNIT_TEST', 'PRINT', 1) scf_helper(name, **kwargs) returnvalue = psi4.plugin('dpd_unit_test.so') psi4.set_variable('CURRENT ENERGY', returnvalue)
def run_main(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that main can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('main') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here # psi4.set_global_option('BASIS', 'sto-3g') # psi4.set_local_option('MAIN', 'PRINT', 1) scf_helper(name, **kwargs) returnvalue = psi4.plugin('main.so') psi4.set_variable('CURRENT ENERGY', returnvalue)
def sherrill_gold_standard(name='mp2', **kwargs): r"""Function to call the quantum chemical method known as 'Gold Standard' in the Sherrill group. Uses :py:func:`~wrappers.complete_basis_set` to evaluate the following expression. Two-point extrapolation of the correlation energy performed according to :py:func:`~wrappers.corl_xtpl_helgaker_2`. .. math:: E_{total}^{\text{Au\_std}} = E_{total,\; \text{SCF}}^{\text{aug-cc-pVQZ}} \; + E_{corl,\; \text{MP2}}^{\text{aug-cc-pV[TQ]Z}} \; + \delta_{\text{MP2}}^{\text{CCSD(T)}}\big\vert_{\text{aug-cc-pVTZ}} >>> # [1] single-point energy by this composite method >>> energy('sherrill_gold_standard') >>> # [2] finite-difference geometry optimization >>> optimize('sherrill_gold_standard') >>> # [3] finite-difference geometry optimization, overwriting some pre-defined sherrill_gold_standard options >>> optimize('sherrill_gold_standard', corl_basis='cc-pV[DT]Z', delta_basis='3-21g') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) if not ('func_cbs' in kwargs): kwargs['func_cbs'] = energy if not ('scf_basis' in kwargs): kwargs['scf_basis'] = 'aug-cc-pVQZ' if not ('scf_scheme' in kwargs): kwargs['scf_scheme'] = highest_1 if not ('corl_wfn' in kwargs): kwargs['corl_wfn'] = 'mp2' name = 'mp2' if not ('corl_basis' in kwargs): kwargs['corl_basis'] = 'aug-cc-pV[TQ]Z' if not ('corl_scheme' in kwargs): kwargs['corl_scheme'] = corl_xtpl_helgaker_2 if not ('delta_wfn' in kwargs): kwargs['delta_wfn'] = 'ccsd(t)' if not ('delta_wfn_lesser' in kwargs): kwargs['delta_wfn_lesser'] = 'mp2' if not ('delta_basis' in kwargs): kwargs['delta_basis'] = 'aug-cc-pVTZ' if not ('delta_scheme' in kwargs): kwargs['delta_scheme'] = highest_1 return cbs(name, **kwargs)
def run_dfdcft(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that dfdcft can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('dfdcft') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # psi4.set_local_option('SCF', 'REFERENCE', 'UHF') # psi4.set_local_option('DFDCFT', 'REFERENCE', 'UHF') # Your plugin's psi4 run sequence goes here scf_helper(name, **kwargs) returnvalue = psi4.plugin('dfdcft.so') psi4.set_variable('CURRENT ENERGY', returnvalue)
def run_fvno(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that fvno can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('fvno') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here if ('wfn' in kwargs): if (kwargs['wfn'] == 'ccsd'): psi4.set_global_option('WFN', 'CCSD') elif (kwargs['wfn'] == 'ccsd(t)'): psi4.set_global_option('WFN', 'CCSD_T') scf_helper(name, **kwargs) psi4.transqt2() returnvalue = psi4.plugin('fvno.so')
def run_v2rdm_casscf(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that v2rdm_casscf can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('v2rdm_casscf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState( ['SCF', 'DF_INTS_IO']) psi4.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # Your plugin's psi4 run sequence goes here ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = driver.scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.get_option("V2RDM_CASSCF","RESTART_FROM_CHECKPOINT_FILE") # todo PSIF_V2RDM_CHECKPOINT should be definied in psifiles.h if ( filename != "" ): molname = ref_wfn.molecule().name() p4util.copy_file_to_scratch(filename,'psi',molname,269,False) # Ensure IWL files have been written when not using DF/CD scf_type = psi4.get_option('SCF', 'SCF_TYPE') if ( scf_type == 'PK' or scf_type == 'DIRECT' ): proc_util.check_iwl_file_from_scf_type(psi4.get_option('SCF', 'SCF_TYPE'), ref_wfn) returnvalue = psi4.plugin('v2rdm_casscf.so', ref_wfn) #psi4.set_variable('CURRENT ENERGY', returnvalue) #return psi4.get_variable('CURRENT ENERGY') return returnvalue
def run_aomp2(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that aomp2 can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('aomp2') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = driver.scf_helper(name, **kwargs) # Your plugin's psi4 run sequence goes here #psi4.set_local_option('AOMP2', 'PRINT', 1) #scf_helper(name, **kwargs) returnvalue = psi4.plugin("aomp2.so", ref_wfn) #psi4.set_variable('CURRENT ENERGY', returnvalue) return returnvalue
def run_ccambit(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that ccambit can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('ccambit') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here if ('wfn' in kwargs): if (kwargs['wfn'] == 'ccsd'): psi4.set_global_option('WFN', 'CCSD') elif (kwargs['wfn'] == 'ccsd(t)'): psi4.set_global_option('WFN', 'CCSD_T') scf_wfn = kwargs.get('ref_wfn', None) if scf_wfn is None: scf_wfn = driver.scf_helper(name, **kwargs) check_iwl_file_from_scf_type(psi4.get_option('SCF', 'SCF_TYPE'), scf_wfn) return psi4.plugin('ccambit.so', scf_wfn)
def allen_focal_point(name='mp2', **kwargs): r"""Function to call Wes Allen-style Focal Point Analysis. JCP 127 014306. Uses :py:func:`~wrappers.complete_basis_set` to evaluate the following expression. SCF employs a three-point extrapolation according to :py:func:`~wrappers.scf_xtpl_helgaker_3`. MP2, CCSD, and CCSD(T) employ two-point extrapolation performed according to :py:func:`~wrappers.corl_xtpl_helgaker_2`. CCSDT and CCSDT(Q) are plain deltas. This wrapper requires :ref:`Kallay's MRCC code <sec:mrcc>`. .. math:: E_{total}^{\text{FPA}} = E_{total,\; \text{SCF}}^{\text{cc-pV[Q56]Z}} \; + E_{corl,\; \text{MP2}}^{\text{cc-pV[56]Z}} \; + \delta_{\text{MP2}}^{\text{CCSD}}\big\vert_{\text{cc-pV[56]Z}} \; + \delta_{\text{CCSD}}^{\text{CCSD(T)}}\big\vert_{\text{cc-pV[56]Z}} \; + \delta_{\text{CCSD(T)}}^{\text{CCSDT}}\big\vert_{\text{cc-pVTZ}} \; + \delta_{\text{CCSDT}}^{\text{CCSDT(Q)}}\big\vert_{\text{cc-pVDZ}} >>> # [1] single-point energy by this composite method >>> energy('allen_focal_point') >>> # [2] finite-difference geometry optimization embarrasingly parallel >>> optimize('allen_focal_point', mode='sow') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) if not ('func_cbs' in kwargs): kwargs['func_cbs'] = energy # SCF if not ('scf_basis' in kwargs): kwargs['scf_basis'] = 'cc-pV[Q56]Z' if not ('scf_scheme' in kwargs): kwargs['scf_scheme'] = scf_xtpl_helgaker_3 # delta MP2 - SCF if not ('corl_wfn' in kwargs): kwargs['corl_wfn'] = 'mp2' name = 'mp2' if not ('corl_basis' in kwargs): kwargs['corl_basis'] = 'cc-pV[56]Z' if not ('corl_scheme' in kwargs): kwargs['corl_scheme'] = corl_xtpl_helgaker_2 # delta CCSD - MP2 if not ('delta_wfn' in kwargs): kwargs['delta_wfn'] = 'mrccsd' if not ('delta_wfn_lesser' in kwargs): kwargs['delta_wfn_lesser'] = 'mp2' if not ('delta_basis' in kwargs): kwargs['delta_basis'] = 'cc-pV[56]Z' if not ('delta_scheme' in kwargs): kwargs['delta_scheme'] = corl_xtpl_helgaker_2 # delta CCSD(T) - CCSD if not ('delta2_wfn' in kwargs): kwargs['delta2_wfn'] = 'mrccsd(t)' if not ('delta2_wfn_lesser' in kwargs): kwargs['delta2_wfn_lesser'] = 'mrccsd' if not ('delta2_basis' in kwargs): kwargs['delta2_basis'] = 'cc-pV[56]Z' if not ('delta2_scheme' in kwargs): kwargs['delta2_scheme'] = corl_xtpl_helgaker_2 # delta CCSDT - CCSD(T) if not ('delta3_wfn' in kwargs): kwargs['delta3_wfn'] = 'mrccsdt' if not ('delta3_wfn_lesser' in kwargs): kwargs['delta3_wfn_lesser'] = 'mrccsd(t)' if not ('delta3_basis' in kwargs): kwargs['delta3_basis'] = 'cc-pVTZ' if not ('delta3_scheme' in kwargs): kwargs['delta3_scheme'] = highest_1 # delta CCSDT(Q) - CCSDT if not ('delta4_wfn' in kwargs): kwargs['delta4_wfn'] = 'mrccsdt(q)' if not ('delta4_wfn_lesser' in kwargs): kwargs['delta4_wfn_lesser'] = 'mrccsdt' if not ('delta4_basis' in kwargs): kwargs['delta4_basis'] = 'cc-pVDZ' if not ('delta4_scheme' in kwargs): kwargs['delta4_scheme'] = highest_1 return cbs(name, **kwargs)
def _nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. Parameters ---------- func : python function Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian. method_string : str Lowername to be passed to function molecule : psi4.Molecule (default: Global Molecule) Molecule to use in all computations return_wfn : bool (default: False) Return a wavefunction or not bsse_type : str or list (default: None, this function is not called) Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. max_nbody : int Maximum n-body to compute, cannot exceede the number of fragments in the moleucle ptype : str Type of the procedure passed in return_total_data : bool (default: False) If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data Returns ------- data : return type of func The interaction data wfn : psi4.Wavefunction (optional) A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains Notes ----- This is a generalized univeral function for compute interaction quantities. Examples -------- """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() psi4.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError( """N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError( "N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # Flip this off for now, needs more testing # If we are doing CP lets save them integrals #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # # Set to save RI integrals for repeated full-basis computations # ri_ints_io = psi4.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # psi4.set_global_option('DF_INTS_IO', 'SAVE') # psioh = psi4.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) > 1: bsse_str = str(bsse_type_list) psi4.print_out("\n\n") psi4.print_out(" ===> N-Body Interaction Abacus <===\n") psi4.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x: set() for x in nbody_range} nocp_compute_list = {x: set() for x in nbody_range} vmfc_compute_list = {x: set() for x in nbody_range} vmfc_level_list = {x: set() for x in nbody_range } # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add((x, basis_tuple)) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add((x, x)) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add(combo_tuple) vmfc_level_list[len(basis_tuple)].add(combo_tuple) # Build a comprehensive compute_range compute_list = {x: set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] psi4.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = { frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag + 1) } start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): psi4.print_out( "\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) print("\n ==> N-Body: Now computing %d-body complexes <==\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): psi4.print_out( "\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = psi4.get_variable("CURRENT ENERGY") psi4.print_out( "\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) # Flip this off for now, needs more testing #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # psi4.set_global_option('DF_INTS_IO', 'LOAD') psi4.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_body_dict = None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ( (-1)**(n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[ max_nbody] - cp_energy_body_dict[1] psi4.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) psi4.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1)**(n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[ max_nbody] - nocp_energy_body_dict[1] psi4.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) psi4.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable( var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[ max_nbody] - vmfc_energy_body_dict[1] psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) psi4.set_variable( 'Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable( var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError( "N-Body Wrapper: Invalid return type. Should never be here, please post this error on github." ) # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape) ret_ptype_view = np.asarray(final_ptype) ret_ptype_view[:] = np_final_ptype else: ret_ptype = ret_energy # Build and set a wavefunction wfn = psi4.new_wavefunction(molecule, 'sto-3g') wfn.nbody_energy = energies_dict wfn.nbody_ptype = ptype_dict wfn.nbody_body_energy = energy_body_dict wfn.nbody_body_ptype = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) psi4.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def _nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. Parameters ---------- func : python function Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian. method_string : str Lowername to be passed to function molecule : psi4.Molecule (default: Global Molecule) Molecule to use in all computations return_wfn : bool (default: False) Return a wavefunction or not bsse_type : str or list (default: None, this function is not called) Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. max_nbody : int Maximum n-body to compute, cannot exceede the number of fragments in the moleucle ptype : str Type of the procedure passed in return_total_data : bool (default: False) If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data Returns ------- data : return type of func The interaction data wfn : psi4.Wavefunction (optional) A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains Notes ----- This is a generalized univeral function for compute interaction quantities. Examples -------- """ ### ==> Parse some kwargs <== kwargs = p4util.kwargs_lower(kwargs) return_wfn = kwargs.pop('return_wfn', False) ptype = kwargs.pop('ptype', None) return_total_data = kwargs.pop('return_total_data', False) molecule = kwargs.pop('molecule', psi4.get_active_molecule()) molecule.update_geometry() psi4.clean_variables() if ptype not in ['energy', 'gradient', 'hessian']: raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % ptype) # Figure out BSSE types do_cp = False do_nocp = False do_vmfc = False return_method = False # Must be passed bsse_type bsse_type_list = kwargs.pop('bsse_type') if bsse_type_list is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(bsse_type_list, list): bsse_type_list = [bsse_type_list] for num, btype in enumerate(bsse_type_list): if btype.lower() == 'cp': do_cp = True if (num == 0): return_method = 'cp' elif btype.lower() == 'nocp': do_nocp = True if (num == 0): return_method = 'nocp' elif btype.lower() == 'vmfc': do_vmfc = True if (num == 0): return_method = 'vmfc' else: raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) max_nbody = kwargs.get('max_nbody', -1) max_frag = molecule.nfragments() if max_nbody == -1: max_nbody = molecule.nfragments() else: max_nbody = min(max_nbody, max_frag) # What levels do we need? nbody_range = range(1, max_nbody + 1) fragment_range = range(1, max_frag + 1) # If we are doing CP lets save them integrals if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): # Set to save RI integrals for repeated full-basis computations ri_ints_io = psi4.get_global_option('DF_INTS_IO') # inquire if above at all applies to dfmp2 or just scf psi4.set_global_option('DF_INTS_IO', 'SAVE') psioh = psi4.IOManager.shared_object() psioh.set_specific_retention(97, True) bsse_str = bsse_type_list[0] if len(bsse_type_list) >1: bsse_str = str(bsse_type_list) psi4.print_out("\n\n") psi4.print_out(" ===> N-Body Interaction Abacus <===\n") psi4.print_out(" BSSE Treatment: %s\n" % bsse_str) cp_compute_list = {x:set() for x in nbody_range} nocp_compute_list = {x:set() for x in nbody_range} vmfc_compute_list = {x:set() for x in nbody_range} vmfc_level_list = {x:set() for x in nbody_range} # Need to sum something slightly different # Build up compute sets if do_cp: # Everything is in dimer basis basis_tuple = tuple(fragment_range) for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): cp_compute_list[nbody].add( (x, basis_tuple) ) if do_nocp: # Everything in monomer basis for nbody in nbody_range: for x in it.combinations(fragment_range, nbody): nocp_compute_list[nbody].add( (x, x) ) if do_vmfc: # Like a CP for all combinations of pairs or greater for nbody in nbody_range: for cp_combos in it.combinations(fragment_range, nbody): basis_tuple = tuple(cp_combos) for interior_nbody in nbody_range: for x in it.combinations(cp_combos, interior_nbody): combo_tuple = (x, basis_tuple) vmfc_compute_list[interior_nbody].add( combo_tuple ) vmfc_level_list[len(basis_tuple)].add( combo_tuple ) # Build a comprehensive compute_range compute_list = {x:set() for x in nbody_range} for n in nbody_range: compute_list[n] |= cp_compute_list[n] compute_list[n] |= nocp_compute_list[n] compute_list[n] |= vmfc_compute_list[n] psi4.print_out(" Number of %d-body computations: %d\n" % (n, len(compute_list[n]))) # Build size and slices dictionaries fragment_size_dict = {frag: molecule.extract_subsets(frag).natom() for frag in range(1, max_frag+1)} start = 0 fragment_slice_dict = {} for k, v in fragment_size_dict.items(): fragment_slice_dict[k] = slice(start, start + v) start += v molecule_total_atoms = sum(fragment_size_dict.values()) # Now compute the energies energies_dict = {} ptype_dict = {} for n in compute_list.keys(): psi4.print_out("\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) print("\n ==> N-Body: Now computing %d-body complexes <==\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): psi4.print_out("\n N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" % (num + 1, total, str(pair[0]), str(pair[1]))) ghost = list(set(pair[1]) - set(pair[0])) current_mol = molecule.extract_subsets(list(pair[0]), ghost) ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs) energies_dict[pair] = psi4.get_variable("CURRENT ENERGY") psi4.print_out("\n N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % (str(pair[0]), str(pair[1]), energies_dict[pair])) if 'cp' in bsse_type_list and (len(bsse_type_list) == 1): psi4.set_global_option('DF_INTS_IO', 'LOAD') psi4.clean() # Final dictionaries cp_energy_by_level = {n: 0.0 for n in nbody_range} nocp_energy_by_level = {n: 0.0 for n in nbody_range} cp_energy_body_dict = {n: 0.0 for n in nbody_range} nocp_energy_body_dict = {n: 0.0 for n in nbody_range} vmfc_energy_body_dict = {n: 0.0 for n in nbody_range} # Build out ptype dictionaries if needed if ptype != 'energy': if ptype == 'gradient': arr_shape = (molecule_total_atoms, 3) elif ptype == 'hessian': arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3) else: raise KeyError("N-Body: ptype '%s' not recognized" % ptype) cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range} cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range} else: cp_ptype_by_level, cp_ptype_body_dict = None, None nocp_ptype_by_level, nocp_ptype_body_dict = None, None vmfc_ptype_by_level= None # Sum up all of the levels for n in nbody_range: # Energy cp_energy_by_level[n] = sum(energies_dict[v] for v in cp_compute_list[n]) nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n]) # Special vmfc case if n > 1: vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1] for tup in vmfc_level_list[n]: vmfc_energy_body_dict[n] += ((-1) ** (n - len(tup[0]))) * energies_dict[tup] # Do ptype if ptype != 'energy': _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n], fragment_slice_dict, fragment_size_dict, cp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n], fragment_slice_dict, fragment_size_dict, nocp_ptype_by_level[n]) _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n], fragment_slice_dict, fragment_size_dict, vmfc_ptype_by_level[n], vmfc=True) # Compute cp energy and ptype if do_cp: for n in nbody_range: if n == max_frag: cp_energy_body_dict[n] = cp_energy_by_level[n] if ptype != 'energy': cp_ptype_body_dict[n][:] = cp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = cp_energy_by_level[k] cp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = cp_ptype_by_level[k] cp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)") cp_interaction_energy = cp_energy_body_dict[max_nbody] - cp_energy_body_dict[1] psi4.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) psi4.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1]) # Compute nocp energy and ptype if do_nocp: for n in nbody_range: if n == max_frag: nocp_energy_body_dict[n] = nocp_energy_by_level[n] if ptype != 'energy': nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n] continue for k in range(1, n + 1): take_nk = nCr(max_frag - k - 1, n - k) sign = ((-1) ** (n - k)) value = nocp_energy_by_level[k] nocp_energy_body_dict[n] += take_nk * sign * value if ptype != 'energy': value = nocp_ptype_by_level[k] nocp_ptype_body_dict[n] += take_nk * sign * value _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)") nocp_interaction_energy = nocp_energy_body_dict[max_nbody] - nocp_energy_body_dict[1] psi4.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) psi4.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy) for n in nbody_range[1:]: var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1]) # Compute vmfc energy and ptype if do_vmfc: _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)") vmfc_interaction_energy = vmfc_energy_body_dict[max_nbody] - vmfc_energy_body_dict[1] psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) psi4.set_variable('Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy) for n in nbody_range[1:]: var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n psi4.set_variable(var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1]) if return_method == 'cp': ptype_body_dict = cp_ptype_body_dict energy_body_dict = cp_energy_body_dict elif return_method == 'nocp': ptype_body_dict = nocp_ptype_body_dict energy_body_dict = nocp_energy_body_dict elif return_method == 'vmfc': ptype_body_dict = vmfc_ptype_body_dict energy_body_dict = vmfc_energy_body_dict else: raise ValidationError("N-Body Wrapper: Invalid return type. Should never be here, please post this error on github.") # Figure out and build return types if return_total_data: ret_energy = energy_body_dict[max_nbody] else: ret_energy = energy_body_dict[max_nbody] ret_energy -= energy_body_dict[1] if ptype != 'energy': if return_total_data: np_final_ptype = ptype_body_dict[max_nbody].copy() else: np_final_ptype = ptype_body_dict[max_nbody].copy() np_final_ptype -= ptype_body_dict[1] ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape) ret_ptype_view = np.asarray(final_ptype) ret_ptype_view[:] = np_final_ptype else: ret_ptype = ret_energy # Build and set a wavefunction wfn = psi4.new_wavefunction(molecule, 'sto-3g') wfn.nbody_energy = energies_dict wfn.nbody_ptype = ptype_dict wfn.nbody_body_energy = energy_body_dict wfn.nbody_body_ptype = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) psi4.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def ip_fitting(molecule, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = kwargs.get('omega_tolerance', 1.0E-3) # By default, do up to twenty iterations maxiter = kwargs.get('maxiter', 20) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # How many electrons are there? N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out("""\n\t==> IP Fitting SCF: Burn-in <==\n""") E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na Nb1 = Nb if H**O > 0: Na1 = Na1 - 1 else: Nb1 = Nb1 - 1 charge1 = charge0 + 1 mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint psi4.set_global_option('DFT_OMEGA', omega_r) # Neutral if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n""") E0r, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOr = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n""") E1r = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r kIPr = -E_HOMOr delta_r = IPr - kIPr if IPr > kIPr: message = ( """\n***IP Fitting Error: Right Omega limit should have kIP > IP""" ) raise ValidationError(message) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS", "READ") # Left endpoint psi4.set_global_option('DFT_OMEGA', omega_l) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n""") E0l, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOl = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n""") E1l = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: message = ( """\n***IP Fitting Error: Left Omega limit should have kIP < IP""") raise ValidationError(message) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0 repeat_r = 0 step = 0 while True: step = step + 1 # Regula Falsi (modified) if repeat_l > 1: delta_l = delta_l / 2.0 if repeat_r > 1: delta_r = delta_r / 2.0 omega = -(omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l psi4.set_global_option('DFT_OMEGA', omega) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out( """\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n""" % omega) E0, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out( """\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n""" % omega) E1 = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IP = E1 - E0 kIP = -E_HOMO delta = IP - kIP if kIP > IP: omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0 repeat_l = repeat_l + 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0 repeat_r = repeat_r + 1 omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True break # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.IO.set_default_namespace("") psi4.print_out("""\n\t==> IP Fitting Results <==\n\n""") psi4.print_out("""\t => Occupation Determination <= \n\n""") psi4.print_out("""\t %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out("""\t Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) psi4.print_out("""\t Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) psi4.print_out("""\t => Regula Falsi Iterations <=\n\n""") psi4.print_out("""\t%3s %11s %14s %14s %14s %s\n""" % ('N', 'Omega', 'IP', 'kIP', 'Delta', 'Type')) for k in range(len(omegas)): psi4.print_out( """\t%3d %11.3E %14.6E %14.6E %14.6E %s\n""" % (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k])) if converged: psi4.print_out("""\n\tIP Fitting Converged\n""") psi4.print_out("""\tFinal omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) psi4.print_out( """\n\t"M,I. does the dying. Fleet just does the flying."\n""") psi4.print_out("""\t\t\t-Starship Troopers\n""") else: psi4.print_out("""\n\tIP Fitting did not converge!\n""") psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)
def frac_traverse(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 # By default, the multiplicity of the cation/anion are mult0 + 1 # These are overridden with the cation_mult and anion_mult kwargs multp = kwargs.get('cation_mult', mult0 + 1) multm = kwargs.get('anion_mult', mult0 + 1) # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons HOMO_occs = kwargs.get( 'HOMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) LUMO_occs = kwargs.get( 'LUMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha Z = 0 for A in range(molecule.natom()): Z += molecule.Z(A) Z -= charge0 H**O = kwargs.get('H**O', (Z / 2 + 1 if (Z % 2) else Z / 2)) LUMO = kwargs.get('LUMO', H**O + 1) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, use the neutral orbitals as a guess for the anion neutral_guess = kwargs.get('neutral_guess', True) # By default, burn-in with UHF first, if UKS hf_guess = False if psi4.get_global_option('REFERENCE') == 'UKS': hf_guess = kwargs.get('hf_guess', True) # By default, re-guess at each N continuous_guess = kwargs.get('continuous_guess', False) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' # => Traverse <= # occs = [] energies = [] potentials = [] convs = [] # => Run the neutral for its orbitals, if requested <= # old_df_ints_io = psi4.get_global_option("DF_INTS_IO") psi4.set_global_option("DF_INTS_IO", "SAVE") old_guess = psi4.get_global_option("GUESS") if (neutral_guess): if (hf_guess): psi4.set_global_option("REFERENCE", "UHF") energy('scf') psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the anion first <= # molecule.set_molecular_charge(chargem) molecule.set_multiplicity(multm) # => Burn the anion in with hf, if requested <= # if hf_guess: psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) psi4.set_global_option("FRAC_LOAD", False) for occ in LUMO_occs: psi4.set_global_option("FRAC_OCC", [LUMO]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(LUMO) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(LUMO) - 1]) occs.append(occ) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the neutral next <= # molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) # Burn the neutral in with hf, if requested <= # if not continuous_guess: psi4.set_global_option("GUESS", old_guess) if hf_guess: psi4.set_global_option("FRAC_START", 0) psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_LOAD", False) psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) for occ in HOMO_occs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(H**O) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(H**O) - 1]) occs.append(occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("DF_INTS_IO", old_df_ints_io) # => Print the results out <= # E = {} psi4.print_out( """\n ==> Fractional Occupation Traverse Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] psi4.print_out('\n\t"You trying to be a hero Watkins?"\n') psi4.print_out('\t"Just trying to kill some bugs sir!"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def frac_nuke(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons foccs = kwargs.get('foccs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) charge = charge0 mult = mult0 # By default, nuke all the electrons Nmin = 0 if ('nmax' in kwargs): Nmin = N - int(kwargs['nmax']) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' stats_filename = root + '.stats.dat' # => Traverse <= # psi4.set_global_option("DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("GUESS", "READ") # Set the next charge/mult molecule.set_molecular_charge(charge) molecule.set_multiplicity(mult) # Determine H**O print('DGAS: What ref should this point to?') #ref = psi4.legacy_wavefunction() eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (Nintegral - 1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "NONE") # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] psi4.print_out('\n') psi4.print_out("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: psi4.print_out(line) psi4.print_out( '\n\t"You shoot a nuke down a bug hole, you got a lot of dead bugs"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) fh.close() fh = open(stats_filename, 'w') fh.write("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def ip_fitting(molecule, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = kwargs.get('omega_tolerance', 1.0E-3) # By default, do up to twenty iterations maxiter = kwargs.get('maxiter', 20) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # How many electrons are there? N = 0 for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out("""\n\t==> IP Fitting SCF: Burn-in <==\n""") E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na; Nb1 = Nb; if H**O > 0: Na1 = Na1 - 1; else: Nb1 = Nb1 - 1; charge1 = charge0 + 1; mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint psi4.set_global_option('DFT_OMEGA', omega_r) # Neutral if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n""") E0r, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0; if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOr = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n""") E1r = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r; kIPr = -E_HOMOr; delta_r = IPr - kIPr; if IPr > kIPr: message = ("""\n***IP Fitting Error: Right Omega limit should have kIP > IP""") raise ValidationError(message) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS", "READ") # Left endpoint psi4.set_global_option('DFT_OMEGA', omega_l) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n""") E0l, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b E_HOMOl = E_HOMO psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n""") E1l = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: message = ("""\n***IP Fitting Error: Left Omega limit should have kIP < IP""") raise ValidationError(message) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0 repeat_r = 0 step = 0 while True: step = step + 1 # Regula Falsi (modified) if repeat_l > 1: delta_l = delta_l / 2.0 if repeat_r > 1: delta_r = delta_r / 2.0 omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l psi4.set_global_option('DFT_OMEGA', omega) # Neutral psi4.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.print_out("""\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n""" % omega) E0, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() E_HOMO = 0.0 if Nb == 0: E_HOMO = eps_a[int(Na - 1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: E_HOMO = E_a else: E_HOMO = E_b psi4.IO.change_file_namespace(180, "ot", "neutral") # Cation psi4.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) psi4.print_out("""\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n""" % omega) E1 = energy('scf', molecule=molecule, **kwargs) psi4.IO.change_file_namespace(180, "ot", "cation") IP = E1 - E0 kIP = -E_HOMO delta = IP - kIP if kIP > IP: omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0 repeat_l = repeat_l + 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0; repeat_r = repeat_r + 1 omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True break # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) psi4.IO.set_default_namespace("") psi4.print_out("""\n\t==> IP Fitting Results <==\n\n""") psi4.print_out("""\t => Occupation Determination <= \n\n""") psi4.print_out("""\t %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out("""\t Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) psi4.print_out("""\t Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) psi4.print_out("""\t => Regula Falsi Iterations <=\n\n""") psi4.print_out("""\t%3s %11s %14s %14s %14s %s\n""" % ('N','Omega','IP','kIP','Delta','Type')) for k in range(len(omegas)): psi4.print_out("""\t%3d %11.3E %14.6E %14.6E %14.6E %s\n""" % (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k])) if converged: psi4.print_out("""\n\tIP Fitting Converged\n""") psi4.print_out("""\tFinal omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) psi4.print_out("""\n\t"M,I. does the dying. Fleet just does the flying."\n""") psi4.print_out("""\t\t\t-Starship Troopers\n""") else: psi4.print_out("""\n\tIP Fitting did not converge!\n""") psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)
def frac_traverse(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 # By default, the multiplicity of the cation/anion are mult0 + 1 # These are overridden with the cation_mult and anion_mult kwargs multp = kwargs.get('cation_mult', mult0 + 1) multm = kwargs.get('anion_mult', mult0 + 1) # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons HOMO_occs = kwargs.get('HOMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) LUMO_occs = kwargs.get('LUMO_occs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha Z = 0; for A in range(molecule.natom()): Z += molecule.Z(A) Z -= charge0 H**O = kwargs.get('H**O', (Z / 2 + 1 if (Z % 2) else Z / 2)) LUMO = kwargs.get('LUMO', H**O + 1) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, use the neutral orbitals as a guess for the anion neutral_guess = kwargs.get('neutral_guess', True) # By default, burn-in with UHF first, if UKS hf_guess = False if psi4.get_global_option('REFERENCE') == 'UKS': hf_guess = kwargs.get('hf_guess', True) # By default, re-guess at each N continuous_guess = kwargs.get('continuous_guess', False) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' # => Traverse <= # occs = [] energies = [] potentials = [] convs = [] # => Run the neutral for its orbitals, if requested <= # old_df_ints_io = psi4.get_global_option("DF_INTS_IO") psi4.set_global_option("DF_INTS_IO", "SAVE") old_guess = psi4.get_global_option("GUESS") if (neutral_guess): if (hf_guess): psi4.set_global_option("REFERENCE","UHF") energy('scf') psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the anion first <= # molecule.set_molecular_charge(chargem) molecule.set_multiplicity(multm) # => Burn the anion in with hf, if requested <= # if hf_guess: psi4.set_global_option("REFERENCE","UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE","UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) psi4.set_global_option("FRAC_LOAD", False) for occ in LUMO_occs: psi4.set_global_option("FRAC_OCC", [LUMO]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(LUMO) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(LUMO) - 1]) occs.append(occ) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") # => Run the neutral next <= # molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) # Burn the neutral in with hf, if requested <= # if not continuous_guess: psi4.set_global_option("GUESS", old_guess) if hf_guess: psi4.set_global_option("FRAC_START", 0) psi4.set_global_option("REFERENCE", "UHF") energy('scf', molecule=molecule, **kwargs) psi4.set_global_option("REFERENCE", "UKS") psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_LOAD", False) psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) for occ in HOMO_occs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps[int(H**O) - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-int(H**O) - 1]) occs.append(occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("GUESS", "READ") psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("DF_INTS_IO", old_df_ints_io) # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Traverse Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] psi4.print_out('\n\t"You trying to be a hero Watkins?"\n') psi4.print_out('\t"Just trying to kill some bugs sir!"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def frac_nuke(molecule, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # The molecule is required, and should be the neutral species molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() # By default, we start the frac procedure on the 25th iteration # when not reading a previous guess frac_start = kwargs.get('frac_start', 25) # By default, we occupy by tenths of electrons foccs = kwargs.get('foccs', [1.0, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, 0.0]) # By default, H**O and LUMO are both in alpha N = 0; for A in range(molecule.natom()): N += molecule.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1) / 2) Na = int(N - Nb) charge = charge0 mult = mult0 # By default, nuke all the electrons Nmin = 0; if ('nmax' in kwargs): Nmin = N - int(kwargs['nmax']) # By default, DIIS in FRAC (1.0 occupation is always DIIS'd) frac_diis = kwargs.get('frac_diis', True) # By default, drop the files to the molecule's name root = kwargs.get('filename', molecule.name()) traverse_filename = root + '.traverse.dat' stats_filename = root + '.stats.dat' # => Traverse <= # psi4.set_global_option("DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn= energy('scf', return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "LOAD") psi4.set_global_option("FRAC_START", frac_start) psi4.set_global_option("FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: psi4.set_global_option("FRAC_OCC", [H**O]) psi4.set_global_option("FRAC_VAL", [occ]) E, wfn = energy('scf', return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = psi4.get_variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) psi4.set_global_option("FRAC_START", 2) psi4.set_global_option("FRAC_LOAD", True) psi4.set_global_option("FRAC_DIIS", frac_diis) psi4.set_global_option("GUESS", "READ") # Set the next charge/mult molecule.set_molecular_charge(charge) molecule.set_multiplicity(mult) # Determine H**O print('DGAS: What ref should this point to?') #ref = psi4.legacy_wavefunction() eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append("""\t%6d %6d %6d %6d %6d %6d\n""" % (Nintegral-1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na = Na - 1 else: Nb = Nb - 1 charge = charge + 1 mult = Na - Nb + 1 psi4.set_global_option("DF_INTS_IO", "NONE") # => Print the results out <= # E = {} psi4.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") psi4.print_out("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): psi4.print_out("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] psi4.print_out('\n') psi4.print_out("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: psi4.print_out(line) psi4.print_out('\n\t"You shoot a nuke down a bug hole, you got a lot of dead bugs"\n') psi4.print_out('\t\t\t-Starship Troopers\n') # Drop the files out fh = open(traverse_filename, 'w') fh.write("""\t%-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write("""\t%11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) fh.close() fh = open(stats_filename, 'w') fh.write("""\t%6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) fh.close() # Properly, should clone molecule but since not returned and easy to unblemish, molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) return E
def ip_fitting(mol, omega_l, omega_r, **kwargs): kwargs = p4util.kwargs_lower(kwargs) # By default, zero the omega to 3 digits omega_tol = 1.0E-3; if (kwargs.has_key('omega_tolerance')): omega_tol = kwargs['omega_tolerance'] # By default, do up to twenty iterations maxiter = 20; if (kwargs.has_key('maxiter')): maxiter = kwargs['maxiter'] # By default, do not read previous 180 orbitals file read = False; read180 = '' if (kwargs.has_key('read')): read = True; read180 = kwargs['read'] # The molecule is required, and should be the neutral species mol.update_geometry() activate(mol) charge0 = mol.molecular_charge() mult0 = mol.multiplicity() # How many electrons are there? N = 0; for A in range(mol.natom()): N += mol.Z(A) N -= charge0 N = int(N) Nb = int((N - mult0 + 1)/2) Na = int(N - Nb) # Work in the ot namespace for this procedure psi4.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if (read): psi4.set_global_option("GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) old_guess = psi4.get_global_option("GUESS") psi4.set_global_option("DF_INTS_IO", "SAVE") psi4.print_out('\n\t==> IP Fitting SCF: Burn-in <==\n') energy('scf') psi4.set_global_option("DF_INTS_IO", "LOAD") # Determine H**O, to determine mult1 ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() if (Na == Nb): H**O = -Nb elif (Nb == 0): H**O = Na else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): H**O = Na else: H**O = -Nb Na1 = Na; Nb1 = Nb; if (H**O > 0): Na1 = Na1-1; else: Nb1 = Nb1-1; charge1 = charge0 + 1; mult1 = Na1 - Nb1 + 1 omegas = []; E0s = []; E1s = []; kIPs = []; IPs = []; types = []; # Right endpoint psi4.set_global_option('DFT_OMEGA',omega_r) # Neutral if (read): psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Right Endpoint <==\n') E0r = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; E_HOMOr = E_HOMO; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation if (read): psi4.set_global_option("GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Right Endpoint <==\n') E1r = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IPr = E1r - E0r; kIPr = -E_HOMOr; delta_r = IPr - kIPr; if (IPr > kIPr): psi4.print_out('\n***IP Fitting Error: Right Omega limit should have kIP > IP') sys.exit(1) omegas.append(omega_r) types.append('Right Limit') E0s.append(E0r) E1s.append(E1r) IPs.append(IPr) kIPs.append(kIPr) # Use previous orbitals from here out psi4.set_global_option("GUESS","READ") # Left endpoint psi4.set_global_option('DFT_OMEGA',omega_l) # Neutral psi4.IO.change_file_namespace(180,"neutral","ot") mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Left Endpoint <==\n') E0l = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; E_HOMOl = E_HOMO; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation psi4.IO.change_file_namespace(180,"cation","ot") mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Left Endpoint <==\n') E1l = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IPl = E1l - E0l; kIPl = -E_HOMOl; delta_l = IPl - kIPl; if (IPl < kIPl): psi4.print_out('\n***IP Fitting Error: Left Omega limit should have kIP < IP') sys.exit(1) omegas.append(omega_l) types.append('Left Limit') E0s.append(E0l) E1s.append(E1l) IPs.append(IPl) kIPs.append(kIPl) converged = False repeat_l = 0; repeat_r = 0; step = 0; while True: step = step + 1; # Regula Falsi (modified) if (repeat_l > 1): delta_l = delta_l / 2.0; if (repeat_r > 1): delta_r = delta_r / 2.0; omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l; psi4.set_global_option('DFT_OMEGA',omega) # Neutral psi4.IO.change_file_namespace(180,"neutral","ot") mol.set_molecular_charge(charge0) mol.set_multiplicity(mult0) psi4.print_out('\n\t==> IP Fitting SCF: Neutral, Omega = %11.3E <==\n' % omega) E0 = energy('scf') ref = psi4.wavefunction() eps_a = ref.epsilon_a() eps_b = ref.epsilon_b() E_HOMO = 0.0; if (Nb == 0): E_HOMO = eps_a[int(Na-1)] else: E_a = eps_a[int(Na - 1)] E_b = eps_b[int(Nb - 1)] if (E_a >= E_b): E_HOMO = E_a; else: E_HOMO = E_b; psi4.IO.change_file_namespace(180,"ot","neutral") # Cation psi4.IO.change_file_namespace(180,"cation","ot") mol.set_molecular_charge(charge1) mol.set_multiplicity(mult1) psi4.print_out('\n\t==> IP Fitting SCF: Cation, Omega = %11.3E <==\n' % omega) E1 = energy('scf') psi4.IO.change_file_namespace(180,"ot","cation") IP = E1 - E0; kIP = -E_HOMO; delta = IP - kIP; if (kIP > IP): omega_r = omega E0r = E0 E1r = E1 IPr = IP kIPr = kIP delta_r = delta repeat_r = 0; repeat_l = repeat_l + 1; else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0; repeat_r = repeat_r + 1; omegas.append(omega) types.append('Regula-Falsi') E0s.append(E0) E1s.append(E1) IPs.append(IP) kIPs.append(kIP) # Termination if (abs(omega_l - omega_r) < omega_tol or step > maxiter): converged = True; break psi4.IO.set_default_namespace("") psi4.print_out('\n\t==> IP Fitting Results <==\n\n') psi4.print_out('\t => Occupation Determination <= \n\n') psi4.print_out('\t %6s %6s %6s %6s %6s %6s\n' %('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) psi4.print_out('\t Neutral: %6d %6d %6d %6d %6d %6d\n' %(N, Na, Nb, charge0, mult0, H**O)) psi4.print_out('\t Cation: %6d %6d %6d %6d %6d\n\n' %(N-1, Na1, Nb1, charge1, mult1)) psi4.print_out('\t => Regula Falsi Iterations <=\n\n') psi4.print_out('\t%3s %11s %14s %14s %14s %s\n' % ('N','Omega','IP','kIP','Delta','Type')) for k in range(len(omegas)): psi4.print_out('\t%3d %11.3E %14.6E %14.6E %14.6E %s\n' % (k+1,omegas[k],IPs[k],kIPs[k],IPs[k] - kIPs[k], types[k])) if (converged): psi4.print_out('\n\tIP Fitting Converged\n') psi4.print_out('\tFinal omega = %14.6E\n' % ((omega_l + omega_r) / 2)) psi4.print_out('\n\t"M,I. does the dying. Fleet just does the flying."\n') psi4.print_out('\t\t\t-Starship Troopers\n') else: psi4.print_out('\n\tIP Fitting did not converge!\n') psi4.set_global_option("DF_INTS_IO", "NONE") psi4.set_global_option("GUESS", old_guess)