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.core.set_local_option('MYPLUGIN', 'PRINT', 1) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more print('Attention! This SCF may be density-fitted.') ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins cis_wfn = psi4.core.plugin('cis.so', ref_wfn) return cis_wfn
def run_mydft(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that mydft can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('mydft') """ #lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here scf_wfn = kwargs.get('ref_wfn', None) if scf_wfn is None: func = psi4.core.get_option('MYDFT', 'FUNCTIONAL') scf_molecule = kwargs.get('molecule', psi4.core.get_active_molecule()) base_wfn = psi4.core.Wavefunction.build( scf_molecule, psi4.core.get_global_option('BASIS')) scf_wfn = proc.scf_wavefunction_factory( func, base_wfn, psi4.core.get_option('SCF', 'REFERENCE')) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), scf_wfn) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins mydft_wfn = psi4.core.plugin('mydft.so', scf_wfn) return mydft_wfn
def run_cct3(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that cct3 can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('cct3') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['CCT3', 'CALC_TYPE']) # Your plugin's psi4 run sequence goes here if lowername == "cct3": pass elif lowername == "ccsd": psi4.core.set_local_option("CCT3", "CALC_TYPE", "CCSD") elif lowername == "cr-cc(2,3)": psi4.core.set_local_option("CCT3", "CALC_TYPE", "CR-CC") elif lowername == "ccsd_lct": # CCSDt psi4.core.set_local_option("CCT3", "CALC_TYPE", "CCSD3A") elif lowername == "cc(t;3)": psi4.core.set_local_option("CCT3", "CALC_TYPE", "CCT3") # 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 = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins cct3_wfn = psi4.core.plugin('cct3.so', ref_wfn) try: cct3_wfn.set_module("cct3") except AttributeError: pass # pre-July 2020 psi4 # Shove variables into global space for k, v in cct3_wfn.variables().items(): psi4.core.set_variable(k, v) optstash.restore() return cct3_wfn
def run_mcpdft(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that mcpdft can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('mcpdft') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') v2rdm_wfn = kwargs.get('ref_wfn', None) if v2rdm_wfn is None: raise ValidationError( """Error: %s requires a reference wave function (v2rdm-casscf).""" % name) psi4.core.set_variable("V2RDM TOTAL ENERGY", v2rdm_wfn.energy()) if ((psi4.core.get_option('MCPDFT', 'MCPDFT_METHOD') == '1DH_MCPDFT') or (psi4.core.get_option('MCPDFT', 'MCPDFT_METHOD') == 'LS1DH_MCPDFT')): proc.run_dfmp2('mp2', **kwargs) if ('WBLYP' == psi4.core.get_option('MCPDFT', 'MCPDFT_FUNCTIONAL')): func = 'BLYP' else: func = psi4.core.get_option('MCPDFT', 'MCPDFT_FUNCTIONAL') ref_molecule = kwargs.get('molecule', psi4.core.get_active_molecule()) base_wfn = psi4.core.Wavefunction.build( ref_molecule, psi4.core.get_global_option('BASIS')) ref_wfn = proc.scf_wavefunction_factory( func, base_wfn, psi4.core.get_option('MCPDFT', 'REFERENCE')) # push v2rdm-casscf orbitals onto reference for irrep in range(0, v2rdm_wfn.Ca().nirrep()): ref_wfn.Ca().nph[irrep][:, :] = v2rdm_wfn.Ca().nph[irrep][:, :] ref_wfn.Cb().nph[irrep][:, :] = v2rdm_wfn.Cb().nph[irrep][:, :] # push v2rdm-casscf energies onto reference for irrep in range(0, v2rdm_wfn.epsilon_a().nirrep()): ref_wfn.epsilon_a().nph[irrep][:] = v2rdm_wfn.epsilon_a().nph[irrep][:] ref_wfn.epsilon_b().nph[irrep][:] = v2rdm_wfn.epsilon_b().nph[irrep][:] # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins mcpdft_wfn = psi4.core.plugin('mcpdft.so', ref_wfn) return mcpdft_wfn
def run_scf_plug(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that scf_plug can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('scf_plug') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.core.set_local_option('MYPLUGIN', 'PRINT', 1) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more print('Attention! This SCF may be density-fitted.') ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD # proc_util.check_iwl_file_from_scf_type(psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # analytic derivatives do not work with scf_type df/cd scf_type = psi4.core.get_option('SCF', 'SCF_TYPE') if ( scf_type == 'CD' or scf_type == 'DF' ): raise ValidationError("""Error: analytic gradients not implemented for scf_type %s.""" % scf_type) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins scf_plug_wfn = psi4.core.plugin('scf_plug.so', ref_wfn) print(scf_plug_wfn) print(ref_wfn) derivobj = psi4.core.Deriv(scf_plug_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute() scf_plug_wfn.set_gradient(grad) return scf_plug_wfn
def run_rhf_dlmg2b(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that rhf_dlmg2b can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('rhf_dlmg2b') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.core.set_local_option('MYPLUGIN', 'PRINT', 1) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more print('Attention! This SCF may be density-fitted.') ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # Call the Psi4 plugin psi4.core.set_global_option('GUESS', 'READ') optstash = p4util.OptionsState(['E_CONVERGENCE']) psi4.core.set_global_option('E_CONVERGENCE', 1492.) # Please note that setting the reference wavefunction in this way is ONLY for plugins vac_wfn = psi4.core.plugin('rhf_dlmg2b.so', ref_wfn) # Vacuum wfn has been returned. Start solvent calc psi4.core.set_local_option('RHF_DLMG2B', 'eps', psi4.core.get_option('RHF_DLMG2B', 'eps_sol')) # If eps_sol set to zero, return vacuum wfn test_eps = psi4.core.get_option('RHF_DLMG2B', 'eps_sol') optstash.restore() if (test_eps == 0.0): print("Vac_wfn being returned now") return vac_wfn # Do solvent scf sol_wfn = psi4.core.plugin('rhf_dlmg2b.so', vac_wfn) kwargs['ref_wfn'] = sol_wfn return sol_wfn
def run_v2rdm_casscf_gradient(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. >>> gradient('v2rdm_casscf') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState( ['GLOBALS', 'DERTYPE'], ['V2RDM_CASSCF', 'OPTIMIZE_ORBITALS'], ['V2RDM_CASSCF', 'SEMICANONICALIZE_ORBITALS'], ['V2RDM_CASSCF', 'ORBOPT_ACTIVE_ACTIVE_ROTATIONS'], ['V2RDM_CASSCF', 'RESTART_FROM_CHECKPOINT_FILE'], ['V2RDM_CASSCF', 'WRITE_CHECKPOINT_FILE']) psi4.core.set_global_option('DERTYPE', 'FIRST') psi4.core.set_local_option("V2RDM_CASSCF", "OPTIMIZE_ORBITALS", True) psi4.core.set_local_option("V2RDM_CASSCF", "ORBOPT_ACTIVE_ACTIVE_ROTATIONS", True) psi4.core.set_local_option("V2RDM_CASSCF", "SEMICANONICALIZE_ORBITALS", False) psi4.core.set_local_option("V2RDM_CASSCF", "RESTART_FROM_CHECKPOINT_FILE", "DUMMY") psi4.core.set_local_option("V2RDM_CASSCF", "WRITE_CHECKPOINT_FILE", True) # analytic derivatives do not work with scf_type df/cd scf_type = psi4.core.get_option('SCF', 'SCF_TYPE') if (scf_type == 'CD' or scf_type == 'DF'): raise ValidationError( """Error: analytic v2RDM-CASSCF gradients not implemented for scf_type %s.""" % scf_type) v2rdm_wfn = run_v2rdm_casscf(name, **kwargs) derivobj = psi4.core.Deriv(v2rdm_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute() v2rdm_wfn.set_gradient(grad) optstash.restore() return v2rdm_wfn
def run_mean_field_cqed(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that mean_field_cqed can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('mean_field_cqed') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) mean_field_cqed_wfn = psi4.core.plugin('mean_field_cqed.so', ref_wfn) return mean_field_cqed_wfn
def run_fcidump(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that fcidump can be called via :py:func:`~driver.energy`. >>> energy('fcidump') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) proc_util.check_iwl_file_from_scf_type(psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) test_wfn = psi4.core.plugin('fcidump.so', ref_wfn) return test_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.core.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 = psi4.driver.scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.core.get_option("V2RDM_CASSCF", "RESTART_FROM_CHECKPOINT_FILE") # todo PSIF_V2RDM_CHECKPOINT should be definied in psifiles.h if (filename != "" and psi4.core.get_global_option('DERTYPE') != 'FIRST'): molname = psi4.wavefunction().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.core.get_option('SCF', 'SCF_TYPE') if (scf_type == 'PK' or scf_type == 'DIRECT'): proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) returnvalue = psi4.core.plugin('v2rdm_casscf.so', ref_wfn) optstash.restore() return returnvalue
def run_erpa(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that erpa can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('erpa') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['SCF', 'DF_INTS_IO']) # psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') erpa_corr_global = psi4.core.get_option('ERPA', 'ERPA_CORRELATION') erpa_corr_local = psi4.core.get_global_option('ERPA_CORRELATION') if not erpa_corr_global and not erpa_corr_local: nat_orbs_global = psi4.core.get_option('V2RDM_CASSCF', 'NAT_ORBS') nat_orbs_local = psi4.core.get_global_option('NAT_ORBS') if not nat_orbs_global and not nat_orbs_local: raise psi4.ValidationError( """ERPA requires that the V2RDM_CASSCF option "nat_orbs" be set to true.""" ) # Your plugin's psi4 run sequence goes here ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD scf_type = psi4.core.get_option('SCF', 'SCF_TYPE') if (scf_type == 'PK' or scf_type == 'DIRECT'): proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) returnvalue = psi4.core.plugin('erpa.so', ref_wfn) # optstash.restore() return returnvalue
def run_ugacc(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that ugacc can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('ugacc') """ 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.core.set_global_option('WFN', 'CCSD') elif (kwargs['wfn'] == 'ccsd(t)'): psi4.core.set_global_option('WFN', 'CCSD_T') scf_wfn = kwargs.get('ref_wfn', None) if scf_wfn is None: scf_wfn = psi4.driver.scf_helper(name, **kwargs) proc_util.check_iwl_file_from_scf_type(psi4.core.get_option('SCF', 'SCF_TYPE'), scf_wfn) return psi4.core.plugin('ugacc.so', scf_wfn)
def run_libresponse_psi4(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that libresponse_psi4 can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('libresponse_psi4') """ kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.core.set_local_option("MYPLUGIN", "PRINT", 1) # The response density is asymmetric; need to build generalized # J/K. proc_util.check_non_symmetric_jk_density("libresponse") # Disable symmetry, even for passed-in wavefunctions. molecule = kwargs.get("molecule", None) if molecule is None: molecule = psi4.core.get_active_molecule() molecule = disable_symmetry(molecule) kwargs["molecule"] = molecule # 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 = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option("SCF", "SCF_TYPE"), ref_wfn) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins libresponse_psi4_wfn = psi4.core.plugin("libresponse_psi4.so", ref_wfn) return libresponse_psi4_wfn
def run_ccreorg(name, **kwargs): """Function encoding sequence of PSI module and plugin calls so that ccreorg can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('ccreorg') """ 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.core.set_global_option('WFN', 'CCSD') elif (kwargs['wfn'] == 'ccsd(t)'): psi4.core.set_global_option('WFN', 'CCSD_T') scf_wfn = kwargs.get('ref_wfn', None) if scf_wfn is None: scf_wfn = psi4.driver.scf_helper(name, **kwargs) proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), scf_wfn) return psi4.core.plugin('ccreorg.so', scf_wfn)
def run_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('forte') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.core.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 = psi4.driver.scf_helper(name, **kwargs) if ('DF' in psi4.core.get_option('FORTE', 'INT_TYPE')): aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), 'DF_BASIS_MP2', psi4.core.get_global_option('DF_BASIS_MP2'), 'RIFIT', psi4.core.get_global_option('BASIS')) ref_wfn.set_basisset('DF_BASIS_MP2', aux_basis) if (psi4.core.get_option('FORTE', 'MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), 'MINAO_BASIS', psi4.core.get_option('FORTE', 'MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins forte_wfn = psi4.core.plugin('forte.so', ref_wfn) return forte_wfn
def run_gpu_dfcc(name, **kwargs): """Function encoding sequence of PSI module calls for a GPU-accelerated DF-CCSD(T) computation. >>> energy('df-ccsd(t)') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # stash user options optstash = p4util.OptionsState(['GPU_DFCC', 'COMPUTE_TRIPLES'], ['GPU_DFCC', 'DFCC'], ['GPU_DFCC', 'NAT_ORBS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'SCF_TYPE'], ['GPU_DFCC', 'CC_TYPE']) psi4.core.set_local_option('GPU_DFCC', 'DFCC', True) psi4.core.set_local_option('GPU_DFCC', 'CC_TYPE', 'DF') # throw an exception for open-shells if (psi4.core.get_option('SCF', 'REFERENCE') != 'RHF'): raise ValidationError("Error: %s requires \"reference rhf\"." % lowername) def set_cholesky_from(mtd_type): type_val = core.get_global_option(mtd_type) if type_val == 'CD': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', 'CHOLESKY') # Alter default algorithm if not core.has_global_option_changed('SCF_TYPE'): optstash.add_option(['SCF_TYPE']) core.set_global_option('SCF_TYPE', 'CD') core.print_out(""" SCF Algorithm Type (re)set to CD.\n""") elif type_val == 'DF': if core.get_option('GPU_DFCC', 'DF_BASIS_CC') == 'CHOLESKY': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', '') proc_util.check_disk_df(name.upper(), optstash) else: raise ValidationError("""Invalid type '%s' for DFCC""" % type_val) # triples? if (lowername == 'gpu-df-ccsd'): psi4.core.set_local_option('GPU_DFCC', 'COMPUTE_TRIPLES', False) set_cholesky_from('CC_TYPE') if (lowername == 'gpu-df-ccsd(t)'): psi4.core.set_local_option('GPU_DFCC', 'COMPUTE_TRIPLES', True) set_cholesky_from('CC_TYPE') if psi4.core.get_global_option('SCF_TYPE') not in ['CD', 'DISK_DF']: raise ValidationError("""Invalid scf_type for DFCC.""") # save DF or CD ints generated by SCF for use in CC psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # psi4 run sequence ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, use_c1=True, **kwargs) # C1 certified else: if ref_wfn.molecule().schoenflies_symbol() != 'c1': raise ValidationError( """ GPU-DFCC does not make use of molecular symmetry: """ """ reference wavefunction must be C1.\n""") scf_aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), "DF_BASIS_SCF", psi4.core.get_option("SCF", "DF_BASIS_SCF"), "JKFIT", psi4.core.get_global_option('BASIS'), puream=ref_wfn.basisset().has_puream()) ref_wfn.set_basisset("DF_BASIS_SCF", scf_aux_basis) aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), "DF_BASIS_CC", psi4.core.get_global_option("DF_BASIS_CC"), "RIFIT", psi4.core.get_global_option("BASIS")) ref_wfn.set_basisset("DF_BASIS_CC", aux_basis) returnvalue = psi4.core.plugin('gpu_dfcc.so', ref_wfn) # restore options optstash.restore() return returnvalue
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.core.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 = psi4.driver.scf_helper(name, **kwargs) # if restarting from a checkpoint file, this file # needs to be in scratch with the correct name filename = psi4.core.get_option("V2RDM_CASSCF", "RESTART_FROM_CHECKPOINT_FILE") # Ensure IWL files have been written when not using DF/CD scf_type = psi4.core.get_option('SCF', 'SCF_TYPE') if (scf_type == 'PK' or scf_type == 'DIRECT'): proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # reorder wavefuntions based on user input # apply a list of 2x2 rotation matrices to the orbitals in the form of [irrep, orbital1, orbital2, theta] # where an angle of 0 would do nothing and an angle of 90 would switch the two orbitals. # the indices of irreps and orbitals start from 0 reorder_orbitals = psi4.core.get_option("V2RDM_CASSCF", "MCSCF_ROTATE") for orbord in reorder_orbitals: if type(orbord) != list: raise psi4.p4util.PsiException( "Each element of the orbtial rotate list requires 4 arguements (irrep, orb1, orb2, theta)." ) if len(orbord) != 4: raise psi4.p4util.PsiException( "Each element of the orbtial rotate list requires 4 arguements (irrep, orb1, orb2, theta)." ) irrep, orb1, orb2, theta = orbord if irrep > ref_wfn.Ca().nirrep(): raise psi4.p4util.PsiException( "REORDER_ORBITALS: Expression %s irrep number is larger than the number of irreps" % (str(orbord))) if max(orb1, orb2) > ref_wfn.Ca().coldim()[irrep]: raise psi4.p4util.PsiException( "REORDER_ORBITALS: Expression %s orbital number exceeds number of orbitals in irrep" % (str(orbord))) theta = numpy.deg2rad(theta) x_a = ref_wfn.Ca().nph[irrep][:, orb1].copy() y_a = ref_wfn.Ca().nph[irrep][:, orb2].copy() xp_a = numpy.cos(theta) * x_a - numpy.sin(theta) * y_a yp_a = numpy.sin(theta) * x_a + numpy.cos(theta) * y_a ref_wfn.Ca().nph[irrep][:, orb1] = xp_a ref_wfn.Ca().nph[irrep][:, orb2] = yp_a x_b = ref_wfn.Ca().nph[irrep][:, orb1].copy() y_b = ref_wfn.Ca().nph[irrep][:, orb2].copy() xp_b = numpy.cos(theta) * x_b - numpy.sin(theta) * y_b yp_b = numpy.sin(theta) * x_b + numpy.cos(theta) * y_b ref_wfn.Ca().nph[irrep][:, orb1] = xp_b ref_wfn.Ca().nph[irrep][:, orb2] = yp_b returnvalue = psi4.core.plugin('v2rdm_casscf.so', ref_wfn) optstash.restore() return returnvalue
def ip_fitting(name, omega_l=0.05, omega_r=2.5, omega_convergence=1.0e-3, maxiter=20, **kwargs): """Optimize DFT omega parameter for molecular system. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. omega_l : float, optional Minimum omega to be considered during fitting. omega_r : float, optional Maximum omega to be considered during fitting. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. omega_convergence : float, optional Threshold below which to consider omega converged. (formerly omega_tolerance) maxiter : int, optional Maximum number of iterations towards omega convergence. Returns ------- float Optimal omega parameter. """ optstash = p4util.OptionsState(['SCF', 'REFERENCE'], ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'DFT_OMEGA'], ['DOCC'], ['SOCC']) kwargs = p4util.kwargs_lower(kwargs) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] if core.get_option('SCF', 'REFERENCE') != 'UKS': core.print_out( """ Requested procedure `ip_fitting` runs further calculations with UKS reference.\n""" ) core.set_local_option('SCF', 'REFERENCE', 'UKS') # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """IP Fitting requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `ip_fitting` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') 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 core.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: core.set_local_option("SCF", "GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) core.set_local_option("SCF", "DF_INTS_IO", "SAVE") E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Burn-in', **kwargs) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") if not wfn.functional().is_x_lrc(): raise ValidationError( """Not sensible to optimize omega for non-long-range-correction functional.""" ) # 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.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na Nb1 = Nb if H**O > 0: Na1 -= 1 else: Nb1 -= 1 charge1 = charge0 + 1 mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_r) # Neutral if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) E0r, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Right Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOr = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) E1r = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Right Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r kIPr = -E_HOMOr delta_r = IPr - kIPr if IPr > kIPr: raise ValidationError( """\n***IP Fitting Error: Right Omega limit should have kIP > IP: {} !> {}""" .format(kIPr, IPr)) 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 core.set_local_option("SCF", "GUESS", "READ") # Left endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_l) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0l, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Left Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOl = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1l = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Left Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: raise ValidationError( """\n***IP Fitting Error: Left Omega limit should have kIP < IP: {} !< {}""" .format(kIPl, IPl)) 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 for step in range(maxiter): # Regula Falsi (modified) if repeat_l > 1: delta_l /= 2.0 if repeat_r > 1: delta_r /= 2.0 omega = -(omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l core.set_local_option('SCF', 'DFT_OMEGA', omega) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0, wfn = driver.energy( 'scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Omega = {:11.3E}'.format(omega), **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1 = driver.energy( 'scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Omega = {:11.3E}'.format(omega), **kwargs) core.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 += 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0 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_convergence: converged = True break core.IO.set_default_namespace("") core.print_out("""\n ==> IP Fitting Results <==\n\n""") core.print_out(""" => Occupation Determination <= \n\n""") core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) core.print_out(""" Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) core.print_out(""" Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) core.print_out(""" => Regula Falsi Iterations <=\n\n""") core.print_out(""" %3s %11s %14s %14s %14s %s\n""" % ('N', 'Omega', 'IP', 'kIP', 'Delta', 'Type')) for k in range(len(omegas)): core.print_out( """ %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])) optstash.restore() if converged: core.print_out("""\n IP Fitting Converged\n""") core.print_out(""" Final omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) core.print_out( """\n "M,I. does the dying. Fleet just does the flying."\n""") core.print_out(""" -Starship Troopers\n""") else: raise ConvergenceError("""IP Fitting """, step + 1) return ((omega_l + omega_r) / 2)
def frac_nuke(name, **kwargs): """Pull all the electrons out, one at a time""" optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], # NYI ["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """frac_nuke requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `frac_nuke` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') 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 <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() eps_a.print_out() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a.get(int(Na - 1)) E_b = eps_b.get(int(Nb - 1)) if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "LOAD") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps.np[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps.np[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "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 = core.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.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (Nintegral - 1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "NONE") # => Print the results out <= # E = {} core.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] core.print_out('\n') core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: core.print_out(line) core.print_out( '\n "You shoot a nuke down a bug hole, you got a lot of dead bugs"\n' ) core.print_out(' -Starship Troopers\n') # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) with open(stats_filename, 'w') as fh: fh.write(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) optstash.restore() return E
def run_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('forte') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # 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 = psi4.driver.scf_helper(name, **kwargs) # Get the option object options = psi4.core.get_options() options.set_current_module('FORTE') forte.forte_options.update_psi_options(options) if ('DF' in options.get_str('INT_TYPE')): aux_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'DF_BASIS_MP2', psi4.core.get_global_option('DF_BASIS_MP2'), 'RIFIT', psi4.core.get_global_option('BASIS')) ref_wfn.set_basisset('DF_BASIS_MP2', aux_basis) if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'MINAO_BASIS', options.get_str('MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Start Forte, initialize ambit my_proc_n_nodes = forte.startup() my_proc, n_nodes = my_proc_n_nodes # Print the banner forte.banner() # Create the MOSpaceInfo object mo_space_info = forte.make_mo_space_info(ref_wfn, forte.forte_options) # Call methods that project the orbitals (AVAS, embedding) orbital_projection(ref_wfn, options) state = forte.make_state_info_from_psi_wfn(ref_wfn) scf_info = forte.SCFInfo(ref_wfn) state_weights_map = forte.make_state_weights_map(forte.forte_options,ref_wfn) # Run a method job_type = options.get_str('JOB_TYPE') energy = 0.0 if job_type == 'NONE': forte.cleanup() return ref_wfn start = timeit.timeit() # Make an integral object ints = forte.make_forte_integrals(ref_wfn, options, mo_space_info) # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, forte.forte_options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua,Ub) # Run a method if (job_type == 'NEWDRIVER'): energy = forte_driver(state_weights_map, scf_info, forte.forte_options, ints, mo_space_info) else: energy = forte.forte_old_methods(ref_wfn, options, ints, mo_space_info) end = timeit.timeit() #print('\n\n Your calculation took ', (end - start), ' seconds'); # Close ambit, etc. forte.cleanup() psi4.core.set_scalar_variable('CURRENT ENERGY', energy) return ref_wfn
def run_psi4fockci(name, molecule, **kwargs): """ A method to run a RAS-nSF-IP/EA calculation. This runs a RAS-nSF-IP/EA calculation using Psi4's DETCI module. The number of spin-flips and IP/EA is determined based on setting the ``new_charge`` and ``new_multiplicity`` of the desired target state. Additional excitations are included by setting the ``conf_space`` keyword; excitations through the CISDT level are currently supported. Parameters ---------- name : str Name of method (for Psi4 interfacing) molecule : psi4.core.Molecule Molecule to run the calculation on. new_charge : int Target charge of the molecule. new_multiplicity : int Target multiplicity of the molecule. conf_space : str ("") Option for including additional excitations outside of the CAS space. Defaults to CAS-nSF-IP/EA. Valid options include: * ``""`` CAS-nSF-IP/EA * ``"h"`` RAS(h)-nSF-IP/EA * ``"p"`` RAS(p)-nSF-IP/EA * ``"1x"`` RAS(h,p)-nSF-IP/EA * ``"S"`` RAS(S)-nSF-IP/EA * ``"SD"`` RAS(SD)-nSF-IP/EA * ``"SDT"`` RAS(SDT)-nSF-IP/EA add_opts : dict ({}) Additional options to pass into Psi4. return_ci_wfn : bool (False) Whether to return the CI wavefunction object. return_rohf_wfn : bool (False) Whether to return the ROHF wavefunction object. return_rohf_e : bool (False) Whether to return the ROHF energy. read_rohf_wfn : bool (False) Whether to read a Psi4 ROHF wavefunction. rohf_wfn_in : psi4.core.Wavefunction The Psi4 ROHF reference wavefunction (pre-computed). write_rohf_wfn : str ("") Name of file (.npz) to write localize : bool (False) Perform BOYS localization on the RAS 2 space before DETCI call? Can help with visualization and analysis of orbitals. frozen_docc : int (0) Number of frozen core orbitals. frozen_vir : int (0) Number of frozen virtual orbitals. Returns ------- return_ci_wfn : psi4.core.Wavefunction The SF-CAS([conf_space]) wavefunction. """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['BASIS'], ['SCF', 'MAXITER'], ['DETCI', 'CI_MAXITER']) new_charge = kwargs.get('new_charge') new_multiplicity = kwargs.get('new_multiplicity') ref_mol = molecule if (not 'new_charge' in kwargs): print("ERROR: Please designate a target charge!") exit() if (not 'new_multiplicity' in kwargs): print("ERROR: Please designate a target multiplicity!") exit() conf_space = kwargs.get('conf_space', "") add_opts = kwargs.get('add_opts', {}) read_rohf_wfn = kwargs.get('read_rohf_wfn', False) wfn_rohf_in = kwargs.get('wfn_rohf_in', None) write_rohf_wfn = kwargs.get('write_rohf_wfn', "") write_ci_vects = kwargs.get('write_ci_vects', False) localize = kwargs.get('localize', False) frozen_docc = kwargs.get('frozen_docc', 0) frozen_uocc = kwargs.get('frozen_vir', 0) print("Starting Psi4FockCI...\n") # default options for Psi4 opts = { 'scf_type': 'pk', 'reference': 'rohf', 'mixed': False, 'maxiter': 1000, 'ci_maxiter': 50 } opts.update(add_opts) # add additional options from user # run ROHF calculation on reference state or read it in psi4.core.clean() psi4.set_options(opts) mol = ref_mol.clone() # clone molecule so original isn't modified # read in ROHF guess wavefunction if provided if (read_rohf_wfn): # set up options and run psi4.set_options(opts) print("Using ROHF from reference...") wfn_rohf = wfn_rohf_in e_rohf = wfn_rohf.energy() # else, run ROHF on reference state else: print("Running reference...") # TODO: Change to scf_helper call e_rohf, wfn_rohf = psi4.energy('scf', molecule=mol, return_wfn=True, options=opts) print("SCF (%i %i): %6.12f" % (mol.molecular_charge(), mol.multiplicity(), e_rohf)) # saving npz file of wavefunction if needed if (write_rohf_wfn != ""): wfn_rohf.to_file(write_rohf_wfn) # update molecular charge and multiplicity mol.set_molecular_charge(new_charge) mol.set_multiplicity(new_multiplicity) # set up reference wfn to pass into detci # save orbital values from reference calculation doccpi = wfn_rohf.doccpi()[0] soccpi = wfn_rohf.soccpi()[0] nmo = wfn_rohf.nmo() # calculate soccpi and doccpi new_soccpi = mol.multiplicity() - 1 del_electrons = ref_mol.molecular_charge() - mol.molecular_charge() n_total = wfn_rohf.nalpha() + wfn_rohf.nbeta() + del_electrons # set orbital occupations wfn_rohf.force_soccpi(psi4.core.Dimension([new_soccpi])) wfn_rohf.force_doccpi( psi4.core.Dimension([(int)((n_total - new_soccpi) / 2)])) # set up RAS1, RAS2, RAS3 spaces ras1 = doccpi ras2 = soccpi ras3 = nmo - soccpi - doccpi # add/remove orbitals to active space if ('add_orbs_ras1' in kwargs): ras1 = ras1 - kwargs['add_orbs_ras1'] ras2 = ras2 + kwargs['add_orbs_ras1'] if ('add_orbs_ras3' in kwargs): ras3 = ras3 - kwargs['add_orbs_ras3'] ras2 = ras2 + kwargs['add_orbs_ras3'] # if we need to localize... if (localize): C = psi4.core.Matrix.to_array(wfn_rohf.Ca(), copy=True) ras1_C = C[:, :doccpi] ras2_C = C[:, doccpi:doccpi + soccpi] ras3_C = C[:, doccpi + soccpi:] loc = psi4.core.Localizer.build('BOYS', wfn_rohf.basisset(), psi4.core.Matrix.from_array(ras2_C)) loc.localize() ras2_localized = psi4.core.Matrix.to_array(loc.L, copy=True) localized_orbs = np.column_stack((ras1_C, ras2_localized, ras3_C)) new_Ca = psi4.core.Matrix.from_array(localized_orbs, name="Ca") new_Cb = psi4.core.Matrix.from_array(localized_orbs, name="Cb") wfn_rohf.Ca().copy(new_Ca) wfn_rohf.Cb().copy(new_Cb) # change charge and multiplicity to new target values n_sf = (ref_mol.multiplicity() - abs(del_electrons) - new_multiplicity) / 2 print("\nRunning RAS-SF-IP/EA...") print(" New Charge/Mult: (%i %i)" % (new_charge, new_multiplicity)) print(" Spin-Flips: %i" % n_sf) print(" Electron Count: %i" % del_electrons) # set active space and docc space based on configuration space input # Regular CAS configuration space # includes only active space configurations if (conf_space == "" or conf_space == "CAS"): opts.update({'frozen_docc': [doccpi]}) opts.update({'ras1': [0]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [0]}) opts.update({'ras4': [0]}) # just (h) excitations elif (conf_space == "h"): opts.update({'ex_level': 0}) opts.update({'val_ex_level': 1}) opts.update({'ras3_max': 0}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [0]}) opts.update({'ras4': [0]}) # just (p) excitations elif (conf_space == "p"): opts.update({'ex_level': 0}) opts.update({'val_ex_level': 0}) opts.update({'ras3_max': 1}) opts.update({'frozen_docc': [doccpi]}) opts.update({'ras1': [0]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # 1x configuration space # includes (h, p) excitations elif (conf_space == "1x" or conf_space == "h,p"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 0}) opts.update({'val_ex_level': 1}) opts.update({'ras3_max': 1}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # S configuration space # includes (h, p, hp) excitations elif (conf_space == "s"): opts.update({'ex_level': 1}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) elif (conf_space == "sd"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 2}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) elif (conf_space == "sdt"): opts.update({'frozen_docc': [0]}) opts.update({'ex_level': 3}) opts.update({'frozen_docc': [frozen_docc]}) opts.update({'ras1': [ras1 - frozen_docc]}) opts.update({'ras2': [ras2]}) opts.update({'ras3': [ras3 - frozen_uocc]}) opts.update({'frozen_uocc': [frozen_uocc]}) opts.update({'ras4': [0]}) # Other configuration spaces aren't supported yet else: print("Configuration space %s not supported. Exiting..." % conf_space) exit() # run cas psi4.set_options(opts) e_cas, wfn_cas = psi4.energy('detci', ref_wfn=wfn_rohf, return_wfn=True, molecule=mol) # printing useful info print("\n Root\tEnergy") print("-----------------------------------") n = 0 while (psi4.core.has_variable("CI ROOT %i TOTAL ENERGY" % n)): n_str = "CI ROOT %i TOTAL ENERGY" % n e_n = psi4.core.variable(n_str) print(" %4i\t%6.12f" % (n, e_n)) n = n + 1 print("-----------------------------------\n") psi4.core.print_variables() # printing Psi4 variables # obtain eigenvectors if needed # partly based on Daniel Smith's answer on Psi4 forums if (write_ci_vects): wfn_cas_2 = psi4.core.CIWavefunction(wfn_rohf) C = np.zeros((wfn_cas_2.ndet(), n_roots)) for i in range(n_roots): dvec = wfn_cas_2.new_civector(i + 1, 53, True, True) dvec.set_nvec(i + 1) dvec.init_io_files(True) dvec.read(i, 0) C[:, i] = np.array(dvec) np.savetxt('ci_vect.txt', C) optstash.restore() print("Psi4FockCI complete. Have a good day!") return wfn_cas
def database(name, db_name, **kwargs): r"""Function to access the molecule objects and reference energies of popular chemical databases. :aliases: db() :returns: (*float*) Mean absolute deviation of the database in kcal/mol :PSI variables: .. hlist:: :columns: 1 * :psivar:`db_name DATABASE MEAN SIGNED DEVIATION <db_nameDATABASEMEANSIGNEDDEVIATION>` * :psivar:`db_name DATABASE MEAN ABSOLUTE DEVIATION <db_nameDATABASEMEANABSOLUTEDEVIATION>` * :psivar:`db_name DATABASE ROOT-MEAN-SQUARE DEVIATION <db_nameDATABASEROOT-MEAN-SQUARESIGNEDDEVIATION>` * Python dictionaries of results accessible as ``DB_RGT`` and ``DB_RXN``. .. note:: It is very easy to make a database from a collection of xyz files using the script :source:`share/scripts/ixyz2database.py`. See :ref:`sec:createDatabase` for details. .. caution:: Some features are not yet implemented. Buy a developer some coffee. - In sow/reap mode, use only global options (e.g., the local option set by ``set scf scf_type df`` will not be respected). .. note:: To access a database that is not embedded in a |PSIfour| distribution, add the path to the directory containing the database to the environment variable :envvar:`PYTHONPATH`. :type name: string :param name: ``'scf'`` || ``'sapt0'`` || ``'ccsd(t)'`` || etc. First argument, usually unlabeled. Indicates the computational method to be applied to the database. May be any valid argument to :py:func:`~driver.energy`. :type db_name: string :param db_name: ``'BASIC'`` || ``'S22'`` || ``'HTBH'`` || etc. Second argument, usually unlabeled. Indicates the requested database name, matching (case insensitive) the name of a python file in ``psi4/share/databases`` or :envvar:`PYTHONPATH`. Consult that directory for available databases and literature citations. :type func: :ref:`function <op_py_function>` :param func: |dl| ``energy`` |dr| || ``optimize`` || ``cbs`` Indicates the type of calculation to be performed on each database member. The default performs a single-point ``energy('name')``, while ``optimize`` perfoms a geometry optimization on each reagent, and ``cbs`` performs a compound single-point energy. If a nested series of python functions is intended (see :ref:`sec:intercalls`), use keyword ``db_func`` instead of ``func``. :type mode: string :param mode: |dl| ``'continuous'`` |dr| || ``'sow'`` || ``'reap'`` Indicates whether the calculations required to complete the database are to be run in one file (``'continuous'``) or are to be farmed out in an embarrassingly parallel fashion (``'sow'``/``'reap'``). For the latter, run an initial job with ``'sow'`` and follow instructions in its output file. :type cp: :ref:`boolean <op_py_boolean>` :param cp: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether counterpoise correction is employed in computing interaction energies. Use this option and NOT the :py:func:`~wrappers.cp` function for BSSE correction in database(). Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes. :type rlxd: :ref:`boolean <op_py_boolean>` :param rlxd: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether correction for deformation energy is employed in computing interaction energies. Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes with non-frozen monomers, e.g., HBC6. :type symm: :ref:`boolean <op_py_boolean>` :param symm: |dl| ``'on'`` |dr| || ``'off'`` Indicates whether the native symmetry of the database reagents is employed (``'on'``) or whether it is forced to :math:`C_1` symmetry (``'off'``). Some computational methods (e.g., SAPT) require no symmetry, and this will be set by database(). :type zpe: :ref:`boolean <op_py_boolean>` :param zpe: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether zero-point-energy corrections are appended to single-point energy values. Option valid only for certain thermochemical databases. Disabled until Hessians ready. :type benchmark: string :param benchmark: |dl| ``'default'`` |dr| || ``'S22A'`` || etc. Indicates whether a non-default set of reference energies, if available (See :ref:`sec:availableDatabases`), are employed for the calculation of error statistics. :type tabulate: array of strings :param tabulate: |dl| ``[]`` |dr| || ``['scf total energy', 'natom']`` || etc. Indicates whether to form tables of variables other than the primary requested energy. Available for any PSI variable. :type subset: string or array of strings :param subset: Indicates a subset of the full database to run. This is a very flexible option and can be used in three distinct ways, outlined below. Note that two take a string and the last takes an array. See `Available Databases`_ for available values. * ``'small'`` || ``'large'`` || ``'equilibrium'`` Calls predefined subsets of the requested database, either ``'small'``, a few of the smallest database members, ``'large'``, the largest of the database members, or ``'equilibrium'``, the equilibrium geometries for a database composed of dissociation curves. * ``'BzBz_S'`` || ``'FaOOFaON'`` || ``'ArNe'`` || ``'HB'`` || etc. For databases composed of dissociation curves, or otherwise divided into subsets, individual curves and subsets can be called by name. Consult the database python files for available molecular systems (case insensitive). * ``[1,2,5]`` || ``['1','2','5']`` || ``['BzMe-3.5', 'MeMe-5.0']`` || etc. Specify a list of database members to run. Consult the database python files for available molecular systems. This is the only portion of database input that is case sensitive; choices for this keyword must match the database python file. :examples: >>> # [1] Two-stage SCF calculation on short, equilibrium, and long helium dimer >>> db('scf','RGC10',cast_up='sto-3g',subset=['HeHe-0.85','HeHe-1.0','HeHe-1.5'], tabulate=['scf total energy','natom']) >>> # [2] Counterpoise-corrected interaction energies for three complexes in S22 >>> # Error statistics computed wrt an old benchmark, S22A >>> database('mp2','S22',cp=1,subset=[16,17,8],benchmark='S22A') >>> # [3] SAPT0 on the neon dimer dissociation curve >>> db('sapt0',subset='NeNe',cp=0,symm=0,db_name='RGC10') >>> # [4] Optimize system 1 in database S22, producing tables of scf and mp2 energy >>> db('mp2','S22',db_func=optimize,subset=[1], tabulate=['mp2 total energy','current energy']) >>> # [5] CCSD on the smallest systems of HTBH, a hydrogen-transfer database >>> database('ccsd','HTBH',subset='small', tabulate=['ccsd total energy', 'mp2 total energy']) """ lowername = name #TODO kwargs = p4util.kwargs_lower(kwargs) # Wrap any positional arguments into kwargs (for intercalls among wrappers) if not('name' in kwargs) and name: kwargs['name'] = name #.lower() if not('db_name' in kwargs) and db_name: kwargs['db_name'] = db_name # Establish function to call func = kwargs.pop('db_func', kwargs.pop('func', energy)) kwargs['db_func'] = func # Bounce to CP if bsse kwarg (someday) if kwargs.get('bsse_type', None) is not None: raise ValidationError("""Database: Cannot specify bsse_type for database. Use the cp keyword withing database instead.""") allowoptexceeded = kwargs.get('allowoptexceeded', False) optstash = p4util.OptionsState( ['WRITER_FILE_LABEL'], ['SCF', 'REFERENCE']) # Wrapper wholly defines molecule. discard any passed-in kwargs.pop('molecule', None) # Paths to search for database files: here + PSIPATH + library + PYTHONPATH db_paths = [] db_paths.append(os.getcwd()) db_paths.extend(os.environ.get('PSIPATH', '').split(os.path.pathsep)) db_paths.append(os.path.join(core.get_datadir(), 'databases')) db_paths.append(os.path.dirname(__file__)) db_paths = list(map(os.path.abspath, db_paths)) sys.path[1:1] = db_paths # TODO this should be modernized a la interface_cfour # Define path and load module for requested database database = p4util.import_ignorecase(db_name) if database is None: core.print_out('\nPython module for database %s failed to load\n\n' % (db_name)) core.print_out('\nSearch path that was tried:\n') core.print_out(", ".join(map(str, sys.path))) raise ValidationError("Python module loading problem for database " + str(db_name)) else: dbse = database.dbse HRXN = database.HRXN ACTV = database.ACTV RXNM = database.RXNM BIND = database.BIND TAGL = database.TAGL GEOS = database.GEOS try: DATA = database.DATA except AttributeError: DATA = {} user_writer_file_label = core.get_global_option('WRITER_FILE_LABEL') user_reference = core.get_global_option('REFERENCE') # Configuration based upon e_name & db_name options # Force non-supramolecular if needed if not hasattr(lowername, '__call__') and re.match(r'^.*sapt', lowername): try: database.ACTV_SA except AttributeError: raise ValidationError('Database %s not suitable for non-supramolecular calculation.' % (db_name)) else: ACTV = database.ACTV_SA # Force open-shell if needed openshell_override = 0 if user_reference in ['RHF', 'RKS']: try: database.isOS except AttributeError: pass else: if p4util.yes.match(str(database.isOS)): openshell_override = 1 core.print_out('\nSome reagents in database %s require an open-shell reference; will be reset to UHF/UKS as needed.\n' % (db_name)) # Configuration based upon database keyword options # Option symmetry- whether symmetry treated normally or turned off (currently req'd for dfmp2 & dft) db_symm = kwargs.get('symm', True) symmetry_override = 0 if db_symm is False: symmetry_override = 1 elif db_symm is True: pass else: raise ValidationError("""Symmetry mode '%s' not valid.""" % (db_symm)) # Option mode of operation- whether db run in one job or files farmed out db_mode = kwargs.pop('db_mode', kwargs.pop('mode', 'continuous')).lower() kwargs['db_mode'] = db_mode if db_mode == 'continuous': pass elif db_mode == 'sow': pass elif db_mode == 'reap': db_linkage = kwargs.get('linkage', None) if db_linkage is None: raise ValidationError("""Database execution mode 'reap' requires a linkage option.""") else: raise ValidationError("""Database execution mode '%s' not valid.""" % (db_mode)) # Option counterpoise- whether for interaction energy databases run in bsse-corrected or not db_cp = kwargs.get('cp', False) if db_cp is True: try: database.ACTV_CP except AttributeError: raise ValidationError("""Counterpoise correction mode 'yes' invalid for database %s.""" % (db_name)) else: ACTV = database.ACTV_CP elif db_cp is False: pass else: raise ValidationError("""Counterpoise correction mode '%s' not valid.""" % (db_cp)) # Option relaxed- whether for non-frozen-monomer interaction energy databases include deformation correction or not? db_rlxd = kwargs.get('rlxd', False) if db_rlxd is True: if db_cp is True: try: database.ACTV_CPRLX database.RXNM_CPRLX except AttributeError: raise ValidationError('Deformation and counterpoise correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_CPRLX RXNM = database.RXNM_CPRLX elif db_cp is False: try: database.ACTV_RLX except AttributeError: raise ValidationError('Deformation correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_RLX elif db_rlxd is False: #elif no.match(str(db_rlxd)): pass else: raise ValidationError('Deformation correction mode \'%s\' not valid.' % (db_rlxd)) # Option zero-point-correction- whether for thermochem databases jobs are corrected by zpe db_zpe = kwargs.get('zpe', False) if db_zpe is True: raise ValidationError('Zero-point-correction mode \'yes\' not yet implemented.') elif db_zpe is False: pass else: raise ValidationError('Zero-point-correction \'mode\' %s not valid.' % (db_zpe)) # Option benchmark- whether error statistics computed wrt alternate reference energies db_benchmark = 'default' if 'benchmark' in kwargs: db_benchmark = kwargs['benchmark'] if db_benchmark.lower() == 'default': pass else: BIND = p4util.getattr_ignorecase(database, 'BIND_' + db_benchmark) if BIND is None: raise ValidationError('Special benchmark \'%s\' not available for database %s.' % (db_benchmark, db_name)) # Option tabulate- whether tables of variables other than primary energy method are formed # TODO db(func=cbs,tabulate=[non-current-energy]) # broken db_tabulate = [] if 'tabulate' in kwargs: db_tabulate = kwargs['tabulate'] # Option subset- whether all of the database or just a portion is run db_subset = HRXN if 'subset' in kwargs: db_subset = kwargs['subset'] if isinstance(db_subset, (str, bytes)): if db_subset.lower() == 'small': try: database.HRXN_SM except AttributeError: raise ValidationError("""Special subset 'small' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_SM elif db_subset.lower() == 'large': try: database.HRXN_LG except AttributeError: raise ValidationError("""Special subset 'large' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_LG elif db_subset.lower() == 'equilibrium': try: database.HRXN_EQ except AttributeError: raise ValidationError("""Special subset 'equilibrium' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_EQ else: HRXN = p4util.getattr_ignorecase(database, db_subset) if HRXN is None: HRXN = p4util.getattr_ignorecase(database, 'HRXN_' + db_subset) if HRXN is None: raise ValidationError("""Special subset '%s' not available for database %s.""" % (db_subset, db_name)) else: temp = [] for rxn in db_subset: if rxn in HRXN: temp.append(rxn) else: raise ValidationError("""Subset element '%s' not a member of database %s.""" % (str(rxn), db_name)) HRXN = temp temp = [] for rxn in HRXN: temp.append(ACTV['%s-%s' % (dbse, rxn)]) HSYS = p4util.drop_duplicates(sum(temp, [])) # Sow all the necessary reagent computations core.print_out("\n\n") p4util.banner(("Database %s Computation" % (db_name))) core.print_out("\n") # write index of calcs to output file instructions = """\n The database single-job procedure has been selected through mode='continuous'.\n""" instructions += """ Calculations for the reagents will proceed in the order below and will be followed\n""" instructions += """ by summary results for the database.\n\n""" for rgt in HSYS: instructions += """ %-s\n""" % (rgt) core.print_out(instructions) # Loop through chemical systems ERGT = {} ERXN = {} VRGT = {} VRXN = {} for rgt in HSYS: VRGT[rgt] = {} core.print_out('\n') p4util.banner(' Database {} Computation: Reagent {} \n {}'.format(db_name, rgt, TAGL[rgt])) core.print_out('\n') molecule = core.Molecule.from_dict(GEOS[rgt].to_dict()) molecule.set_name(rgt) molecule.update_geometry() if symmetry_override: molecule.reset_point_group('c1') molecule.fix_orientation(True) molecule.fix_com(True) molecule.update_geometry() if (openshell_override) and (molecule.multiplicity() != 1): if user_reference == 'RHF': core.set_global_option('REFERENCE', 'UHF') elif user_reference == 'RKS': core.set_global_option('REFERENCE', 'UKS') core.set_global_option('WRITER_FILE_LABEL', user_writer_file_label + ('' if user_writer_file_label == '' else '-') + rgt) if allowoptexceeded: try: ERGT[rgt] = func(molecule=molecule, **kwargs) except ConvergenceError: core.print_out(f"Optimization exceeded cycles for {rgt}") ERGT[rgt] = 0.0 else: ERGT[rgt] = func(molecule=molecule, **kwargs) core.print_variables() core.print_out(" Database Contributions Map:\n {}\n".format('-' * 75)) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if rgt in ACTV[db_rxn]: core.print_out(' reagent {} contributes by {:.4f} to reaction {}\n'.format(rgt, RXNM[db_rxn][rgt], db_rxn)) core.print_out('\n') for envv in db_tabulate: VRGT[rgt][envv.upper()] = core.variable(envv) core.set_global_option("REFERENCE", user_reference) core.clean() #core.opt_clean() core.clean_variables() # Reap all the necessary reaction computations core.print_out("\n") p4util.banner(("Database %s Results" % (db_name))) core.print_out("\n") maxactv = [] for rxn in HRXN: maxactv.append(len(ACTV[dbse + '-' + str(rxn)])) maxrgt = max(maxactv) table_delimit = '-' * (62 + 20 * maxrgt) tables = '' # find any reactions that are incomplete FAIL = collections.defaultdict(int) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) for i in range(len(ACTV[db_rxn])): if abs(ERGT[ACTV[db_rxn][i]]) < 1.0e-12: if not allowoptexceeded: FAIL[rxn] = 1 # tabulate requested process::environment variables tables += """ For each VARIABLE requested by tabulate, a 'Reaction Value' will be formed from\n""" tables += """ 'Reagent' values according to weightings 'Wt', as for the REQUESTED ENERGY below.\n""" tables += """ Depending on the nature of the variable, this may or may not make any physical sense.\n""" for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) VRXN[db_rxn] = {} for envv in db_tabulate: envv = envv.upper() tables += """\n ==> %s <==\n\n""" % (envv.title()) tables += _tblhead(maxrgt, table_delimit, 2) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8s %8s %8s %8s""" % (db_rxn, '', '****', '', '') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) else: VRXN[db_rxn][envv] = 0.0 for i in range(len(ACTV[db_rxn])): VRXN[db_rxn][envv] += VRGT[ACTV[db_rxn][i]][envv] * RXNM[db_rxn][ACTV[db_rxn][i]] tables += """\n%23s %16.8f """ % (db_rxn, VRXN[db_rxn][envv]) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) tables += """\n %s\n""" % (table_delimit) # tabulate primary requested energy variable with statistics count_rxn = 0 minDerror = 100000.0 maxDerror = 0.0 MSDerror = 0.0 MADerror = 0.0 RMSDerror = 0.0 tables += """\n ==> %s <==\n\n""" % ('Requested Energy') tables += _tblhead(maxrgt, table_delimit, 1) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8.4f %8s %10s %10s""" % (db_rxn, BIND[db_rxn], '****', '****', '****') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) else: ERXN[db_rxn] = 0.0 for i in range(len(ACTV[db_rxn])): ERXN[db_rxn] += ERGT[ACTV[db_rxn][i]] * RXNM[db_rxn][ACTV[db_rxn][i]] error = constants.hartree2kcalmol * ERXN[db_rxn] - BIND[db_rxn] tables += """\n%23s %8.4f %8.4f %10.4f %10.4f""" % (db_rxn, BIND[db_rxn], constants.hartree2kcalmol * ERXN[db_rxn], error, error * constants.cal2J) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) if abs(error) < abs(minDerror): minDerror = error if abs(error) > abs(maxDerror): maxDerror = error MSDerror += error MADerror += abs(error) RMSDerror += error * error count_rxn += 1 tables += """\n %s\n""" % (table_delimit) if count_rxn: MSDerror /= float(count_rxn) MADerror /= float(count_rxn) RMSDerror = math.sqrt(RMSDerror / float(count_rxn)) tables += """%23s %19s %10.4f %10.4f\n""" % ('Minimal Dev', '', minDerror, minDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ('Maximal Dev', '', maxDerror, maxDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ('Mean Signed Dev', '', MSDerror, MSDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ('Mean Absolute Dev', '', MADerror, MADerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ('RMS Dev', '', RMSDerror, RMSDerror * constants.cal2J) tables += """ %s\n""" % (table_delimit) core.set_variable('%s DATABASE MEAN SIGNED DEVIATION' % (db_name), MSDerror) core.set_variable('%s DATABASE MEAN ABSOLUTE DEVIATION' % (db_name), MADerror) core.set_variable('%s DATABASE ROOT-MEAN-SQUARE DEVIATION' % (db_name), RMSDerror) core.print_out(tables) finalenergy = MADerror else: finalenergy = 0.0 optstash.restore() DB_RGT.clear() DB_RGT.update(VRGT) DB_RXN.clear() DB_RXN.update(VRXN) return finalenergy
def run_gpu_dfcc(name, **kwargs): """Function encoding sequence of PSI module calls for a GPU-accelerated DF-CCSD(T) computation. >>> energy('df-ccsd(t)') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # stash user options optstash = p4util.OptionsState( ['GPU_DFCC','COMPUTE_TRIPLES'], ['GPU_DFCC','DFCC'], ['GPU_DFCC','NAT_ORBS'], ['SCF','DF_INTS_IO'], ['GPU_DFCC','CC_TYPE']) psi4.core.set_local_option('GPU_DFCC','DFCC', True) psi4.core.set_local_option('GPU_DFCC','CC_TYPE', 'DF') # throw an exception for open-shells if (psi4.core.get_option('SCF','REFERENCE') != 'RHF' ): raise ValidationError("Error: %s requires \"reference rhf\"." % lowername) def set_cholesky_from(mtd_type): type_val = core.get_global_option(mtd_type) if type_val == 'CD': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', 'CHOLESKY') # Alter default algorithm if not core.has_global_option_changed('SCF_TYPE'): optstash.add_option(['SCF_TYPE']) core.set_global_option('SCF_TYPE', 'CD') core.print_out(""" SCF Algorithm Type (re)set to CD.\n""") elif type_val in ['DF', 'DISK_DF']: if core.get_option('GPU_DFCC', 'DF_BASIS_CC') == 'CHOLESKY': core.set_local_option('GPU_DFCC', 'DF_BASIS_CC', '') proc_util.check_disk_df(name.upper(), optstash) else: raise ValidationError("""Invalid type '%s' for DFCC""" % type_val) # triples? if (lowername == 'gpu-df-ccsd'): psi4.core.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', False) set_cholesky_from('CC_TYPE') if (lowername == 'gpu-df-ccsd(t)'): psi4.core.set_local_option('GPU_DFCC','COMPUTE_TRIPLES', True) set_cholesky_from('CC_TYPE') if psi4.core.get_global_option('SCF_TYPE') not in ['CD', 'DISK_DF']: raise ValidationError("""Invalid scf_type for DFCC.""") # save DF or CD ints generated by SCF for use in CC psi4.core.set_local_option('SCF', 'DF_INTS_IO', 'SAVE') # psi4 run sequence ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, use_c1=True, **kwargs) # C1 certified else: if ref_wfn.molecule().schoenflies_symbol() != 'c1': raise ValidationError(""" GPU-DFCC does not make use of molecular symmetry: """ """ reference wavefunction must be C1.\n""") scf_aux_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), "DF_BASIS_SCF", psi4.core.get_option("SCF", "DF_BASIS_SCF"), "JKFIT", psi4.core.get_global_option('BASIS'), puream=ref_wfn.basisset().has_puream()) ref_wfn.set_basisset("DF_BASIS_SCF", scf_aux_basis) aux_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), "DF_BASIS_CC", psi4.core.get_global_option("DF_BASIS_CC"), "RIFIT", psi4.core.get_global_option("BASIS")) ref_wfn.set_basisset("DF_BASIS_CC", aux_basis) returnvalue = psi4.core.plugin('gpu_dfcc.so',ref_wfn) # restore options optstash.restore() return returnvalue
def database(name, db_name, **kwargs): r"""Function to access the molecule objects and reference energies of popular chemical databases. :aliases: db() :returns: (*float*) Mean absolute deviation of the database in kcal/mol :PSI variables: .. hlist:: :columns: 1 * :psivar:`db_name DATABASE MEAN SIGNED DEVIATION <db_nameDATABASEMEANSIGNEDDEVIATION>` * :psivar:`db_name DATABASE MEAN ABSOLUTE DEVIATION <db_nameDATABASEMEANABSOLUTEDEVIATION>` * :psivar:`db_name DATABASE ROOT-MEAN-SQUARE DEVIATION <db_nameDATABASEROOT-MEAN-SQUARESIGNEDDEVIATION>` * Python dictionaries of results accessible as ``DB_RGT`` and ``DB_RXN``. .. note:: It is very easy to make a database from a collection of xyz files using the script :source:`share/scripts/ixyz2database.py`. See :ref:`sec:createDatabase` for details. .. caution:: Some features are not yet implemented. Buy a developer some coffee. - In sow/reap mode, use only global options (e.g., the local option set by ``set scf scf_type df`` will not be respected). .. note:: To access a database that is not embedded in a |PSIfour| distribution, add the path to the directory containing the database to the environment variable :envvar:`PYTHONPATH`. :type name: string :param name: ``'scf'`` || ``'sapt0'`` || ``'ccsd(t)'`` || etc. First argument, usually unlabeled. Indicates the computational method to be applied to the database. May be any valid argument to :py:func:`~driver.energy`. :type db_name: string :param db_name: ``'BASIC'`` || ``'S22'`` || ``'HTBH'`` || etc. Second argument, usually unlabeled. Indicates the requested database name, matching (case insensitive) the name of a python file in ``psi4/share/databases`` or :envvar:`PYTHONPATH`. Consult that directory for available databases and literature citations. :type func: :ref:`function <op_py_function>` :param func: |dl| ``energy`` |dr| || ``optimize`` || ``cbs`` Indicates the type of calculation to be performed on each database member. The default performs a single-point ``energy('name')``, while ``optimize`` perfoms a geometry optimization on each reagent, and ``cbs`` performs a compound single-point energy. If a nested series of python functions is intended (see :ref:`sec:intercalls`), use keyword ``db_func`` instead of ``func``. :type mode: string :param mode: |dl| ``'continuous'`` |dr| || ``'sow'`` || ``'reap'`` Indicates whether the calculations required to complete the database are to be run in one file (``'continuous'``) or are to be farmed out in an embarrassingly parallel fashion (``'sow'``/``'reap'``). For the latter, run an initial job with ``'sow'`` and follow instructions in its output file. :type cp: :ref:`boolean <op_py_boolean>` :param cp: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether counterpoise correction is employed in computing interaction energies. Use this option and NOT the :py:func:`~wrappers.cp` function for BSSE correction in database(). Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes. :type rlxd: :ref:`boolean <op_py_boolean>` :param rlxd: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether correction for deformation energy is employed in computing interaction energies. Option available (See :ref:`sec:availableDatabases`) only for databases of bimolecular complexes with non-frozen monomers, e.g., HBC6. :type symm: :ref:`boolean <op_py_boolean>` :param symm: |dl| ``'on'`` |dr| || ``'off'`` Indicates whether the native symmetry of the database reagents is employed (``'on'``) or whether it is forced to :math:`C_1` symmetry (``'off'``). Some computational methods (e.g., SAPT) require no symmetry, and this will be set by database(). :type zpe: :ref:`boolean <op_py_boolean>` :param zpe: ``'on'`` || |dl| ``'off'`` |dr| Indicates whether zero-point-energy corrections are appended to single-point energy values. Option valid only for certain thermochemical databases. Disabled until Hessians ready. :type benchmark: string :param benchmark: |dl| ``'default'`` |dr| || ``'S22A'`` || etc. Indicates whether a non-default set of reference energies, if available (See :ref:`sec:availableDatabases`), are employed for the calculation of error statistics. :type tabulate: array of strings :param tabulate: |dl| ``[]`` |dr| || ``['scf total energy', 'natom']`` || etc. Indicates whether to form tables of variables other than the primary requested energy. Available for any PSI variable. :type subset: string or array of strings :param subset: Indicates a subset of the full database to run. This is a very flexible option and can be used in three distinct ways, outlined below. Note that two take a string and the last takes an array. See `Available Databases`_ for available values. * ``'small'`` || ``'large'`` || ``'equilibrium'`` Calls predefined subsets of the requested database, either ``'small'``, a few of the smallest database members, ``'large'``, the largest of the database members, or ``'equilibrium'``, the equilibrium geometries for a database composed of dissociation curves. * ``'BzBz_S'`` || ``'FaOOFaON'`` || ``'ArNe'`` || ``'HB'`` || etc. For databases composed of dissociation curves, or otherwise divided into subsets, individual curves and subsets can be called by name. Consult the database python files for available molecular systems (case insensitive). * ``[1,2,5]`` || ``['1','2','5']`` || ``['BzMe-3.5', 'MeMe-5.0']`` || etc. Specify a list of database members to run. Consult the database python files for available molecular systems. This is the only portion of database input that is case sensitive; choices for this keyword must match the database python file. :examples: >>> # [1] Two-stage SCF calculation on short, equilibrium, and long helium dimer >>> db('scf','RGC10',cast_up='sto-3g',subset=['HeHe-0.85','HeHe-1.0','HeHe-1.5'], tabulate=['scf total energy','natom']) >>> # [2] Counterpoise-corrected interaction energies for three complexes in S22 >>> # Error statistics computed wrt an old benchmark, S22A >>> database('mp2','S22',cp=1,subset=[16,17,8],benchmark='S22A') >>> # [3] SAPT0 on the neon dimer dissociation curve >>> db('sapt0',subset='NeNe',cp=0,symm=0,db_name='RGC10') >>> # [4] Optimize system 1 in database S22, producing tables of scf and mp2 energy >>> db('mp2','S22',db_func=optimize,subset=[1], tabulate=['mp2 total energy','current energy']) >>> # [5] CCSD on the smallest systems of HTBH, a hydrogen-transfer database >>> database('ccsd','HTBH',subset='small', tabulate=['ccsd total energy', 'mp2 total energy']) """ lowername = name #TODO kwargs = p4util.kwargs_lower(kwargs) # Wrap any positional arguments into kwargs (for intercalls among wrappers) if not ('name' in kwargs) and name: kwargs['name'] = name #.lower() if not ('db_name' in kwargs) and db_name: kwargs['db_name'] = db_name # Establish function to call func = kwargs.pop('db_func', kwargs.pop('func', energy)) kwargs['db_func'] = func # Bounce to CP if bsse kwarg (someday) if kwargs.get('bsse_type', None) is not None: raise ValidationError( """Database: Cannot specify bsse_type for database. Use the cp keyword withing database instead.""" ) allowoptexceeded = kwargs.get('allowoptexceeded', False) optstash = p4util.OptionsState(['WRITER_FILE_LABEL'], ['SCF', 'REFERENCE']) # Wrapper wholly defines molecule. discard any passed-in kwargs.pop('molecule', None) # Paths to search for database files: here + PSIPATH + library + PYTHONPATH db_paths = [] db_paths.append(os.getcwd()) db_paths.extend(os.environ.get('PSIPATH', '').split(os.path.pathsep)) db_paths.append(os.path.join(core.get_datadir(), 'databases')) db_paths.append(os.path.dirname(__file__)) db_paths = list(map(os.path.abspath, db_paths)) sys.path[1:1] = db_paths # TODO this should be modernized a la interface_cfour # Define path and load module for requested database database = p4util.import_ignorecase(db_name) if database is None: core.print_out('\nPython module for database %s failed to load\n\n' % (db_name)) core.print_out('\nSearch path that was tried:\n') core.print_out(", ".join(map(str, sys.path))) raise ValidationError("Python module loading problem for database " + str(db_name)) else: dbse = database.dbse HRXN = database.HRXN ACTV = database.ACTV RXNM = database.RXNM BIND = database.BIND TAGL = database.TAGL GEOS = database.GEOS try: DATA = database.DATA except AttributeError: DATA = {} user_writer_file_label = core.get_global_option('WRITER_FILE_LABEL') user_reference = core.get_global_option('REFERENCE') # Configuration based upon e_name & db_name options # Force non-supramolecular if needed if not hasattr(lowername, '__call__') and re.match(r'^.*sapt', lowername): try: database.ACTV_SA except AttributeError: raise ValidationError( 'Database %s not suitable for non-supramolecular calculation.' % (db_name)) else: ACTV = database.ACTV_SA # Force open-shell if needed openshell_override = 0 if user_reference in ['RHF', 'RKS']: try: database.isOS except AttributeError: pass else: if p4util.yes.match(str(database.isOS)): openshell_override = 1 core.print_out( '\nSome reagents in database %s require an open-shell reference; will be reset to UHF/UKS as needed.\n' % (db_name)) # Configuration based upon database keyword options # Option symmetry- whether symmetry treated normally or turned off (currently req'd for dfmp2 & dft) db_symm = kwargs.get('symm', True) symmetry_override = 0 if db_symm is False: symmetry_override = 1 elif db_symm is True: pass else: raise ValidationError("""Symmetry mode '%s' not valid.""" % (db_symm)) # Option mode of operation- whether db run in one job or files farmed out db_mode = kwargs.pop('db_mode', kwargs.pop('mode', 'continuous')).lower() kwargs['db_mode'] = db_mode if db_mode == 'continuous': pass elif db_mode == 'sow': pass elif db_mode == 'reap': db_linkage = kwargs.get('linkage', None) if db_linkage is None: raise ValidationError( """Database execution mode 'reap' requires a linkage option.""" ) else: raise ValidationError("""Database execution mode '%s' not valid.""" % (db_mode)) # Option counterpoise- whether for interaction energy databases run in bsse-corrected or not db_cp = kwargs.get('cp', False) if db_cp is True: try: database.ACTV_CP except AttributeError: raise ValidationError( """Counterpoise correction mode 'yes' invalid for database %s.""" % (db_name)) else: ACTV = database.ACTV_CP elif db_cp is False: pass else: raise ValidationError( """Counterpoise correction mode '%s' not valid.""" % (db_cp)) # Option relaxed- whether for non-frozen-monomer interaction energy databases include deformation correction or not? db_rlxd = kwargs.get('rlxd', False) if db_rlxd is True: if db_cp is True: try: database.ACTV_CPRLX database.RXNM_CPRLX except AttributeError: raise ValidationError( 'Deformation and counterpoise correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_CPRLX RXNM = database.RXNM_CPRLX elif db_cp is False: try: database.ACTV_RLX except AttributeError: raise ValidationError( 'Deformation correction mode \'yes\' invalid for database %s.' % (db_name)) else: ACTV = database.ACTV_RLX elif db_rlxd is False: #elif no.match(str(db_rlxd)): pass else: raise ValidationError('Deformation correction mode \'%s\' not valid.' % (db_rlxd)) # Option zero-point-correction- whether for thermochem databases jobs are corrected by zpe db_zpe = kwargs.get('zpe', False) if db_zpe is True: raise ValidationError( 'Zero-point-correction mode \'yes\' not yet implemented.') elif db_zpe is False: pass else: raise ValidationError('Zero-point-correction \'mode\' %s not valid.' % (db_zpe)) # Option benchmark- whether error statistics computed wrt alternate reference energies db_benchmark = 'default' if 'benchmark' in kwargs: db_benchmark = kwargs['benchmark'] if db_benchmark.lower() == 'default': pass else: BIND = p4util.getattr_ignorecase(database, 'BIND_' + db_benchmark) if BIND is None: raise ValidationError( 'Special benchmark \'%s\' not available for database %s.' % (db_benchmark, db_name)) # Option tabulate- whether tables of variables other than primary energy method are formed # TODO db(func=cbs,tabulate=[non-current-energy]) # broken db_tabulate = [] if 'tabulate' in kwargs: db_tabulate = kwargs['tabulate'] # Option subset- whether all of the database or just a portion is run db_subset = HRXN if 'subset' in kwargs: db_subset = kwargs['subset'] if isinstance(db_subset, (str, bytes)): if db_subset.lower() == 'small': try: database.HRXN_SM except AttributeError: raise ValidationError( """Special subset 'small' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_SM elif db_subset.lower() == 'large': try: database.HRXN_LG except AttributeError: raise ValidationError( """Special subset 'large' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_LG elif db_subset.lower() == 'equilibrium': try: database.HRXN_EQ except AttributeError: raise ValidationError( """Special subset 'equilibrium' not available for database %s.""" % (db_name)) else: HRXN = database.HRXN_EQ else: HRXN = p4util.getattr_ignorecase(database, db_subset) if HRXN is None: HRXN = p4util.getattr_ignorecase(database, 'HRXN_' + db_subset) if HRXN is None: raise ValidationError( """Special subset '%s' not available for database %s.""" % (db_subset, db_name)) else: temp = [] for rxn in db_subset: if rxn in HRXN: temp.append(rxn) else: raise ValidationError( """Subset element '%s' not a member of database %s.""" % (str(rxn), db_name)) HRXN = temp temp = [] for rxn in HRXN: temp.append(ACTV['%s-%s' % (dbse, rxn)]) HSYS = p4util.drop_duplicates(sum(temp, [])) # Sow all the necessary reagent computations core.print_out("\n\n") p4util.banner(("Database %s Computation" % (db_name))) core.print_out("\n") # write index of calcs to output file instructions = """\n The database single-job procedure has been selected through mode='continuous'.\n""" instructions += """ Calculations for the reagents will proceed in the order below and will be followed\n""" instructions += """ by summary results for the database.\n\n""" for rgt in HSYS: instructions += """ %-s\n""" % (rgt) core.print_out(instructions) # Loop through chemical systems ERGT = {} ERXN = {} VRGT = {} VRXN = {} for rgt in HSYS: VRGT[rgt] = {} core.print_out('\n') p4util.banner(' Database {} Computation: Reagent {} \n {}'.format( db_name, rgt, TAGL[rgt])) core.print_out('\n') molecule = core.Molecule.from_dict(GEOS[rgt].to_dict()) molecule.set_name(rgt) molecule.update_geometry() if symmetry_override: molecule.reset_point_group('c1') molecule.fix_orientation(True) molecule.fix_com(True) molecule.update_geometry() if (openshell_override) and (molecule.multiplicity() != 1): if user_reference == 'RHF': core.set_global_option('REFERENCE', 'UHF') elif user_reference == 'RKS': core.set_global_option('REFERENCE', 'UKS') core.set_global_option( 'WRITER_FILE_LABEL', user_writer_file_label + ('' if user_writer_file_label == '' else '-') + rgt) if allowoptexceeded: try: ERGT[rgt] = func(molecule=molecule, **kwargs) except ConvergenceError: core.print_out(f"Optimization exceeded cycles for {rgt}") ERGT[rgt] = 0.0 else: ERGT[rgt] = func(molecule=molecule, **kwargs) core.print_variables() core.print_out(" Database Contributions Map:\n {}\n".format('-' * 75)) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if rgt in ACTV[db_rxn]: core.print_out( ' reagent {} contributes by {:.4f} to reaction {}\n'. format(rgt, RXNM[db_rxn][rgt], db_rxn)) core.print_out('\n') for envv in db_tabulate: VRGT[rgt][envv.upper()] = core.variable(envv) core.set_global_option("REFERENCE", user_reference) core.clean() #core.opt_clean() core.clean_variables() # Reap all the necessary reaction computations core.print_out("\n") p4util.banner(("Database %s Results" % (db_name))) core.print_out("\n") maxactv = [] for rxn in HRXN: maxactv.append(len(ACTV[dbse + '-' + str(rxn)])) maxrgt = max(maxactv) table_delimit = '-' * (62 + 20 * maxrgt) tables = '' # find any reactions that are incomplete FAIL = collections.defaultdict(int) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) for i in range(len(ACTV[db_rxn])): if abs(ERGT[ACTV[db_rxn][i]]) < 1.0e-12: if not allowoptexceeded: FAIL[rxn] = 1 # tabulate requested process::environment variables tables += """ For each VARIABLE requested by tabulate, a 'Reaction Value' will be formed from\n""" tables += """ 'Reagent' values according to weightings 'Wt', as for the REQUESTED ENERGY below.\n""" tables += """ Depending on the nature of the variable, this may or may not make any physical sense.\n""" for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) VRXN[db_rxn] = {} for envv in db_tabulate: envv = envv.upper() tables += """\n ==> %s <==\n\n""" % (envv.title()) tables += _tblhead(maxrgt, table_delimit, 2) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8s %8s %8s %8s""" % (db_rxn, '', '****', '', '') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) else: VRXN[db_rxn][envv] = 0.0 for i in range(len(ACTV[db_rxn])): VRXN[db_rxn][envv] += VRGT[ ACTV[db_rxn][i]][envv] * RXNM[db_rxn][ACTV[db_rxn][i]] tables += """\n%23s %16.8f """ % ( db_rxn, VRXN[db_rxn][envv]) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (VRGT[ ACTV[db_rxn][i]][envv], RXNM[db_rxn][ACTV[db_rxn][i]]) tables += """\n %s\n""" % (table_delimit) # tabulate primary requested energy variable with statistics count_rxn = 0 minDerror = 100000.0 maxDerror = 0.0 MSDerror = 0.0 MADerror = 0.0 RMSDerror = 0.0 tables += """\n ==> %s <==\n\n""" % ('Requested Energy') tables += _tblhead(maxrgt, table_delimit, 1) for rxn in HRXN: db_rxn = dbse + '-' + str(rxn) if FAIL[rxn]: tables += """\n%23s %8.4f %8s %10s %10s""" % ( db_rxn, BIND[db_rxn], '****', '****', '****') for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) else: ERXN[db_rxn] = 0.0 for i in range(len(ACTV[db_rxn])): ERXN[db_rxn] += ERGT[ACTV[db_rxn][i]] * RXNM[db_rxn][ ACTV[db_rxn][i]] error = constants.hartree2kcalmol * ERXN[db_rxn] - BIND[db_rxn] tables += """\n%23s %8.4f %8.4f %10.4f %10.4f""" % ( db_rxn, BIND[db_rxn], constants.hartree2kcalmol * ERXN[db_rxn], error, error * constants.cal2J) for i in range(len(ACTV[db_rxn])): tables += """ %16.8f %2.0f""" % (ERGT[ACTV[db_rxn][i]], RXNM[db_rxn][ACTV[db_rxn][i]]) if abs(error) < abs(minDerror): minDerror = error if abs(error) > abs(maxDerror): maxDerror = error MSDerror += error MADerror += abs(error) RMSDerror += error * error count_rxn += 1 tables += """\n %s\n""" % (table_delimit) if count_rxn: MSDerror /= float(count_rxn) MADerror /= float(count_rxn) RMSDerror = math.sqrt(RMSDerror / float(count_rxn)) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Minimal Dev', '', minDerror, minDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Maximal Dev', '', maxDerror, maxDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Mean Signed Dev', '', MSDerror, MSDerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'Mean Absolute Dev', '', MADerror, MADerror * constants.cal2J) tables += """%23s %19s %10.4f %10.4f\n""" % ( 'RMS Dev', '', RMSDerror, RMSDerror * constants.cal2J) tables += """ %s\n""" % (table_delimit) core.set_variable('%s DATABASE MEAN SIGNED DEVIATION' % (db_name), MSDerror) core.set_variable('%s DATABASE MEAN ABSOLUTE DEVIATION' % (db_name), MADerror) core.set_variable('%s DATABASE ROOT-MEAN-SQUARE DEVIATION' % (db_name), RMSDerror) core.print_out(tables) finalenergy = MADerror else: finalenergy = 0.0 optstash.restore() DB_RGT.clear() DB_RGT.update(VRGT) DB_RXN.clear() DB_RXN.update(VRXN) return finalenergy
def run_n_body(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that n_body can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('n_body') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # print("START run_n_body()") # Your plugin's psi4 run sequence goes here psi4.core.set_local_option('MYPLUGIN', 'PRINT', 1) ##### BEGIN TAYLOR'S run_n_body() FUNCTION ##### # Only energy and properties are implemented for now, not sure others even # make sense anyways. db = shelve.open('database', writeback=True) # print(db) # Initialize database if not 'initialized' in db: n_body.initialize_database(db, kwargs) db['initialized'] = True elif not db['initialized']: n_body.initialize_database(db, kwargs) db['initialized'] = True # Remove n_body_func, it's being tracked in the database and we don't want to propagate it if 'n_body_func' in kwargs: kwargs.pop('n_body_func') # Process user requested options n_body_options = n_body.process_options(name, db, kwargs.pop('n_body')) # Compare database options to n_body_options if 'distributed' in n_body_options: db['distributed'] = n_body_options['distributed'] if not db['distributed']: raise Exception( "Currently n_body jobs must be run in distributed mode" " use the n_body wrapper instead.\n") if 'cutoff' in n_body_options: db['cutoff'] = n_body_options['cutoff'] if 'num_threads' in n_body_options: db['num_threads'] = n_body_options['num_threads'] if 'bsse' in n_body_options: db['bsse'] = n_body_options['bsse'] if 'pcm' in n_body_options: db['pcm'] = n_body_options['pcm'] if 'solute' in n_body_options: db['solute'] = n_body_options['solute'] if 'timing' in n_body_options: db['timing'] = n_body_options['timing'] if 'harvest' in n_body_options: db['harvest'] = n_body_options['harvest'] if 'cook' in n_body_options: db['cook'] = n_body_options['cook'] # methods consistency check if 'methods' in n_body_options: for key, val in n_body_options['methods'].items(): if key in db['methods'].keys(): # requested n_body_max smaller than previously requested if val < db['methods'][key]: raise Exception('n_body_max for {} has ' 'decreased.'.format(key)) # requested n_body_max has increased elif val > db['methods'][key]: # save requested n_body_max db['methods'][key] = val # update database entries n_body.extend_database(db, kwargs) # set flag to regenerate inputs db['inputs_generated'] = False else: # method not already in database # add method and n_body_max to database db['methods'][key] = val # update database entries n_body.extend_database(db, kwargs) # set flag to regenerate inputs db['inputs_generated'] = False # Get complete system molecule = psi4.core.get_active_molecule() # Determine interacting fragments if db['cutoff']: i_pairs = n_body.interacting_pairs(db['cutoff']) i_clusters = [] if not db['inputs_generated']: # Create method directories for method, n_body_max in db['methods'].items(): try: os.makedirs(method) except: if os.path.isdir(method): print(method) pass else: raise Exception('Attempt to create {} directory ' 'failed.'.format(method)) # Create n_body_level directories for n in range(1, n_body_max + 1): directory = '{}/{}'.format(method, n_body.n_body_dir(n)) try: os.makedirs(directory) except: if os.path.isdir(directory): print(directory) pass else: raise Exception('Attempt to create {} directory ' 'failed.'.format(directory)) # Create job input files # Grab all ghost functions if SSFC # Else, get all possible clusters without ghost atoms if db['bsse'] == 'ssfc': clusters = psi4.extract_clusters(molecule, True, n) else: clusters = psi4.extract_clusters(molecule, False, n) # Flip distance-dropoff off for now #if db['bsse'] == 'radius' #clusters = extract_clusters(molecule, False, n) #clusters = n_body.expand_basis(clusters, db['basis_cutoff']) # Get list of fragments involved in clusters indexes = psi4.extract_cluster_indexing(molecule, n) print('Length of index array = {}'.format(len(indexes))) # Testing for extension to inclusion of nearby atoms (ghosted) #cluster_string = clusters[0].save_string_xyz_g09() #print(cluster_string) #cluster_string += molecule.extract_subsets(2).save_string_xyz_g09() #print(molecule.extract_subsets(2).save_string_xyz_g09()) #print(clusters[0].save_string_xyz_g09()) #print(cluster_string) #cluster_string = '' # Get interacting clusters if db['cutoff']: if n == 1: # Generate all inputs pass elif n == 2: # generate inputs for i_pairs only for k in range(len(clusters) - 1, -1, -1): clus = indexes[k] if clus in i_pairs: i_clusters.append(clus) else: # Remove non-interacting clusters from lists clusters.pop(k) indexes.pop(k) elif n > 2: prev_clusters = copy.deepcopy(i_clusters) i_clusters = [] for k in range(len(clusters) - 1, -1, -1): # Check if interacting cluster clus = indexes[k] for p_clus in prev_clusters: # previous cluster is contained within current cluster if set(p_clus).issubset(set(clus)): # Get remaining frag number if len(set(clus).difference( set(p_clus))) == 1: l = set(clus).difference( set(p_clus)).pop() else: raise Exception( 'Length of difference ' 'set is greater than 1') for m in p_clus: # Is this an interacting pair? if sorted([l, m], reverse=True) in i_pairs: i_clusters.append(clus) # Don't check anymore pairs break if clus in i_clusters: break else: # Remove non-interacting clusters from lists clusters.pop(k) indexes.pop(k) # Create input files db[method][n]['total_num_jobs'] = len(clusters) print('n = {}'.format(n)) print('Number of jobs created = {}'.format(len(clusters))) for k in range(len(clusters)): psi4.activate(clusters[k]) cluster = psi4.core.get_active_molecule() # db[method][n]['MW'][job] = value ### Set-up things for input generation # Get list of combinations to ghost # MBCP if db['bsse'] == 'mbcp' and n > 1: mbcp = list(itertools.combinations(indexes[k], n - 1)) for ghost in mbcp: directory = '{}/{}/{}'.format( method, n_body.n_body_dir(n), n_body.ghost_dir(indexes[k], ghost)) cluster.set_ghost_fragments(list(ghost)) cluster.update_geometry() cluster.set_name('{}_{}'.format( molecule.name(), n_body.ghost_dir(indexes[k], ghost))) n_body.plant(cluster, db, kwargs, method, directory) # Update job_status dict n_basis = n n_real = n - len(ghost) ghost_jobs = '{}-{}r'.format(n_basis, n_real) # db[method][ghost_jobs]['total_num_jobs'] = len(mbcp) * len(clusters) # db[method][ghost_jobs]['job_status'].update({n_body.ghost_dir(indexes[k],ghost): # 'not_started'}) db[method][n]['total_num_jobs'] = len(mbcp) * len( clusters) db[method][n]['job_status'].update({ n_body.ghost_dir(indexes[k], ghost): 'not_started' }) # Calculate molecular weight totalmass = sum( mol2.mass(i) for i in range(mol2.natom()) if mol2.Z(i)) totmass = sum( cluster.mass(i) for i in range(cluster.natom()) if cluster.Z(i)) db[method][n]['MW'].update( {n_body.ghost_dir(indexes[k], ghost): totmass}) # Reactivate fragments for next round cluster.set_active_fragments(list(ghost)) cluster.update_geometry() # VMFC elif db['bsse'] == 'vmfc': vmfc = [] for n_ghost in range(n - 1, 0, -1): vmfc.extend( list( itertools.combinations( indexes[k], n_ghost))) for ghost in vmfc: directory = '{}/{}/{}'.format( method, n_body.n_body_dir(n), n_body.ghost_dir(indexes[k], ghost)) cluster.set_ghost_fragments(list(ghost)) cluster.update_geometry() cluster.set_name('{}_{}'.format( molecule.name(), n_body.ghost_dir(indexes[k], ghost))) n_body.plant(cluster, db, kwargs, method, directory) # Update job_status dict n_basis = n n_real = n - len(ghost) ghost_jobs = '{}-{}r'.format(n_basis, n_real) #db[method][ghost_jobs]['total_num_jobs'] = len(vmfc) * len(clusters) # db[method][ghost_jobs]['total_num_jobs'] = len(list(itertools.combinations(range(0,n),n_real))) * len(clusters) # db[method][ghost_jobs]['job_status'].update({n_body.ghost_dir(indexes[k],ghost): # 'not_started'}) db[method][n]['total_num_jobs'] = len( list( itertools.combinations( range(0, n), n_real))) * len(clusters) db[method][n]['job_status'].update({ n_body.ghost_dir(indexes[k], ghost): 'not_started' }) # Calculate molecular weight totmass = sum( cluster.mass(i) for i in range(cluster.natom()) if cluster.Z(i)) db[method][n]['MW'].update( {n_body.ghost_dir(indexes[k], ghost): totmass}) # Reactivate fragments for next round cluster.set_active_fragments(list(ghost)) cluster.update_geometry() # SSFC or no BSSE else: cluster.set_name('{}_{}'.format( molecule.name(), n_body.cluster_dir(indexes[k]))) #molecule.set_name('{}_{}'.format(molecule.name(),cluster_dir(indexes[k]))) directory = '{}/{}/{}'.format( method, n_body.n_body_dir(n), n_body.cluster_dir(indexes[k])) n_body.plant(cluster, db, kwargs, method, directory) # Update database job_status dict db[method][n]['job_status'].update( {n_body.cluster_dir(indexes[k]): 'not_started'}) # Calculate molecular weight totmass = sum( cluster.mass(i) for i in range(cluster.natom()) if cluster.Z(i)) db[method][n]['MW'].update( {n_body.cluster_dir(indexes[k]): totmass}) # Check for zero jobs (happens occasionally due to cutoff) for method in db['methods']: for n in range(1, db[method]['n_body_max'] + 1): if db[method][n]['total_num_jobs'] == 0: db[method]['n_body_max'] = n - 1 break # Inputs successfully generated db['inputs_generated'] = True # For now open and close database frequently, until I figure out atexit db.close() db = shelve.open('database', writeback=True) # Check status of jobs if not db['jobs_complete']: n_incomplete = 0 # Check all methods for method in db['methods'].keys(): #### NOTE: dft_methods is defunct ##### # if method in n_body.dft_methods: # outname = 'input.log' # complete_message = 'Normal termination of Gaussian' # if (method == 'b3lyp'): if method in n_body.dft_methods: outname = 'input.log' complete_message = 'Normal termination of Gaussian' error_message = 'Error termination' else: outname = 'output.dat' complete_message = 'Psi4 exiting successfully' error_message = 'Psi4 encountered an error' # Check all n_body_levels print('Before job checking:') for field in db[method]['farm']: num_fin = db[method][field]['num_jobs_complete'] tot_num = db[method][field]['total_num_jobs'] print('{}/{} {}-body jobs finished'.format( num_fin, tot_num, field)) n_complete = num_fin if num_fin != tot_num: db_stat = db[method][field]['job_status'] n_complete = 0 for job, status in db_stat.items(): if status == 'complete': n_complete += 1 elif status in ('not_started', 'running', 'error'): try: outfile = open('{}/{}/{}/{}'.format( method, n_body.n_body_dir(field), job, outname)) for line in outfile: if complete_message in line: # This actually marks the job complete as soon as # the first route section is complete in the case # of g09, not when the job is totally finished. db_stat[job] = 'complete' n_complete += 1 break elif error_message in line: db_stat[job] = 'error' break else: db_stat[job] = 'running' n_incomplete += 1 except: # this print statement is super annoying, muting it for now # print('Exception: probably could not open file for {}'.format(job)) n_incomplete += 1 db[method][field]['num_jobs_complete'] = n_complete if n_incomplete == 0: db['jobs_complete'] = True db.close() db = shelve.open('database', writeback=True) # Gather results if not db['results_computed']: for method in db['methods'].keys(): print('\nAfter job checking:') for field in db[method]['farm']: num_fin = db[method][field]['num_jobs_complete'] tot_num = db[method][field]['total_num_jobs'] print('{}/{} {}-body jobs finished'.format( num_fin, tot_num, field)) if (db[method][field]['num_jobs_complete'] == db[method][field] ['total_num_jobs']): if db['harvest']: if method in n_body.dft_methods: n_body.harvest_g09(db, method, field) else: n_body.harvest_data(db, method, field) if not db['harvest']: print("Data harvesting has been disabled.") elif not db['cook']: print("Data cooking has been disabled.") else: for field in db[method]['farm']: if (db[method][field]['num_jobs_complete'] == db[method] [field]['total_num_jobs']): n_body.cook_data(db, method, field) # #if db['bsse'] == 'vmfc' and field > 1: # # n_body.vmfc_cook(db, method, field) # # n_body.mbcp_cook(db, method, field) # if db['bsse'] == 'vmfc': # n_body.vmfc_cook(db, method, field) # if field > 1: # n_body.mbcp_cook(db, method, field) # elif db['bsse'] == 'mbcp' and field > 1: # n_body.mbcp_cook(db,method,field) # # Future location of an if check for mass adjust or not # if 'rotation' in db[method]['results']: # n_body.mass_adjust_rotations(db,method,field) db.close() db = shelve.open('database', writeback=True) # Print banner to output file including user options n_body.banner(db) db.close() pass
def run_psixas(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that psixas can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('psixas') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) #print the banner printBanner() mol = kwargs["molecule"] func = kwargs["functional"] tmp = psi4.core.get_local_option("PSIXAS", "MODE") mode = tmp.split("+") if not (all([x in ["GS", "LOC", "EX", "SPEC"] for x in mode])): raise Exception("Wrong mode, possible values are GS, LOC, EX, SPEC.") if "GS" in mode: DFTGroundState(mol, func, PREFIX=psi4.core.get_local_option("PSIXAS", "PREFIX")) if "LOC" in mode: loc_sub = np.array(psi4.core.get_local_option("PSIXAS", "LOC_SUB"), dtype=np.int) wfn = psi4.core.Wavefunction.build( mol, psi4.core.get_global_option('BASIS')) nbf = wfn.nso() sup = psi4.driver.dft.build_superfunctional(func, False)[0] sup.set_deriv(2) sup.allocate() uhf = psi4.core.UHF(wfn, sup) prefix = psi4.core.get_local_option("PSIXAS", "PREFIX") Ca = np.load(prefix + "_gsorbs.npz")["Ca"] Cb = np.load(prefix + "_gsorbs.npz")["Cb"] occa = np.load(prefix + "_gsorbs.npz")["occa"] occb = np.load(prefix + "_gsorbs.npz")["occb"] epsa = np.load(prefix + "_gsorbs.npz")["epsa"] epsb = np.load(prefix + "_gsorbs.npz")["epsb"] locCa = psi4.core.Matrix(wfn.nso(), len(loc_sub)) locCb = psi4.core.Matrix(wfn.nso(), len(loc_sub)) locCa.np[:] = np.copy(Ca[:, loc_sub]) locCb.np[:] = np.copy(Cb[:, loc_sub]) LocalA = psi4.core.Localizer.build("PIPEK_MEZEY", wfn.basisset(), locCa) LocalB = psi4.core.Localizer.build("PIPEK_MEZEY", wfn.basisset(), locCb) LocalA.localize() LocalB.localize() Ca[:, loc_sub] = LocalA.L Cb[:, loc_sub] = LocalB.L np.savez(prefix + '_gsorbs', Ca=Ca, Cb=Cb, occa=occa, occb=occb) psi4.core.print_out("Localized Orbitals written") OCCA = psi4.core.Vector(nbf) OCCB = psi4.core.Vector(nbf) OCCA.np[:] = occa OCCB.np[:] = occb uhf.Ca().np[:] = Ca uhf.Cb().np[:] = Cb uhf.epsilon_a().np[:] = epsa uhf.epsilon_b().np[:] = epsb uhf.occupation_a().np[:] = occa uhf.occupation_b().np[:] = occb mw = psi4.core.MoldenWriter(uhf) mw.write(prefix + '_loc.molden', uhf.Ca(), uhf.Cb(), uhf.epsilon_a(), uhf.epsilon_b(), OCCA, OCCB, True) psi4.core.print_out("Moldenfile written\n") print("Localize subset") print(loc_sub) orbitals = [] if ("EX" in mode): orbs = psi4.core.get_local_option("PSIXAS", "ORBS") occs = psi4.core.get_local_option("PSIXAS", "OCCS") freeze = psi4.core.get_local_option("PSIXAS", "FREEZE") spin = psi4.core.get_local_option("PSIXAS", "SPIN") ovl = psi4.core.get_local_option("PSIXAS", "OVL") lens = [len(x) for x in [orbs, occs, freeze, spin, ovl]] if len(list((set(lens)))) > 1: raise Exception("Input arrays have inconsistent length" + " ".join(str(lens))) for i in range(len(orbs)): orbitals.append({ "orb": orbs[i], "spin": spin[i].lower(), "occ": occs[i], "frz": freeze[i] == "T", "DoOvl": ovl[i] == "T" }) DFTExcitedState(mol, func, orbitals) if ("SPEC" in mode): CalcSpec(mol, func) #psixas_wfn = psi4.core.plugin('psixas.so', wfn) return 0 #psixas_wfn
def nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction quantities. :returns: *return type of func* |w--w| The interaction data. :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :py:class:`~psi4.core.Wavefunction` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. """ # Initialize dictionaries for easy data passing metadata, component_results, nbody_results = {}, {}, {} # Parse some kwargs kwargs = p4util.kwargs_lower(kwargs) metadata['ptype'] = kwargs.pop('ptype', None) metadata['return_wfn'] = kwargs.pop('return_wfn', False) metadata['return_total_data'] = kwargs.pop('return_total_data', False) metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule()) metadata['molecule'].update_geometry() metadata['kwargs'] = kwargs core.clean_variables() if metadata['ptype'] not in ['energy', 'gradient', 'hessian']: raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % metadata['ptype']) # Parse bsse_type, raise exception if not provided or unrecognized metadata['bsse_type_list'] = kwargs.pop('bsse_type') if metadata['bsse_type_list'] is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(metadata['bsse_type_list'], list): metadata['bsse_type_list'] = [metadata['bsse_type_list']] for num, btype in enumerate(metadata['bsse_type_list']): metadata['bsse_type_list'][num] = btype.lower() if btype.lower() not in ['cp', 'nocp', 'vmfc']: raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) metadata['max_nbody'] = kwargs.get('max_nbody', -1) metadata['max_frag'] = metadata['molecule'].nfragments() if metadata['max_nbody'] == -1: metadata['max_nbody'] = metadata['molecule'].nfragments() else: metadata['max_nbody'] = min(metadata['max_nbody'], metadata['max_frag']) # 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 = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = metadata['bsse_type_list'][0] if len(metadata['bsse_type_list']) > 1: bsse_str = str(metadata['bsse_type_list']) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.print_out(" BSSE Treatment: %s\n" % bsse_str) # Get compute list metadata = build_nbody_compute_list(metadata) # Compute N-Body components component_results = compute_nbody_components(func, method_string, metadata) # Assemble N-Body quantities nbody_results = assemble_nbody_components(metadata, component_results) # Figure out returns if metadata['ptype'] != 'energy': if metadata['return_total_data']: np_final_ptype = nbody_results['ptype_body_dict'][metadata['max_nbody']].copy() else: np_final_ptype = nbody_results['ptype_body_dict'][metadata['max_nbody']].copy() np_final_ptype -= ptype_body_dict[1] nbody_results['ret_ptype'] = core.Matrix.from_array(np_final_ptype) else: nbody_results['ret_ptype'] = nbody_results['ret_energy'] # Build wfn and bind variables wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp') dicts = ['energies', 'ptype', 'intermediates', 'energy_body_dict', 'ptype_body_dict', 'nbody'] for r in [component_results, nbody_results]: for d in r: if d in dicts: for var, value in r[d].items(): wfn.set_variable(str(var), value) core.set_variable(str(var), value) if metadata['ptype'] == 'gradient': wfn.set_gradient(ret_ptype) elif metadata['ptype'] == 'hessian': wfn.set_hessian(ret_ptype) core.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) if metadata['return_wfn']: return (nbody_results['ret_ptype'], wfn) else: return nbody_results['ret_ptype']
def run_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('forte') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # 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 = psi4.driver.scf_helper(name, **kwargs) # Get the option object options = psi4.core.get_options() options.set_current_module('FORTE') forte.forte_options.update_psi_options(options) if ('DF' in options.get_str('INT_TYPE')): aux_basis = psi4.core.BasisSet.build( ref_wfn.molecule(), 'DF_BASIS_MP2', psi4.core.get_global_option('DF_BASIS_MP2'), 'RIFIT', psi4.core.get_global_option('BASIS')) ref_wfn.set_basisset('DF_BASIS_MP2', aux_basis) if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'MINAO_BASIS', options.get_str('MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Start Forte, initialize ambit my_proc_n_nodes = forte.startup() my_proc, n_nodes = my_proc_n_nodes # Print the banner forte.banner() # Create the MOSpaceInfo object mo_space_info = forte.make_mo_space_info(ref_wfn, forte.forte_options) # Create the AO subspace projector ps = forte.make_aosubspace_projector(ref_wfn, options) state = forte.make_state_info_from_psi_wfn(ref_wfn) scf_info = forte.SCFInfo(ref_wfn) state_weights_map = forte.make_state_weights_map(forte.forte_options, ref_wfn) # Run a method job_type = options.get_str('JOB_TYPE') energy = 0.0 if job_type != 'NONE': start = timeit.timeit() # Make an integral object ints = forte.make_forte_integrals(ref_wfn, options, mo_space_info) # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, forte.forte_options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua, Ub) # Run a method if (job_type == 'NEWDRIVER'): energy = forte_driver(state_weights_map, scf_info, forte.forte_options, ints, mo_space_info) else: energy = forte.forte_old_methods(ref_wfn, options, ints, mo_space_info) end = timeit.timeit() #print('\n\n Your calculation took ', (end - start), ' seconds'); # Close ambit, etc. forte.cleanup() psi4.core.set_scalar_variable('CURRENT ENERGY', energy) return ref_wfn
def prepare_psi4_ref_wfn(options, **kwargs): """ Prepare a Psi4 Wavefunction as reference for Forte. :param options: a ForteOptions object for options :param kwargs: named arguments associated with Psi4 :return: (the processed Psi4 Wavefunction, a Forte MOSpaceInfo object) Notes: We will create a new Psi4 Wavefunction (wfn_new) if necessary. 1. For an empty ref_wfn, wfn_new will come from Psi4 SCF or MCSCF. 2. For a valid ref_wfn, we will test the orbital orthonormality against molecule. If the orbitals from ref_wfn are consistent with the active geometry, wfn_new will simply be a link to ref_wfn. If not, we will rerun a Psi4 SCF and orthogonalize orbitals, where wfn_new comes from this new Psi4 SCF computation. """ p4print = psi4.core.print_out # grab reference Wavefunction and Molecule from kwargs kwargs = p4util.kwargs_lower(kwargs) ref_wfn = kwargs.get('ref_wfn', None) molecule = kwargs.pop('molecule', psi4.core.get_active_molecule()) point_group = molecule.point_group().symbol() # try to read orbitals from file Ca = read_orbitals() if options.get_bool('READ_ORBITALS') else None need_orbital_check = True fresh_ref_wfn = True if ref_wfn is None else False if ref_wfn is None: ref_type = options.get_str('REF_TYPE') p4print('\n No reference wave function provided for Forte.' f' Computing {ref_type} orbitals using Psi4 ...\n') # no warning printing for MCSCF job_type = options.get_str('JOB_TYPE') do_mcscf = (job_type in ["CASSCF", "MCSCF_TWO_STEP"] or options.get_bool("CASSCF_REFERENCE")) # run Psi4 SCF or MCSCF ref_wfn = run_psi4_ref(ref_type, molecule, not do_mcscf, **kwargs) need_orbital_check = False if Ca is None else True else: # Ca from file has higher priority than that of ref_wfn Ca = ref_wfn.Ca().clone() if Ca is None else Ca # build Forte MOSpaceInfo nmopi = ref_wfn.nmopi() mo_space_info = forte.make_mo_space_info(nmopi, point_group, options) # do we need to check MO overlap? if not need_orbital_check: wfn_new = ref_wfn else: # test if input Ca has the correct dimension if Ca.rowdim() != nmopi or Ca.coldim() != nmopi: raise ValueError( "Invalid orbitals: different basis set / molecule") new_S = psi4.core.Wavefunction.build(molecule, options.get_str("BASIS")).S() if check_MO_orthonormality(new_S, Ca): wfn_new = ref_wfn wfn_new.Ca().copy(Ca) else: if fresh_ref_wfn: wfn_new = ref_wfn wfn_new.Ca().copy(ortho_orbs_forte(wfn_new, mo_space_info, Ca)) else: p4print("\n Perform new SCF at current geometry ...\n") kwargs_copy = { k: v for k, v in kwargs.items() if k != 'ref_wfn' } wfn_new = run_psi4_ref('scf', molecule, False, **kwargs_copy) # orthonormalize orbitals wfn_new.Ca().copy(ortho_orbs_forte(wfn_new, mo_space_info, Ca)) # copy wfn_new to ref_wfn ref_wfn.shallow_copy(wfn_new) # set DF and MINAO basis if 'DF' in options.get_str('INT_TYPE'): aux_basis = psi4.core.BasisSet.build(molecule, 'DF_BASIS_MP2', options.get_str('DF_BASIS_MP2'), 'RIFIT', options.get_str('BASIS')) wfn_new.set_basisset('DF_BASIS_MP2', aux_basis) if options.get_str('MINAO_BASIS'): minao_basis = psi4.core.BasisSet.build(molecule, 'MINAO_BASIS', options.get_str('MINAO_BASIS')) wfn_new.set_basisset('MINAO_BASIS', minao_basis) return wfn_new, mo_space_info
def gradient_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> gradient('forte') available for : CASSCF """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # 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 = psi4.driver.scf_helper(name, **kwargs) # Get the psi4 option object optstash = p4util.OptionsState(['GLOBALS', 'DERTYPE']) psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') # Get the forte option object options = forte.forte_options options.get_options_from_psi4(psi4_options) if ('DF' in options.get_str('INT_TYPE')): raise Exception('analytic gradient is not implemented for density fitting') if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'MINAO_BASIS', options.get_str('MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Start Forte, initialize ambit my_proc_n_nodes = forte.startup() my_proc, n_nodes = my_proc_n_nodes # Print the banner forte.banner() # Create the MOSpaceInfo object mo_space_info = forte.make_mo_space_info(ref_wfn, options) # Call methods that project the orbitals (AVAS, embedding) mo_space_info = orbital_projection(ref_wfn, options, mo_space_info) state = forte.make_state_info_from_psi_wfn(ref_wfn) scf_info = forte.SCFInfo(ref_wfn) state_weights_map = forte.make_state_weights_map(options,ref_wfn) # Run a method job_type = options.get_str('JOB_TYPE') energy = 0.0 if not job_type == 'CASSCF': raise Exception('analytic gradient is only implemented for CASSCF') start = time.time() # Make an integral object ints = forte.make_forte_integrals(ref_wfn, options, mo_space_info) # Rotate orbitals before computation orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua,Ub) # Run gradient computation energy = forte.forte_old_methods(ref_wfn, options, ints, mo_space_info) derivobj = psi4.core.Deriv(ref_wfn) derivobj.set_deriv_density_backtransformed(True) derivobj.set_ignore_reference(True) grad = derivobj.compute() #psi4.core.DerivCalcType.Correlated ref_wfn.set_gradient(grad) optstash.restore() end = time.time() #print('\n\n Your calculation took ', (end - start), ' seconds'); # Close ambit, etc. forte.cleanup() return ref_wfn
def nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction quantities. :returns: *return type of func* |w--w| The interaction data. :returns: (*float*, :ref:`Wavefunction<sec:psimod_Wavefunction>`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :ref:`Wavefunction<sec:psimod_Wavefunction>` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. """ ### ==> 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', core.get_active_molecule()) molecule.update_geometry() core.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 = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.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) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.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] core.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(): core.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]): core.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] = core.get_variable("CURRENT ENERGY") core.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): # core.set_global_option('DF_INTS_IO', 'LOAD') core.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} vmfc_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] core.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) core.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.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] core.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) core.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 core.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] core.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) core.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 core.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 = core.Matrix.from_array(np_final_ptype) else: ret_ptype = ret_energy # Build and set a wavefunction wfn = core.Wavefunction.build(molecule, 'sto-3g') wfn.cdict["nbody_energy"] = energies_dict wfn.cdict["nbody_ptype"] = ptype_dict wfn.cdict["nbody_body_energy"] = energy_body_dict wfn.cdict["nbody_body_ptype"] = ptype_body_dict if ptype == 'gradient': wfn.set_gradient(ret_ptype) elif ptype == 'hessian': wfn.set_hessian(ret_ptype) core.set_variable("CURRENT ENERGY", ret_energy) if return_wfn: return (ret_ptype, wfn) else: return ret_ptype
def run_efp_gamess(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that efp_gamess can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('efp_gamess') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # Your plugin's psi4 run sequence goes here psi4.core.set_local_option('MYPLUGIN', 'PRINT', 1) # Compute a SCF reference, a wavefunction is return which holds the molecule used, orbitals # Fock matrices, and more print('Attention! This SCF may be density-fitted.') ref_wfn = kwargs.get('ref_wfn', None) if ref_wfn is None: ref_wfn = psi4.driver.scf_helper(name, **kwargs) # Ensure IWL files have been written when not using DF/CD proc_util.check_iwl_file_from_scf_type( psi4.core.get_option('SCF', 'SCF_TYPE'), ref_wfn) # Call the Psi4 plugin # Please note that setting the reference wavefunction in this way is ONLY for plugins # This prints a bound python method print("\nPrinting ref_wfn.Fa") print(ref_wfn.Fa) # This defines a psi4.core.Matrix object and prints it print("\nPrinting np.asarray(ref_wfn.Fa())") print(np.asarray(ref_wfn.Fa())) #Farray=np.asarray(ref_wfn.Fa()) Farray = ref_wfn.Fa().to_array() print("\nPrinting Farray, which is np.asarray(ref_wfn.Fa())") print(Farray) print("\nPrinting Farray asarray") print(np.asarray(Farray)) # I think this does nothing different from the above Fa = ref_wfn.Fa() print("\nPrinting Fa=ref_wfn.Fa()") print(Fa) print("\nPrinting np.asarray(Fa)") print(np.asarray(Fa)) # This prints to the output the matrix values #Fa.print_out() # This actually changes Fa and the ref_wfn Fa value #Fa.set(0,0,0.0) #Fa.print_out() #ref_wfn.Fa=Fa # Tests my previous assertion about ref_wfn changes #Fa_zeroed=ref_wfn.Fa() #Fa_zeroed.print_out() # Change return name to the trans matrix I'm trying to send back #efp_gamess_wfn = psi4.core.plugin('efp_gamess.so', ref_wfn) psi4.core.set_local_option('efp_gamess', 'coleman', 0) trans_mat_c = psi4.core.plugin('efp_gamess.so', ref_wfn) print("\nPrinting C transformation matrix") print(trans_mat_c) print("\nPrinting C transformation matrix (as array)") print(np.asarray(trans_mat_c)) trans_mat_c.print_out() psi4.core.print_out("\nalright coleman H coming now") psi4.core.set_local_option('efp_gamess', 'coleman', 1) trans_mat_h = psi4.core.plugin('efp_gamess.so', ref_wfn) print("\nPrinting H transformation matrix") print(trans_mat_h) print("\nPrinting H transformation matrix (as array)") print(np.asarray(trans_mat_h)) trans_mat_h.print_out() # Define hdf5 file f = h5py.File("form.h5", "r") group = f["EFPcalc"] print("\nListing dataset in h5 file EFPcalc group") print(list(group.keys())) fock_dset = group['CONVERGED TOTAL FOCK MATRIX'] fock_np = np.array(fock_dset) print("\nGAMESS Fock matrix as numpy array") print(fock_np) mo_dset = group['MO_coeff'] mo_np = np.array(mo_dset) print("\nGAMESS MO coeficients") print(mo_np) print("\nPSI4 MO coefficients") print("\nTransforming MO coefficients") psi4_C = np.matmul(trans_mat_c, np.transpose(mo_np)) print("\nTransformed MO coefficients") print(psi4_C) # Test adding these together #print("Adding np.asarray(trans_mat_h)+np.asarray(trans_mat_c)\n") #test_sum=np.asarray(trans_mat_h)+np.asarray(trans_mat_c) #print(test_sum) #test_sum2=np.asarray(Fa)+np.asarray(trans_mat_c) #print(test_sum2) #test_sum.print_out() # Find out what trans_mat is (it's like the others) #print(trans_mat) # print actual values #trans_mat.print_out() # Change return to original ref_wfn, hopefully changed #return efp_gamess_wfn return ref_wfn
def nbody_gufunc(func: Union[str, Callable], method_string: str, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction and total quantities. :returns: *return type of func* |w--w| The data. :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: Callable :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: str :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :py:class:`~psi4.core.Wavefunction` calculation result as the second element of a tuple. :type bsse_type: str or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: str :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. :type levels: dict :param levels: ``{1: 'ccsd(t)', 2: 'mp2', 'supersystem': 'scf'}`` || ``{1: 2, 2: 'ccsd(t)', 3: 'mp2'}`` || etc Dictionary of different levels of theory for different levels of expansion Note that method_string is not used in this case. supersystem computes all higher order n-body effects up to nfragments. :type embedding_charges: dict :param embedding_charges: ``{1: [-0.834, 0.417, 0.417], ..}`` Dictionary of atom-centered point charges. keys: 1-based index of fragment, values: list of charges for each fragment. :type charge_method: str :param charge_method: ``scf/6-31g`` || ``b3lyp/6-31g*`` || etc Method to compute point charges for monomers. Overridden by embedding_charges if both are provided. :type charge_type: str :param charge_type: ``MULLIKEN_CHARGES`` || ``LOWDIN_CHARGES`` Default is ``MULLIKEN_CHARGES`` """ # Initialize dictionaries for easy data passing metadata, component_results, nbody_results = {}, {}, {} # Parse some kwargs kwargs = p4util.kwargs_lower(kwargs) if kwargs.get('levels', False): return driver_nbody_helper.multi_level(func, **kwargs) metadata['ptype'] = kwargs.pop('ptype', None) metadata['return_wfn'] = kwargs.pop('return_wfn', False) metadata['return_total_data'] = kwargs.pop('return_total_data', False) metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule()) metadata['molecule'].update_geometry() metadata['molecule'].fix_com(True) metadata['molecule'].fix_orientation(True) metadata['embedding_charges'] = kwargs.get('embedding_charges', False) metadata['kwargs'] = kwargs core.clean_variables() if metadata['ptype'] not in ['energy', 'gradient', 'hessian']: raise ValidationError( """N-Body driver: The ptype '%s' is not regonized.""" % metadata['ptype']) # Parse bsse_type, raise exception if not provided or unrecognized metadata['bsse_type_list'] = kwargs.pop('bsse_type') if metadata['bsse_type_list'] is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(metadata['bsse_type_list'], list): metadata['bsse_type_list'] = [metadata['bsse_type_list']] for num, btype in enumerate(metadata['bsse_type_list']): metadata['bsse_type_list'][num] = btype.lower() if btype.lower() not in ['cp', 'nocp', 'vmfc']: raise ValidationError( "N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) metadata['max_nbody'] = kwargs.get('max_nbody', -1) if metadata['molecule'].nfragments() == 1: raise ValidationError( "N-Body requires active molecule to have more than 1 fragment.") metadata['max_frag'] = metadata['molecule'].nfragments() if metadata['max_nbody'] == -1: metadata['max_nbody'] = metadata['molecule'].nfragments() else: metadata['max_nbody'] = min(metadata['max_nbody'], metadata['max_frag']) # 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 = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = metadata['bsse_type_list'][0] if len(metadata['bsse_type_list']) > 1: bsse_str = str(metadata['bsse_type_list']) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.print_out(" BSSE Treatment: %s\n" % bsse_str) # Get compute list metadata = build_nbody_compute_list(metadata) # Compute N-Body components component_results = compute_nbody_components(func, method_string, metadata) # Assemble N-Body quantities nbody_results = assemble_nbody_components(metadata, component_results) # Build wfn and bind variables wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp') dicts = [ 'energies', 'ptype', 'intermediates', 'energy_body_dict', 'gradient_body_dict', 'hessian_body_dict', 'nbody', 'cp_energy_body_dict', 'nocp_energy_body_dict', 'vmfc_energy_body_dict' ] if metadata['ptype'] == 'gradient': wfn.set_gradient(nbody_results['ret_ptype']) nbody_results['gradient_body_dict'] = nbody_results['ptype_body_dict'] elif metadata['ptype'] == 'hessian': nbody_results['hessian_body_dict'] = nbody_results['ptype_body_dict'] wfn.set_hessian(nbody_results['ret_ptype']) component_results_gradient = component_results.copy() component_results_gradient['ptype'] = component_results_gradient[ 'gradients'] metadata['ptype'] = 'gradient' nbody_results_gradient = assemble_nbody_components( metadata, component_results_gradient) wfn.set_gradient(nbody_results_gradient['ret_ptype']) nbody_results['gradient_body_dict'] = nbody_results_gradient[ 'ptype_body_dict'] for r in [component_results, nbody_results]: for d in r: if d in dicts: for var, value in r[d].items(): try: wfn.set_scalar_variable(str(var), value) core.set_scalar_variable(str(var), value) except: wfn.set_array_variable( d.split('_')[0].upper() + ' ' + str(var), core.Matrix.from_array(value)) core.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) if metadata['ptype'] == 'gradient': core.set_variable("CURRENT GRADIENT", nbody_results['ret_ptype']) elif metadata['ptype'] == 'hessian': core.set_variable("CURRENT HESSIAN", nbody_results['ret_ptype']) if metadata['return_wfn']: return (nbody_results['ret_ptype'], wfn) else: return nbody_results['ret_ptype']
def nbody_gufunc(func, method_string, **kwargs): """ Computes the nbody interaction energy, gradient, or Hessian depending on input. This is a generalized univeral function for computing interaction quantities. :returns: *return type of func* |w--w| The interaction data. :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :py:class:`~psi4.core.Wavefunction` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. """ ### ==> 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', core.get_active_molecule()) molecule.update_geometry() core.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 = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.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) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.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] core.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(): core.print_out("\n ==> N-Body: Now computing %d-body complexes <==\n\n" % n) total = len(compute_list[n]) for num, pair in enumerate(compute_list[n]): core.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] = core.get_variable("CURRENT ENERGY") core.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): # core.set_global_option('DF_INTS_IO', 'LOAD') core.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} vmfc_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] core.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody]) core.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy) for n in nbody_range[1:]: var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n core.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] core.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody]) core.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 core.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] core.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody]) core.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 core.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 = core.Matrix.from_array(np_final_ptype) else: ret_ptype = ret_energy # Build and set a wavefunction wfn = core.Wavefunction.build(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) core.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. This is a generalized univeral function for computing interaction and total quantities. :returns: *return type of func* |w--w| The data. :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified. :type func: function :param func: ``energy`` || etc. Python function that accepts method_string and a molecule. Returns a energy, gradient, or Hessian as requested. :type method_string: string :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc. First argument, lowercase and usually unlabeled. Indicates the computational method to be passed to func. :type molecule: :ref:`molecule <op_py_molecule>` :param molecule: ``h2o`` || etc. The target molecule, if not the last molecule defined. :type return_wfn: :ref:`boolean <op_py_boolean>` :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr| Indicate to additionally return the :py:class:`~psi4.core.Wavefunction` calculation result as the second element of a tuple. :type bsse_type: string or list :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc. Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function. By default, this function is not called. :type max_nbody: int :param max_nbody: ``3`` || etc. Maximum n-body to compute, cannot exceed the number of fragments in the moleucle. :type ptype: string :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'`` Type of the procedure passed in. :type return_total_data: :ref:`boolean <op_py_boolean>` :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr| If True returns the total data (energy/gradient/etc) of the system, otherwise returns interaction data. :type levels: dict :param levels: ``{1: 'ccsd(t)', 2: 'mp2', 'supersystem': 'scf'}`` || ``{1: 2, 2: 'ccsd(t)', 3: 'mp2'}`` || etc Dictionary of different levels of theory for different levels of expansion Note that method_string is not used in this case. supersystem computes all higher order n-body effects up to nfragments. :type embedding_charges: dict :param embedding_charges: ``{1: [-0.834, 0.417, 0.417], ..}`` Dictionary of atom-centered point charges. keys: 1-based index of fragment, values: list of charges for each fragment. :type charge_method: string :param charge_method: ``scf/6-31g`` || ``b3lyp/6-31g*`` || etc Method to compute point charges for monomers. Overridden by embedding_charges if both are provided. :type charge_type: string :param charge_type: ``MULLIKEN_CHARGES`` || ``LOWDIN_CHARGES`` Default is ``MULLIKEN_CHARGES`` """ # Initialize dictionaries for easy data passing metadata, component_results, nbody_results = {}, {}, {} # Parse some kwargs kwargs = p4util.kwargs_lower(kwargs) if kwargs.get('levels', False): return driver_nbody_helper.multi_level(func, **kwargs) metadata['ptype'] = kwargs.pop('ptype', None) metadata['return_wfn'] = kwargs.pop('return_wfn', False) metadata['return_total_data'] = kwargs.pop('return_total_data', False) metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule()) metadata['molecule'].update_geometry() metadata['molecule'].fix_com(True) metadata['molecule'].fix_orientation(True) metadata['embedding_charges'] = kwargs.get('embedding_charges', False) metadata['kwargs'] = kwargs core.clean_variables() if metadata['ptype'] not in ['energy', 'gradient', 'hessian']: raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % metadata['ptype']) # Parse bsse_type, raise exception if not provided or unrecognized metadata['bsse_type_list'] = kwargs.pop('bsse_type') if metadata['bsse_type_list'] is None: raise ValidationError("N-Body GUFunc: Must pass a bsse_type") if not isinstance(metadata['bsse_type_list'], list): metadata['bsse_type_list'] = [metadata['bsse_type_list']] for num, btype in enumerate(metadata['bsse_type_list']): metadata['bsse_type_list'][num] = btype.lower() if btype.lower() not in ['cp', 'nocp', 'vmfc']: raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower()) metadata['max_nbody'] = kwargs.get('max_nbody', -1) metadata['max_frag'] = metadata['molecule'].nfragments() if metadata['max_nbody'] == -1: metadata['max_nbody'] = metadata['molecule'].nfragments() else: metadata['max_nbody'] = min(metadata['max_nbody'], metadata['max_frag']) # 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 = core.get_global_option('DF_INTS_IO') # # inquire if above at all applies to dfmp2 or just scf # core.set_global_option('DF_INTS_IO', 'SAVE') # psioh = core.IOManager.shared_object() # psioh.set_specific_retention(97, True) bsse_str = metadata['bsse_type_list'][0] if len(metadata['bsse_type_list']) > 1: bsse_str = str(metadata['bsse_type_list']) core.print_out("\n\n") core.print_out(" ===> N-Body Interaction Abacus <===\n") core.print_out(" BSSE Treatment: %s\n" % bsse_str) # Get compute list metadata = build_nbody_compute_list(metadata) # Compute N-Body components component_results = compute_nbody_components(func, method_string, metadata) # Assemble N-Body quantities nbody_results = assemble_nbody_components(metadata, component_results) # Build wfn and bind variables wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp') dicts = [ 'energies', 'ptype', 'intermediates', 'energy_body_dict', 'gradient_body_dict', 'hessian_body_dict', 'nbody', 'cp_energy_body_dict', 'nocp_energy_body_dict', 'vmfc_energy_body_dict' ] if metadata['ptype'] == 'gradient': wfn.set_gradient(nbody_results['ret_ptype']) nbody_results['gradient_body_dict'] = nbody_results['ptype_body_dict'] elif metadata['ptype'] == 'hessian': nbody_results['hessian_body_dict'] = nbody_results['ptype_body_dict'] wfn.set_hessian(nbody_results['ret_ptype']) component_results_gradient = component_results.copy() component_results_gradient['ptype'] = component_results_gradient['gradients'] metadata['ptype'] = 'gradient' nbody_results_gradient = assemble_nbody_components(metadata, component_results_gradient) wfn.set_gradient(nbody_results_gradient['ret_ptype']) nbody_results['gradient_body_dict'] = nbody_results_gradient['ptype_body_dict'] for r in [component_results, nbody_results]: for d in r: if d in dicts: for var, value in r[d].items(): try: wfn.set_scalar_variable(str(var), value) core.set_scalar_variable(str(var), value) except: wfn.set_array_variable(d.split('_')[0].upper() + ' ' + str(var), core.Matrix.from_array(value)) core.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy']) if metadata['return_wfn']: return (nbody_results['ret_ptype'], wfn) else: return nbody_results['ret_ptype']
def frac_nuke(name, **kwargs): """Pull all the electrons out, one at a time""" optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], # NYI ["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError("""frac_nuke requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out(""" Requested procedure `frac_nuke` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') 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 <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") Ns = [] energies = [] potentials = [] convs = [] stats = [] # Run one SCF to burn things in E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) # Determine H**O eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() eps_a.print_out() if Na == Nb: H**O = -Nb elif Nb == 0: H**O = Na else: E_a = eps_a.get(int(Na - 1)) E_b = eps_b.get(int(Nb - 1)) if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "LOAD") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # Nuke 'em Rico! for Nintegral in range(N, Nmin, -1): # Nuke the current H**O for occ in foccs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if H**O > 0: eps = wfn.epsilon_a() potentials.append(eps.np[H**O - 1]) else: eps = wfn.epsilon_b() potentials.append(eps.np[-H**O - 1]) Ns.append(Nintegral + occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "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 = core.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.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb stats.append(""" %6d %6d %6d %6d %6d %6d\n""" % (Nintegral-1, Na, Nb, charge, mult, H**O)) if H**O > 0: Na -= 1 else: Nb -= 1 charge += 1 mult = Na - Nb + 1 core.set_local_option("SCF", "DF_INTS_IO", "NONE") # => Print the results out <= # E = {} core.print_out("""\n ==> Fractional Occupation Nuke Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) E[Ns[k]] = energies[k] core.print_out('\n') core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: core.print_out(line) core.print_out('\n "You shoot a nuke down a bug hole, you got a lot of dead bugs"\n') core.print_out(' -Starship Troopers\n') # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(Ns)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (Ns[k], energies[k], potentials[k], convs[k])) with open(stats_filename, 'w') as fh: fh.write(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) for line in stats: fh.write(line) optstash.restore() return E
def frac_traverse(name, **kwargs): """Scan electron occupancy from +1 electron to -1. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. cation_mult : int, optional Multiplicity of cation, if not neutral multiplicity + 1. anion_mult : int, optional Multiplicity of anion, if not neutral multiplicity + 1. frac_start : int, optional Iteration at which to start frac procedure when not reading previous guess. Defaults to 25. HOMO_occs : list, optional Occupations to step through for cation, by default `[1 - 0.1 * x for x in range(11)]`. LUMO_occs : list, optional Occupations to step through for anion, by default `[1 - 0.1 * x for x in range(11)]`. H**O : int, optional Index of H**O. LUMO : int, optional Index of LUMO. frac_diis : bool, optional Do use DIIS for non-1.0-occupied points? neutral_guess : bool, optional Do use neutral orbitals as guess for the anion? hf_guess: bool, optional Do use UHF guess before UKS? continuous_guess : bool, optional Do carry along guess rather than reguessing at each occupation? filename : str, optional Result filename, if not name of molecule. Returns ------- dict Dictionary associating SCF energies with occupations. """ optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'REFERENCE'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], #["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError( """frac_traverse requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out( """ Requested procedure `frac_traverse` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 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 core.get_local_option('SCF', '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 <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") old_guess = core.get_local_option("SCF", "GUESS") if (neutral_guess): if (hf_guess): core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "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: core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "DF_INTS_IO", "SAVE") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # NYI core.set_local_option("SCF", "FRAC_LOAD", False) for occ in LUMO_occs: core.set_local_option("SCF", "FRAC_OCC", [LUMO]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(LUMO) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(LUMO) - 1)) occs.append(occ) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) #core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "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: core.set_local_option("SCF", "GUESS", old_guess) if hf_guess: core.set_local_option("SCF", "FRAC_START", 0) core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") # NYI core.set_local_option("SCF", "FRAC_LOAD", False) core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) for occ in HOMO_occs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(H**O) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(H**O) - 1)) occs.append(occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") # => Print the results out <= # E = {} core.print_out( """\n ==> Fractional Occupation Traverse Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] core.print_out(""" You trying to be a hero Watkins? Just trying to kill some bugs sir! -Starship Troopers""") # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) optstash.restore() return E
def frac_traverse(name, **kwargs): """Scan electron occupancy from +1 electron to -1. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. cation_mult : int, optional Multiplicity of cation, if not neutral multiplicity + 1. anion_mult : int, optional Multiplicity of anion, if not neutral multiplicity + 1. frac_start : int, optional Iteration at which to start frac procedure when not reading previous guess. Defaults to 25. HOMO_occs : list, optional Occupations to step through for cation, by default `[1 - 0.1 * x for x in range(11)]`. LUMO_occs : list, optional Occupations to step through for anion, by default `[1 - 0.1 * x for x in range(11)]`. H**O : int, optional Index of H**O. LUMO : int, optional Index of LUMO. frac_diis : bool, optional Do use DIIS for non-1.0-occupied points? neutral_guess : bool, optional Do use neutral orbitals as guess for the anion? hf_guess: bool, optional Do use UHF guess before UKS? continuous_guess : bool, optional Do carry along guess rather than reguessing at each occupation? filename : str, optional Result filename, if not name of molecule. Returns ------- dict Dictionary associating SCF energies with occupations. """ optstash = p4util.OptionsState( ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'REFERENCE'], ["SCF", "FRAC_START"], ["SCF", "FRAC_RENORMALIZE"], #["SCF", "FRAC_LOAD"], ["SCF", "FRAC_OCC"], ["SCF", "FRAC_VAL"], ["SCF", "FRAC_DIIS"]) kwargs = p4util.kwargs_lower(kwargs) # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError("""frac_traverse requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out(""" Requested procedure `frac_traverse` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') molecule.update_geometry() charge0 = molecule.molecular_charge() mult0 = molecule.multiplicity() chargep = charge0 + 1 chargem = charge0 - 1 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 core.get_local_option('SCF', '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 <= # core.set_local_option("SCF", "DF_INTS_IO", "SAVE") old_guess = core.get_local_option("SCF", "GUESS") if (neutral_guess): if (hf_guess): core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "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: core.set_local_option("SCF", "REFERENCE","UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "DF_INTS_IO", "SAVE") core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) # NYI core.set_local_option("SCF", "FRAC_LOAD", False) for occ in LUMO_occs: core.set_local_option("SCF", "FRAC_OCC", [LUMO]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(LUMO) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(LUMO) - 1)) occs.append(occ) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) #core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "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: core.set_local_option("SCF", "GUESS", old_guess) if hf_guess: core.set_local_option("SCF", "FRAC_START", 0) core.set_local_option("SCF", "REFERENCE", "UHF") driver.energy('scf', dft_functional=name, molecule=molecule, **kwargs) core.set_local_option("SCF", "REFERENCE", "UKS") core.set_local_option("SCF", "GUESS", "READ") # NYI core.set_local_option("SCF", "FRAC_LOAD", False) core.set_local_option("SCF", "FRAC_START", frac_start) core.set_local_option("SCF", "FRAC_RENORMALIZE", True) for occ in HOMO_occs: core.set_local_option("SCF", "FRAC_OCC", [H**O]) core.set_local_option("SCF", "FRAC_VAL", [occ]) E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, **kwargs) C = 1 if E == 0.0: E = core.variable('SCF ITERATION ENERGY') C = 0 if LUMO > 0: eps = wfn.epsilon_a() potentials.append(eps.get(int(H**O) - 1)) else: eps = wfn.epsilon_b() potentials.append(eps.get(-int(H**O) - 1)) occs.append(occ - 1.0) energies.append(E) convs.append(C) core.set_local_option("SCF", "FRAC_START", 2) # NYI core.set_local_option("SCF", "FRAC_LOAD", True) core.set_local_option("SCF", "GUESS", "READ") core.set_local_option("SCF", "FRAC_DIIS", frac_diis) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") # => Print the results out <= # E = {} core.print_out("""\n ==> Fractional Occupation Traverse Results <==\n\n""") core.print_out(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): core.print_out(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) E[occs[k]] = energies[k] core.print_out(""" You trying to be a hero Watkins? Just trying to kill some bugs sir! -Starship Troopers""") # Drop the files out with open(traverse_filename, 'w') as fh: fh.write(""" %-11s %-24s %-24s %11s\n""" % ('N', 'Energy', 'H**O Energy', 'Converged')) for k in range(len(occs)): fh.write(""" %11.3E %24.16E %24.16E %11d\n""" % (occs[k], energies[k], potentials[k], convs[k])) optstash.restore() return E
def run_forte(name, **kwargs): r"""Function encoding sequence of PSI module and plugin calls so that forte can be called via :py:func:`~driver.energy`. For post-scf plugins. >>> energy('forte') """ lowername = name.lower() kwargs = p4util.kwargs_lower(kwargs) # 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 = psi4.driver.scf_helper(name, **kwargs) # Get the option object psi4_options = psi4.core.get_options() psi4_options.set_current_module('FORTE') # Get the forte option object options = forte.forte_options options.get_options_from_psi4(psi4_options) if ('DF' in options.get_str('INT_TYPE')): aux_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'DF_BASIS_MP2', options.get_str('DF_BASIS_MP2'), 'RIFIT', options.get_str('BASIS')) ref_wfn.set_basisset('DF_BASIS_MP2', aux_basis) if (options.get_str('MINAO_BASIS')): minao_basis = psi4.core.BasisSet.build(ref_wfn.molecule(), 'MINAO_BASIS', options.get_str('MINAO_BASIS')) ref_wfn.set_basisset('MINAO_BASIS', minao_basis) # Start Forte, initialize ambit my_proc_n_nodes = forte.startup() my_proc, n_nodes = my_proc_n_nodes # Print the banner forte.banner() # Create the MOSpaceInfo object mo_space_info = forte.make_mo_space_info(ref_wfn, options) # Call methods that project the orbitals (AVAS, embedding) mo_space_info = orbital_projection(ref_wfn, options, mo_space_info) # Averaging spin multiplets if doing spin-adapted computation if options.get_str('CORRELATION_SOLVER') == 'SA-MRDSRG': options_dict = options.dict() options_dict['SPIN_AVG_DENSITY']['value'] = True options.set_dict(options_dict) state = forte.make_state_info_from_psi_wfn(ref_wfn) scf_info = forte.SCFInfo(ref_wfn) state_weights_map = forte.make_state_weights_map(options,ref_wfn) # Run a method job_type = options.get_str('JOB_TYPE') energy = 0.0 if job_type == 'NONE': forte.cleanup() return ref_wfn start_pre_ints = time.time() # Make an integral object ints = forte.make_forte_integrals(ref_wfn, options, mo_space_info) start = time.time() # Rotate orbitals before computation (e.g. localization, MP2 natural orbitals, etc.) orb_type = options.get_str("ORBITAL_TYPE") if orb_type != 'CANONICAL': orb_t = forte.make_orbital_transformation(orb_type, scf_info, options, ints, mo_space_info) orb_t.compute_transformation() Ua = orb_t.get_Ua() Ub = orb_t.get_Ub() ints.rotate_orbitals(Ua,Ub) # Run a method if (job_type == 'NEWDRIVER'): energy = forte_driver(state_weights_map, scf_info, options, ints, mo_space_info) else: energy = forte.forte_old_methods(ref_wfn, options, ints, mo_space_info) end = time.time() # Close ambit, etc. forte.cleanup() psi4.core.set_scalar_variable('CURRENT ENERGY', energy) psi4.core.print_out(f'\n\n Time to prepare integrals: {start - start_pre_ints:12.3f} seconds') psi4.core.print_out(f'\n Time to run job : {end - start:12.3f} seconds') psi4.core.print_out(f'\n Total : {end - start:12.3f} seconds') return ref_wfn
def ip_fitting(name, omega_l=0.05, omega_r=2.5, omega_convergence=1.0e-3, maxiter=20, **kwargs): """Optimize DFT omega parameter for molecular system. Parameters ---------- name : string or function DFT functional string name or function defining functional whose omega is to be optimized. omega_l : float, optional Minimum omega to be considered during fitting. omega_r : float, optional Maximum omega to be considered during fitting. molecule : :ref:`molecule <op_py_molecule>`, optional Target molecule (neutral) for which omega is to be tuned, if not last defined. omega_convergence : float, optional Threshold below which to consider omega converged. (formerly omega_tolerance) maxiter : int, optional Maximum number of iterations towards omega convergence. Returns ------- float Optimal omega parameter. """ optstash = p4util.OptionsState( ['SCF', 'REFERENCE'], ['SCF', 'GUESS'], ['SCF', 'DF_INTS_IO'], ['SCF', 'DFT_OMEGA'], ['DOCC'], ['SOCC']) kwargs = p4util.kwargs_lower(kwargs) # By default, do not read previous 180 orbitals file read = False read180 = '' if 'read' in kwargs: read = True read180 = kwargs['read'] if core.get_option('SCF', 'REFERENCE') != 'UKS': core.print_out(""" Requested procedure `ip_fitting` runs further calculations with UKS reference.\n""") core.set_local_option('SCF', 'REFERENCE', 'UKS') # Make sure the molecule the user provided is the active one, and neutral molecule = kwargs.pop('molecule', core.get_active_molecule()) molecule.update_geometry() if molecule.molecular_charge() != 0: raise ValidationError("""IP Fitting requires neutral molecule to start.""") if molecule.schoenflies_symbol() != 'c1': core.print_out(""" Requested procedure `ip_fitting` does not make use of molecular symmetry: """ """further calculations in C1 point group.\n""") molecule = molecule.clone() molecule.reset_point_group('c1') 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 core.IO.set_default_namespace("ot") # Burn in to determine orbital eigenvalues if read: core.set_local_option("SCF", "GUESS", "READ") copy_file_to_scratch(read180, 'psi', 'ot', 180) core.set_local_option("SCF", "DF_INTS_IO", "SAVE") E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Burn-in', **kwargs) core.set_local_option("SCF", "DF_INTS_IO", "LOAD") if not wfn.functional().is_x_lrc(): raise ValidationError("""Not sensible to optimize omega for non-long-range-correction functional.""") # 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.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] if E_a >= E_b: H**O = Na else: H**O = -Nb Na1 = Na Nb1 = Nb if H**O > 0: Na1 -= 1 else: Nb1 -= 1 charge1 = charge0 + 1 mult1 = Na1 - Nb1 + 1 omegas = [] E0s = [] E1s = [] kIPs = [] IPs = [] types = [] # Right endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_r) # Neutral if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) E0r, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Right Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOr = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation if read: core.set_local_option("SCF", "GUESS", "READ") p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180) molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) E1r = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Right Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPr = E1r - E0r kIPr = -E_HOMOr delta_r = IPr - kIPr if IPr > kIPr: raise ValidationError("""\n***IP Fitting Error: Right Omega limit should have kIP > IP: {} !> {}""".format(kIPr, IPr)) 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 core.set_local_option("SCF", "GUESS", "READ") # Left endpoint core.set_local_option('SCF', 'DFT_OMEGA', omega_l) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0l, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Left Endpoint', **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) E_HOMOl = E_HOMO core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1l = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Left Endpoint', **kwargs) core.IO.change_file_namespace(180, "ot", "cation") IPl = E1l - E0l kIPl = -E_HOMOl delta_l = IPl - kIPl if IPl < kIPl: raise ValidationError("""\n***IP Fitting Error: Left Omega limit should have kIP < IP: {} !< {}""".format(kIPl, IPl)) 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 for step in range(maxiter): # Regula Falsi (modified) if repeat_l > 1: delta_l /= 2.0 if repeat_r > 1: delta_r /= 2.0 omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l core.set_local_option('SCF', 'DFT_OMEGA', omega) # Neutral core.IO.change_file_namespace(180, "neutral", "ot") molecule.set_molecular_charge(charge0) molecule.set_multiplicity(mult0) core.set_global_option("DOCC", [Nb]) core.set_global_option("SOCC", [Na - Nb]) E0, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule, banner='IP Fitting SCF: Neutral, Omega = {:11.3E}'.format(omega), **kwargs) eps_a = wfn.epsilon_a() eps_b = wfn.epsilon_b() if Nb == 0: E_HOMO = eps_a.np[int(Na - 1)] else: E_a = eps_a.np[int(Na - 1)] E_b = eps_b.np[int(Nb - 1)] E_HOMO = max(E_a, E_b) core.IO.change_file_namespace(180, "ot", "neutral") # Cation core.IO.change_file_namespace(180, "cation", "ot") molecule.set_molecular_charge(charge1) molecule.set_multiplicity(mult1) core.set_global_option("DOCC", [Nb1]) core.set_global_option("SOCC", [Na1 - Nb1]) E1 = driver.energy('scf', dft_functional=name, molecule=molecule, banner='IP Fitting SCF: Cation, Omega = {:11.3E}'.format(omega), **kwargs) core.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 += 1 else: omega_l = omega E0l = E0 E1l = E1 IPl = IP kIPl = kIP delta_l = delta repeat_l = 0 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_convergence: converged = True break core.IO.set_default_namespace("") core.print_out("""\n ==> IP Fitting Results <==\n\n""") core.print_out(""" => Occupation Determination <= \n\n""") core.print_out(""" %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O')) core.print_out(""" Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O)) core.print_out(""" Cation: %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1)) core.print_out(""" => Regula Falsi Iterations <=\n\n""") core.print_out(""" %3s %11s %14s %14s %14s %s\n""" % ('N','Omega','IP','kIP','Delta','Type')) for k in range(len(omegas)): core.print_out(""" %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])) optstash.restore() if converged: core.print_out("""\n IP Fitting Converged\n""") core.print_out(""" Final omega = %14.6E\n""" % ((omega_l + omega_r) / 2)) core.print_out("""\n "M,I. does the dying. Fleet just does the flying."\n""") core.print_out(""" -Starship Troopers\n""") else: raise ConvergenceError("""IP Fitting """, step + 1) return ((omega_l + omega_r) / 2)