Exemple #1
0
        def run_sapt():
            if ddlow.name() == 'SCF':
                dimer_wfn = ddlow
            else:
                dimer_wfn = ddlow.reference_wavefunction()

            aux_basis = core.BasisSet.build(
                dimer_wfn.molecule(), "DF_BASIS_SAPT",
                core.get_global_option("DF_BASIS_SAPT"), "RIFIT",
                core.get_global_option("BASIS"))
            dimer_wfn.set_basisset("DF_BASIS_SAPT", aux_basis)
            dimer_wfn.set_basisset("DF_BASIS_ELST", aux_basis)
            core.sapt(dimer_wfn, m1mlow, m2mlow)
            return {
                k: core.variable(k)
                for k in (
                    'SAPT ELST10,R ENERGY',
                    'SAPT EXCH10 ENERGY',
                    'SAPT EXCH10(S^2) ENERGY',
                    'SAPT IND20,R ENERGY',
                    'SAPT EXCH-IND20,R ENERGY',
                    'SAPT EXCH-DISP20 ENERGY',
                    'SAPT DISP20 ENERGY',
                    'SAPT SAME-SPIN EXCH-DISP20 ENERGY',
                    'SAPT SAME-SPIN DISP20 ENERGY',
                    'SAPT HF TOTAL ENERGY',
                )
            }
Exemple #2
0
def compare_csx():
    """Function to validate energies in CSX files against PSIvariables. Only
    active if write_csx flag on.

    """
    warnings.warn(
        "Using `psi4.driver.p4util.compare_csx` is deprecated (silently in 1.2), and in 1.3 it will stop working\n",
        category=FutureWarning,
        stacklevel=2)

    if 'csx4psi' in sys.modules.keys():
        if core.get_global_option('WRITE_CSX'):
            enedict = csx2endict()
            compare_integers(len(enedict) >= 2, True, 'CSX harvested')
            for pv, en in enedict.items():
                compare_values(core.variable(pv), en, 6, 'CSX ' + pv + ' ' + str(round(en, 4)))
Exemple #3
0
def run_sapt_dft(name, **kwargs):
    optstash = p4util.OptionsState(['SCF_TYPE'], ['SCF', 'REFERENCE'],
                                   ['SCF', 'DFT_GRAC_SHIFT'],
                                   ['SCF', 'SAVE_JK'])

    core.tstart()
    # Alter default algorithm
    if not core.has_global_option_changed('SCF_TYPE'):
        core.set_global_option('SCF_TYPE', 'DF')

    core.prepare_options_for_module("SAPT")

    # Get the molecule of interest
    ref_wfn = kwargs.get('ref_wfn', None)
    if ref_wfn is None:
        sapt_dimer = kwargs.pop('molecule', core.get_active_molecule())
    else:
        core.print_out(
            'Warning! SAPT argument "ref_wfn" is only able to use molecule information.'
        )
        sapt_dimer = ref_wfn.molecule()

    sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule(
        sapt_dimer, "dimer")

    # Grab overall settings
    mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A")
    mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B")
    do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF")
    sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL")

    # Print out the title and some information
    core.print_out("\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("         " + "SAPT(DFT) Procedure".center(58) + "\n")
    core.print_out("\n")
    core.print_out("         " + "by Daniel G. A. Smith".center(58) + "\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("\n")

    core.print_out(
        "  !!!  WARNING:  SAPT(DFT) capability is in beta. Please use with caution. !!!\n\n"
    )

    core.print_out("  ==> Algorithm <==\n\n")
    core.print_out("   SAPT DFT Functional     %12s\n" %
                   str(sapt_dft_functional))
    core.print_out("   Monomer A GRAC Shift    %12.6f\n" % mon_a_shift)
    core.print_out("   Monomer B GRAC Shift    %12.6f\n" % mon_b_shift)
    core.print_out("   Delta HF                %12s\n" %
                   ("True" if do_delta_hf else "False"))
    core.print_out("   JK Algorithm            %12s\n" %
                   core.get_global_option("SCF_TYPE"))
    core.print_out("\n")
    core.print_out("   Required computations:\n")
    if (do_delta_hf):
        core.print_out("     HF  (Dimer)\n")
        core.print_out("     HF  (Monomer A)\n")
        core.print_out("     HF  (Monomer B)\n")
    core.print_out("     DFT (Monomer A)\n")
    core.print_out("     DFT (Monomer B)\n")
    core.print_out("\n")

    if (sapt_dft_functional != "HF") and ((mon_a_shift == 0.0) or
                                          (mon_b_shift == 0.0)):
        raise ValidationError(
            'SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".')

    if (core.get_option('SCF', 'REFERENCE') != 'RHF'):
        raise ValidationError(
            'SAPT(DFT) currently only supports restricted references.')

    core.IO.set_default_namespace('dimer')
    data = {}

    if (core.get_global_option('SCF_TYPE') == 'DF'):
        # core.set_global_option('DF_INTS_IO', 'LOAD')
        core.set_global_option('DF_INTS_IO', 'SAVE')

    # # Compute dimer wavefunction
    hf_wfn_dimer = None
    if do_delta_hf:
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.set_global_option('DF_INTS_IO', 'SAVE')

        core.timer_on("SAPT(DFT): Dimer SCF")
        hf_data = {}
        hf_wfn_dimer = scf_helper("SCF",
                                  molecule=sapt_dimer,
                                  banner="SAPT(DFT): delta HF Dimer",
                                  **kwargs)
        hf_data["HF DIMER"] = core.variable("CURRENT ENERGY")
        core.timer_off("SAPT(DFT): Dimer SCF")

        core.timer_on("SAPT(DFT): Monomer A SCF")
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'dimer', 'monomerA')

        hf_wfn_A = scf_helper("SCF",
                              molecule=monomerA,
                              banner="SAPT(DFT): delta HF Monomer A",
                              **kwargs)
        hf_data["HF MONOMER A"] = core.variable("CURRENT ENERGY")
        core.timer_off("SAPT(DFT): Monomer A SCF")

        core.timer_on("SAPT(DFT): Monomer B SCF")
        core.set_global_option("SAVE_JK", True)
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

        hf_wfn_B = scf_helper("SCF",
                              molecule=monomerB,
                              banner="SAPT(DFT): delta HF Monomer B",
                              **kwargs)
        hf_data["HF MONOMER B"] = core.variable("CURRENT ENERGY")
        core.set_global_option("SAVE_JK", False)
        core.timer_off("SAPT(DFT): Monomer B SCF")

        # Grab JK object and set to A (so we do not save many JK objects)
        sapt_jk = hf_wfn_B.jk()
        hf_wfn_A.set_jk(sapt_jk)
        core.set_global_option("SAVE_JK", False)

        # Move it back to monomer A
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerB', 'dimer')

        core.print_out("\n")
        core.print_out(
            "         ---------------------------------------------------------\n"
        )
        core.print_out("         " + "SAPT(DFT): delta HF Segment".center(58) +
                       "\n")
        core.print_out("\n")
        core.print_out("         " +
                       "by Daniel G. A. Smith and Rob Parrish".center(58) +
                       "\n")
        core.print_out(
            "         ---------------------------------------------------------\n"
        )
        core.print_out("\n")

        # Build cache
        hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B,
                                                     sapt_jk, True)

        # Electrostatics
        core.timer_on("SAPT(DFT):SAPT:elst")
        elst = sapt_jk_terms.electrostatics(hf_cache, True)
        hf_data.update(elst)
        core.timer_off("SAPT(DFT):SAPT:elst")

        # Exchange
        core.timer_on("SAPT(DFT):SAPT:exch")
        exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True)
        hf_data.update(exch)
        core.timer_off("SAPT(DFT):SAPT:exch")

        # Induction
        core.timer_on("SAPT(DFT):SAPT:ind")
        ind = sapt_jk_terms.induction(
            hf_cache,
            sapt_jk,
            True,
            maxiter=core.get_option("SAPT", "MAXITER"),
            conv=core.get_option("SAPT", "D_CONVERGENCE"),
            Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF"))
        hf_data.update(ind)
        core.timer_off("SAPT(DFT):SAPT:ind")

        dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data[
            "HF MONOMER B"]

        core.print_out("\n")
        core.print_out(
            print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value))

        data["Delta HF Correction"] = core.variable("SAPT(DFT) Delta HF")
        sapt_jk.finalize()

        del hf_wfn_A, hf_wfn_B, sapt_jk

    if hf_wfn_dimer is None:
        dimer_wfn = core.Wavefunction.build(sapt_dimer,
                                            core.get_global_option("BASIS"))
    else:
        dimer_wfn = hf_wfn_dimer

    # Set the primary functional
    core.set_local_option('SCF', 'REFERENCE', 'RKS')

    # Compute Monomer A wavefunction
    core.timer_on("SAPT(DFT): Monomer A DFT")
    if (core.get_global_option('SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'dimer', 'monomerA')

    if mon_a_shift:
        core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift)

    core.IO.set_default_namespace('monomerA')
    wfn_A = scf_helper(sapt_dft_functional,
                       post_scf=False,
                       molecule=monomerA,
                       banner="SAPT(DFT): DFT Monomer A",
                       **kwargs)
    data["DFT MONOMERA"] = core.variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)
    core.timer_off("SAPT(DFT): Monomer A DFT")

    # Compute Monomer B wavefunction
    core.timer_on("SAPT(DFT): Monomer B DFT")
    if (core.get_global_option('SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

    if mon_b_shift:
        core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift)

    core.set_global_option("SAVE_JK", True)
    core.IO.set_default_namespace('monomerB')
    wfn_B = scf_helper(sapt_dft_functional,
                       post_scf=False,
                       molecule=monomerB,
                       banner="SAPT(DFT): DFT Monomer B",
                       **kwargs)
    data["DFT MONOMERB"] = core.variable("CURRENT ENERGY")

    # Save JK object
    sapt_jk = wfn_B.jk()
    wfn_A.set_jk(sapt_jk)
    core.set_global_option("SAVE_JK", False)

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)
    core.timer_off("SAPT(DFT): Monomer B DFT")

    # Write out header
    scf_alg = core.get_global_option("SCF_TYPE")
    sapt_dft_header(sapt_dft_functional, mon_a_shift, mon_b_shift,
                    bool(do_delta_hf), scf_alg)

    # Call SAPT(DFT)
    sapt_jk = wfn_B.jk()
    sapt_dft(dimer_wfn,
             wfn_A,
             wfn_B,
             sapt_jk=sapt_jk,
             data=data,
             print_header=False)

    # Copy data back into globals
    for k, v in data.items():
        core.set_variable(k, v)

    core.tstop()

    return dimer_wfn
Exemple #4
0
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
Exemple #5
0
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
Exemple #6
0
def compute_nbody_components(func, method_string, metadata):
    """Computes requested N-body components.

    Performs requested computations for psi4::Molecule object `molecule` according to
    `compute_list` with function `func` at `method_string` level of theory.

    Parameters
    ----------
    func : {'energy', 'gradient', 'hessian'}
        Function object to be called within N-Body procedure.
    method_string : str
        Indicates level of theory to be passed to function `func`.
    metadata : dict of str
        Dictionary of N-body metadata.

        Required ``'key': value`` pairs:
        ``'compute_list'``: dict of int: set
            List of computations to perform.  Keys indicate body-levels, e.g,. `compute_list[2]` is the
            list of all 2-body computations required.
        ``'kwargs'``: dict
            Arbitrary keyword arguments to be passed to function `func`.

    Returns
    -------
    dict of str: dict
        Dictionary containing computed N-body components.

        Contents:
        ``'energies'``: dict of set: float64
               Dictionary containing all energy components required for given N-body procedure.
        ``'ptype'``: dict of set: float64 or dict of set: psi4.Matrix
               Dictionary of returned quantities from calls of function `func` during N-body computations
        ``'intermediates'``: dict of str: float64
               Dictionary of psivars for intermediate N-body computations to be set at the end of the
               N-body procedure.
    """
    # Get required metadata
    kwargs = metadata['kwargs']
    molecule = metadata['molecule']
    #molecule = core.get_active_molecule()
    compute_list = metadata['compute_dict']['all']

    # Now compute the energies
    energies_dict = {}
    gradients_dict = {}
    ptype_dict = {}
    intermediates_dict = {}
    if kwargs.get('charge_method', False) and not metadata['embedding_charges']:
        metadata['embedding_charges'] = driver_nbody_helper.compute_charges(kwargs['charge_method'],
                                            kwargs.get('charge_type', 'MULLIKEN_CHARGES').upper(), molecule)
    for count, n in enumerate(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)
            current_mol.set_name("%s_%i_%i" % (current_mol.name(), count, num))
            if metadata['embedding_charges']: driver_nbody_helper.electrostatic_embedding(metadata, pair=pair)
            # Save energies info
            ptype_dict[pair], wfn = func(method_string, molecule=current_mol, return_wfn=True, **kwargs)
            core.set_global_option_python('EXTERN', None)
            energies_dict[pair] = core.variable("CURRENT ENERGY")
            gradients_dict[pair] = wfn.gradient()
            var_key = "N-BODY (%s)@(%s) TOTAL ENERGY" % (', '.join([str(i) for i in pair[0]]), ', '.join(
                [str(i) for i in pair[1]]))
            intermediates_dict[var_key] = core.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()

    return {
        'energies': energies_dict,
        'gradients': gradients_dict,
        'ptype': ptype_dict,
        'intermediates': intermediates_dict
    }
Exemple #7
0
def compute_nbody_components(func, method_string, metadata):
    """Computes requested N-body components.

    Performs requested computations for psi4::Molecule object `molecule` according to
    `compute_list` with function `func` at `method_string` level of theory.

    Parameters
    ----------
    func : str
        {'energy', 'gradient', 'hessian'}
        Function object to be called within N-Body procedure.
    method_string : str
        Indicates level of theory to be passed to function `func`.
    metadata : dict of str
        Dictionary of N-body metadata.

        Required ``'key': value`` pairs:
        ``'compute_list'``: dict of int: set
            List of computations to perform.  Keys indicate body-levels, e.g,. `compute_list[2]` is the
            list of all 2-body computations required.
        ``'kwargs'``: dict
            Arbitrary keyword arguments to be passed to function `func`.

    Returns
    -------
    dict of str: dict
        Dictionary containing computed N-body components.

        Contents:
        ``'energies'``: dict of set: float64
               Dictionary containing all energy components required for given N-body procedure.
        ``'ptype'``: dict of set: float64 or dict of set: psi4.Matrix
               Dictionary of returned quantities from calls of function `func` during N-body computations
        ``'intermediates'``: dict of str: float64
               Dictionary of psivars for intermediate N-body computations to be set at the end of the
               N-body procedure.
    """
    # Get required metadata
    kwargs = metadata['kwargs']
    molecule = metadata['molecule']
    #molecule = core.get_active_molecule()
    compute_list = metadata['compute_dict']['all']

    # Now compute the energies
    energies_dict = {}
    gradients_dict = {}
    ptype_dict = {}
    intermediates_dict = {}
    if kwargs.get('charge_method',
                  False) and not metadata['embedding_charges']:
        metadata['embedding_charges'] = driver_nbody_helper.compute_charges(
            kwargs['charge_method'],
            kwargs.get('charge_type', 'MULLIKEN_CHARGES').upper(), molecule)
    for count, n in enumerate(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)
            current_mol.set_name("%s_%i_%i" % (current_mol.name(), count, num))
            if metadata['embedding_charges']:
                driver_nbody_helper.electrostatic_embedding(metadata,
                                                            pair=pair)
            # Save energies info
            ptype_dict[pair], wfn = func(method_string,
                                         molecule=current_mol,
                                         return_wfn=True,
                                         **kwargs)
            core.set_global_option_python('EXTERN', None)
            energies_dict[pair] = core.variable("CURRENT ENERGY")
            gradients_dict[pair] = wfn.gradient()
            var_key = "N-BODY (%s)@(%s) TOTAL ENERGY" % (', '.join(
                [str(i)
                 for i in pair[0]]), ', '.join([str(i) for i in pair[1]]))
            intermediates_dict[var_key] = core.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()

    return {
        'energies': energies_dict,
        'gradients': gradients_dict,
        'ptype': ptype_dict,
        'intermediates': intermediates_dict
    }
Exemple #8
0
def mcscf_solver(ref_wfn):

    # Build CIWavefunction
    core.prepare_options_for_module("DETCI")
    ciwfn = core.CIWavefunction(ref_wfn)

    # Hush a lot of CI output
    ciwfn.set_print(0)

    # Begin with a normal two-step
    step_type = 'Initial CI'
    total_step = core.Matrix("Total step", ciwfn.get_dimension('OA'),
                             ciwfn.get_dimension('AV'))
    start_orbs = ciwfn.get_orbitals("ROT").clone()
    ciwfn.set_orbitals("ROT", start_orbs)

    # Grab da options
    mcscf_orb_grad_conv = core.get_option("DETCI", "MCSCF_R_CONVERGENCE")
    mcscf_e_conv = core.get_option("DETCI", "MCSCF_E_CONVERGENCE")
    mcscf_max_macroiteration = core.get_option("DETCI", "MCSCF_MAXITER")
    mcscf_type = core.get_option("DETCI", "MCSCF_TYPE")
    mcscf_d_file = core.get_option("DETCI", "CI_FILE_START") + 3
    mcscf_nroots = core.get_option("DETCI", "NUM_ROOTS")
    mcscf_wavefunction_type = core.get_option("DETCI", "WFN")
    mcscf_ndet = ciwfn.ndet()
    mcscf_nuclear_energy = ciwfn.molecule().nuclear_repulsion_energy()
    mcscf_steplimit = core.get_option("DETCI", "MCSCF_MAX_ROT")
    mcscf_rotate = core.get_option("DETCI", "MCSCF_ROTATE")

    # DIIS info
    mcscf_diis_start = core.get_option("DETCI", "MCSCF_DIIS_START")
    mcscf_diis_freq = core.get_option("DETCI", "MCSCF_DIIS_FREQ")
    mcscf_diis_error_type = core.get_option("DETCI", "MCSCF_DIIS_ERROR_TYPE")
    mcscf_diis_max_vecs = core.get_option("DETCI", "MCSCF_DIIS_MAX_VECS")

    # One-step info
    mcscf_target_conv_type = core.get_option("DETCI", "MCSCF_ALGORITHM")
    mcscf_so_start_grad = core.get_option("DETCI", "MCSCF_SO_START_GRAD")
    mcscf_so_start_e = core.get_option("DETCI", "MCSCF_SO_START_E")
    mcscf_current_step_type = 'Initial CI'

    # Start with SCF energy and other params
    scf_energy = ciwfn.variable("HF TOTAL ENERGY")
    eold = scf_energy
    norb_iter = 1
    converged = False
    ah_step = False
    qc_step = False
    approx_integrals_only = True

    # Fake info to start with the initial diagonalization
    ediff = 1.e-4
    orb_grad_rms = 1.e-3

    # Grab needed objects
    diis_obj = solvers.DIIS(mcscf_diis_max_vecs)
    mcscf_obj = ciwfn.mcscf_object()

    # Execute the rotate command
    for rot in mcscf_rotate:
        if len(rot) != 4:
            raise p4util.PsiException(
                "Each element of the MCSCF rotate command requires 4 arguements (irrep, orb1, orb2, theta)."
            )

        irrep, orb1, orb2, theta = rot
        if irrep > ciwfn.Ca().nirrep():
            raise p4util.PsiException(
                "MCSCF_ROTATE: Expression %s irrep number is larger than the number of irreps"
                % (str(rot)))

        if max(orb1, orb2) > ciwfn.Ca().coldim()[irrep]:
            raise p4util.PsiException(
                "MCSCF_ROTATE: Expression %s orbital number exceeds number of orbitals in irrep"
                % (str(rot)))

        theta = np.deg2rad(theta)

        x = ciwfn.Ca().nph[irrep][:, orb1].copy()
        y = ciwfn.Ca().nph[irrep][:, orb2].copy()

        xp = np.cos(theta) * x - np.sin(theta) * y
        yp = np.sin(theta) * x + np.cos(theta) * y

        ciwfn.Ca().nph[irrep][:, orb1] = xp
        ciwfn.Ca().nph[irrep][:, orb2] = yp

    # Limited RAS functionality
    if core.get_local_option(
            "DETCI", "WFN") == "RASSCF" and mcscf_target_conv_type != "TS":
        core.print_out(
            "\n  Warning! Only the TS algorithm for RASSCF wavefunction is currently supported.\n"
        )
        core.print_out("             Switching to the TS algorithm.\n\n")
        mcscf_target_conv_type = "TS"

    # Print out headers
    if mcscf_type == "CONV":
        mtype = "   @MCSCF"
        core.print_out("\n   ==> Starting MCSCF iterations <==\n\n")
        core.print_out(
            "        Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n"
        )
    elif mcscf_type == "DF":
        mtype = "   @DF-MCSCF"
        core.print_out("\n   ==> Starting DF-MCSCF iterations <==\n\n")
        core.print_out(
            "           Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n"
        )
    else:
        mtype = "   @AO-MCSCF"
        core.print_out("\n   ==> Starting AO-MCSCF iterations <==\n\n")
        core.print_out(
            "           Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n"
        )

    # Iterate !
    for mcscf_iter in range(1, mcscf_max_macroiteration + 1):

        # Transform integrals, diagonalize H
        ciwfn.transform_mcscf_integrals(approx_integrals_only)
        nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3)

        # After the first diag we need to switch to READ
        ciwfn.set_ci_guess("DFILE")

        ciwfn.form_opdm()
        ciwfn.form_tpdm()
        ci_grad_rms = core.variable("DETCI AVG DVEC NORM")

        # Update MCSCF object
        Cocc = ciwfn.get_orbitals("DOCC")
        Cact = ciwfn.get_orbitals("ACT")
        Cvir = ciwfn.get_orbitals("VIR")
        opdm = ciwfn.get_opdm(-1, -1, "SUM", False)
        tpdm = ciwfn.get_tpdm("SUM", True)
        mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm)

        current_energy = core.variable("MCSCF TOTAL ENERGY")

        orb_grad_rms = mcscf_obj.gradient_rms()
        ediff = current_energy - eold

        # Print iterations
        print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms,
                        ci_grad_rms, nci_iter, norb_iter,
                        mcscf_current_step_type)
        eold = current_energy

        if mcscf_current_step_type == 'Initial CI':
            mcscf_current_step_type = 'TS'

        # Check convergence
        if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)) and\
            (mcscf_iter > 3) and not qc_step:

            core.print_out("\n       %s has converged!\n\n" % mtype)
            converged = True
            break

        # Which orbital convergence are we doing?
        if ah_step:
            converged, norb_iter, step = ah_iteration(mcscf_obj,
                                                      print_micro=False)
            norb_iter += 1

            if converged:
                mcscf_current_step_type = 'AH'
            else:
                core.print_out(
                    "      !Warning. Augmented Hessian did not converge. Taking an approx step.\n"
                )
                step = mcscf_obj.approx_solve()
                mcscf_current_step_type = 'TS, AH failure'

        else:
            step = mcscf_obj.approx_solve()
            step_type = 'TS'

        maxstep = step.absmax()
        if maxstep > mcscf_steplimit:
            core.print_out(
                '      Warning! Maxstep = %4.2f, scaling to %4.2f\n' %
                (maxstep, mcscf_steplimit))
            step.scale(mcscf_steplimit / maxstep)

        xstep = total_step.clone()
        total_step.add(step)

        # Do or add DIIS
        if (mcscf_iter >= mcscf_diis_start) and ("TS"
                                                 in mcscf_current_step_type):

            # Figure out DIIS error vector
            if mcscf_diis_error_type == "GRAD":
                error = core.Matrix.triplet(ciwfn.get_orbitals("OA"),
                                            mcscf_obj.gradient(),
                                            ciwfn.get_orbitals("AV"), False,
                                            False, True)
            else:
                error = step

            diis_obj.add(total_step, error)

            if not (mcscf_iter % mcscf_diis_freq):
                total_step = diis_obj.extrapolate()
                mcscf_current_step_type = 'TS, DIIS'

        # Build the rotation by continuous updates
        if mcscf_iter == 1:
            totalU = mcscf_obj.form_rotation_matrix(total_step)
        else:
            xstep.axpy(-1.0, total_step)
            xstep.scale(-1.0)
            Ustep = mcscf_obj.form_rotation_matrix(xstep)
            totalU = core.Matrix.doublet(totalU, Ustep, False, False)

        # Build the rotation directly (not recommended)
        # orbs_mat = mcscf_obj.Ck(start_orbs, total_step)

        # Finally rotate and set orbitals
        orbs_mat = core.Matrix.doublet(start_orbs, totalU, False, False)
        ciwfn.set_orbitals("ROT", orbs_mat)

        # Figure out what the next step should be
        if (orb_grad_rms < mcscf_so_start_grad) and (abs(ediff) < abs(mcscf_so_start_e)) and\
                (mcscf_iter >= 2):

            if mcscf_target_conv_type == 'AH':
                approx_integrals_only = False
                ah_step = True
            elif mcscf_target_conv_type == 'OS':
                approx_integrals_only = False
                mcscf_current_step_type = 'OS, Prep'
                break
            else:
                continue
        #raise p4util.PsiException("")

    # If we converged do not do onestep
    if converged or (mcscf_target_conv_type != 'OS'):
        one_step_iters = []

    # If we are not converged load in Dvec and build iters array
    else:
        one_step_iters = range(mcscf_iter + 1, mcscf_max_macroiteration + 1)
        dvec = ciwfn.D_vector()
        dvec.init_io_files(True)
        dvec.read(0, 0)
        dvec.symnormalize(1.0, 0)

        ci_grad = ciwfn.new_civector(1, mcscf_d_file + 1, True, True)
        ci_grad.set_nvec(1)
        ci_grad.init_io_files(True)

    # Loop for onestep
    for mcscf_iter in one_step_iters:

        # Transform integrals and update the MCSCF object
        ciwfn.transform_mcscf_integrals(ciwfn.H(), False)
        ciwfn.form_opdm()
        ciwfn.form_tpdm()

        # Update MCSCF object
        Cocc = ciwfn.get_orbitals("DOCC")
        Cact = ciwfn.get_orbitals("ACT")
        Cvir = ciwfn.get_orbitals("VIR")
        opdm = ciwfn.get_opdm(-1, -1, "SUM", False)
        tpdm = ciwfn.get_tpdm("SUM", True)
        mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm)

        orb_grad_rms = mcscf_obj.gradient_rms()

        # Warning! Does not work for SA-MCSCF
        current_energy = mcscf_obj.current_total_energy()
        current_energy += mcscf_nuclear_energy

        core.set_variable("CI ROOT %d TOTAL ENERGY" % 1, current_energy)
        core.set_variable("CURRENT ENERGY", current_energy)

        docc_energy = mcscf_obj.current_docc_energy()
        ci_energy = mcscf_obj.current_ci_energy()

        # Compute CI gradient
        ciwfn.sigma(dvec, ci_grad, 0, 0)
        ci_grad.scale(2.0, 0)
        ci_grad.axpy(-2.0 * ci_energy, dvec, 0, 0)

        ci_grad_rms = ci_grad.norm(0)
        orb_grad_rms = mcscf_obj.gradient().rms()

        ediff = current_energy - eold

        print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms,
                        ci_grad_rms, nci_iter, norb_iter,
                        mcscf_current_step_type)
        mcscf_current_step_type = 'OS'

        eold = current_energy

        if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) <
                                                     abs(mcscf_e_conv)):

            core.print_out("\n       %s has converged!\n\n" % mtype)
            converged = True
            break

        # Take a step
        converged, norb_iter, nci_iter, step = qc_iteration(
            dvec, ci_grad, ciwfn, mcscf_obj)

        # Rotate integrals to new frame
        total_step.add(step)
        orbs_mat = mcscf_obj.Ck(ciwfn.get_orbitals("ROT"), step)
        ciwfn.set_orbitals("ROT", orbs_mat)

    core.print_out(mtype + " Final Energy: %20.15f\n" % current_energy)

    # Die if we did not converge
    if (not converged):
        if core.get_global_option("DIE_IF_NOT_CONVERGED"):
            raise p4util.PsiException("MCSCF: Iterations did not converge!")
        else:
            core.print_out("\nWarning! MCSCF iterations did not converge!\n\n")

    # Print out CI vector information
    if mcscf_target_conv_type == 'OS':
        dvec.close_io_files()
        ci_grad.close_io_files()

    # For orbital invariant methods we transform the orbitals to the natural or
    # semicanonical basis. Frozen doubly occupied and virtual orbitals are not
    # modified.
    if core.get_option("DETCI", "WFN") == "CASSCF":
        # Do we diagonalize the opdm?
        if core.get_option("DETCI", "NAT_ORBS"):
            ciwfn.ci_nat_orbs()
        else:
            ciwfn.semicanonical_orbs()

        # Retransform intragrals and update CI coeffs., OPDM, and TPDM
        ciwfn.transform_mcscf_integrals(approx_integrals_only)
        nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3)

        ciwfn.set_ci_guess("DFILE")

        ciwfn.form_opdm()
        ciwfn.form_tpdm()

    proc_util.print_ci_results(ciwfn,
                               "MCSCF",
                               scf_energy,
                               current_energy,
                               print_opdm_no=True)

    # Set final energy
    core.set_variable("CURRENT ENERGY", core.variable("MCSCF TOTAL ENERGY"))

    # What do we need to cleanup?
    if core.get_option("DETCI", "MCSCF_CI_CLEANUP"):
        ciwfn.cleanup_ci()
    if core.get_option("DETCI", "MCSCF_DPD_CLEANUP"):
        ciwfn.cleanup_dpd()

    del diis_obj
    del mcscf_obj
    return ciwfn
Exemple #9
0
def mcscf_solver(ref_wfn):

    # Build CIWavefunction
    core.prepare_options_for_module("DETCI")
    ciwfn = core.CIWavefunction(ref_wfn)

    # Hush a lot of CI output
    ciwfn.set_print(0)

    # Begin with a normal two-step
    step_type = 'Initial CI'
    total_step = core.Matrix("Total step", ciwfn.get_dimension('OA'), ciwfn.get_dimension('AV'))
    start_orbs = ciwfn.get_orbitals("ROT").clone()
    ciwfn.set_orbitals("ROT", start_orbs)

    # Grab da options
    mcscf_orb_grad_conv = core.get_option("DETCI", "MCSCF_R_CONVERGENCE")
    mcscf_e_conv = core.get_option("DETCI", "MCSCF_E_CONVERGENCE")
    mcscf_max_macroiteration = core.get_option("DETCI", "MCSCF_MAXITER")
    mcscf_type = core.get_option("DETCI", "MCSCF_TYPE")
    mcscf_d_file = core.get_option("DETCI", "CI_FILE_START") + 3
    mcscf_nroots = core.get_option("DETCI", "NUM_ROOTS")
    mcscf_wavefunction_type = core.get_option("DETCI", "WFN")
    mcscf_ndet = ciwfn.ndet()
    mcscf_nuclear_energy = ciwfn.molecule().nuclear_repulsion_energy()
    mcscf_steplimit = core.get_option("DETCI", "MCSCF_MAX_ROT")
    mcscf_rotate = core.get_option("DETCI", "MCSCF_ROTATE")

    # DIIS info
    mcscf_diis_start = core.get_option("DETCI", "MCSCF_DIIS_START")
    mcscf_diis_freq = core.get_option("DETCI", "MCSCF_DIIS_FREQ")
    mcscf_diis_error_type = core.get_option("DETCI", "MCSCF_DIIS_ERROR_TYPE")
    mcscf_diis_max_vecs = core.get_option("DETCI", "MCSCF_DIIS_MAX_VECS")

    # One-step info
    mcscf_target_conv_type = core.get_option("DETCI", "MCSCF_ALGORITHM")
    mcscf_so_start_grad = core.get_option("DETCI", "MCSCF_SO_START_GRAD")
    mcscf_so_start_e = core.get_option("DETCI", "MCSCF_SO_START_E")
    mcscf_current_step_type = 'Initial CI'

    # Start with SCF energy and other params
    scf_energy = ciwfn.variable("HF TOTAL ENERGY")
    eold = scf_energy
    norb_iter = 1
    converged = False
    ah_step = False
    qc_step = False
    approx_integrals_only = True

    # Fake info to start with the initial diagonalization
    ediff = 1.e-4
    orb_grad_rms = 1.e-3

    # Grab needed objects
    diis_obj = solvers.DIIS(mcscf_diis_max_vecs)
    mcscf_obj = ciwfn.mcscf_object()

    # Execute the rotate command
    for rot in mcscf_rotate:
        if len(rot) != 4:
            raise p4util.PsiException("Each element of the MCSCF rotate command requires 4 arguements (irrep, orb1, orb2, theta).")

        irrep, orb1, orb2, theta = rot
        if irrep > ciwfn.Ca().nirrep():
            raise p4util.PsiException("MCSCF_ROTATE: Expression %s irrep number is larger than the number of irreps" %
                                    (str(rot)))

        if max(orb1, orb2) > ciwfn.Ca().coldim()[irrep]:
            raise p4util.PsiException("MCSCF_ROTATE: Expression %s orbital number exceeds number of orbitals in irrep" %
                                    (str(rot)))

        theta = np.deg2rad(theta)

        x = ciwfn.Ca().nph[irrep][:, orb1].copy()
        y = ciwfn.Ca().nph[irrep][:, orb2].copy()

        xp = np.cos(theta) * x - np.sin(theta) * y
        yp = np.sin(theta) * x + np.cos(theta) * y

        ciwfn.Ca().nph[irrep][:, orb1] = xp
        ciwfn.Ca().nph[irrep][:, orb2] = yp


    # Limited RAS functionality
    if core.get_local_option("DETCI", "WFN") == "RASSCF" and mcscf_target_conv_type != "TS":
        core.print_out("\n  Warning! Only the TS algorithm for RASSCF wavefunction is currently supported.\n")
        core.print_out("             Switching to the TS algorithm.\n\n")
        mcscf_target_conv_type = "TS"

    # Print out headers
    if mcscf_type == "CONV":
        mtype = "   @MCSCF"
        core.print_out("\n   ==> Starting MCSCF iterations <==\n\n")
        core.print_out("        Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n")
    elif mcscf_type == "DF":
        mtype = "   @DF-MCSCF"
        core.print_out("\n   ==> Starting DF-MCSCF iterations <==\n\n")
        core.print_out("           Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n")
    else:
        mtype = "   @AO-MCSCF"
        core.print_out("\n   ==> Starting AO-MCSCF iterations <==\n\n")
        core.print_out("           Iter         Total Energy       Delta E   Orb RMS    CI RMS  NCI NORB\n")

    # Iterate !
    for mcscf_iter in range(1, mcscf_max_macroiteration + 1):

        # Transform integrals, diagonalize H
        ciwfn.transform_mcscf_integrals(approx_integrals_only)
        nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3)

        # After the first diag we need to switch to READ
        ciwfn.set_ci_guess("DFILE")

        ciwfn.form_opdm()
        ciwfn.form_tpdm()
        ci_grad_rms = core.variable("DETCI AVG DVEC NORM")

        # Update MCSCF object
        Cocc = ciwfn.get_orbitals("DOCC")
        Cact = ciwfn.get_orbitals("ACT")
        Cvir = ciwfn.get_orbitals("VIR")
        opdm = ciwfn.get_opdm(-1, -1, "SUM", False)
        tpdm = ciwfn.get_tpdm("SUM", True)
        mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm)

        current_energy = core.variable("MCSCF TOTAL ENERGY")

        orb_grad_rms = mcscf_obj.gradient_rms()
        ediff = current_energy - eold

        # Print iterations
        print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms,
                        nci_iter, norb_iter, mcscf_current_step_type)
        eold = current_energy

        if mcscf_current_step_type == 'Initial CI':
            mcscf_current_step_type = 'TS'

        # Check convergence
        if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)) and\
            (mcscf_iter > 3) and not qc_step:

            core.print_out("\n       %s has converged!\n\n" % mtype);
            converged = True
            break


        # Which orbital convergence are we doing?
        if ah_step:
            converged, norb_iter, step = ah_iteration(mcscf_obj, print_micro=False)
            norb_iter += 1

            if converged:
                mcscf_current_step_type = 'AH'
            else:
                core.print_out("      !Warning. Augmented Hessian did not converge. Taking an approx step.\n")
                step = mcscf_obj.approx_solve()
                mcscf_current_step_type = 'TS, AH failure'

        else:
            step = mcscf_obj.approx_solve()
            step_type = 'TS'

        maxstep = step.absmax()
        if maxstep > mcscf_steplimit:
            core.print_out('      Warning! Maxstep = %4.2f, scaling to %4.2f\n' % (maxstep, mcscf_steplimit))
            step.scale(mcscf_steplimit / maxstep)

        xstep = total_step.clone()
        total_step.add(step)

        # Do or add DIIS
        if (mcscf_iter >= mcscf_diis_start) and ("TS" in mcscf_current_step_type):

            # Figure out DIIS error vector
            if mcscf_diis_error_type == "GRAD":
                error = core.triplet(ciwfn.get_orbitals("OA"),
                                            mcscf_obj.gradient(),
                                            ciwfn.get_orbitals("AV"),
                                            False, False, True)
            else:
                error = step

            diis_obj.add(total_step, error)

            if not (mcscf_iter % mcscf_diis_freq):
                total_step = diis_obj.extrapolate()
                mcscf_current_step_type = 'TS, DIIS'

        # Build the rotation by continuous updates
        if mcscf_iter == 1:
            totalU = mcscf_obj.form_rotation_matrix(total_step)
        else:
            xstep.axpy(-1.0, total_step)
            xstep.scale(-1.0)
            Ustep = mcscf_obj.form_rotation_matrix(xstep)
            totalU = core.doublet(totalU, Ustep, False, False)

        # Build the rotation directly (not recommended)
        # orbs_mat = mcscf_obj.Ck(start_orbs, total_step)

        # Finally rotate and set orbitals
        orbs_mat = core.doublet(start_orbs, totalU, False, False)
        ciwfn.set_orbitals("ROT", orbs_mat)

        # Figure out what the next step should be
        if (orb_grad_rms < mcscf_so_start_grad) and (abs(ediff) < abs(mcscf_so_start_e)) and\
                (mcscf_iter >= 2):

            if mcscf_target_conv_type == 'AH':
                approx_integrals_only = False
                ah_step = True
            elif mcscf_target_conv_type == 'OS':
                approx_integrals_only = False
                mcscf_current_step_type = 'OS, Prep'
                break
            else:
                continue
        #raise p4util.PsiException("")

    # If we converged do not do onestep
    if converged or (mcscf_target_conv_type != 'OS'):
        one_step_iters = []

    # If we are not converged load in Dvec and build iters array
    else:
        one_step_iters = range(mcscf_iter + 1, mcscf_max_macroiteration + 1)
        dvec = ciwfn.D_vector()
        dvec.init_io_files(True)
        dvec.read(0, 0)
        dvec.symnormalize(1.0, 0)

        ci_grad = ciwfn.new_civector(1, mcscf_d_file + 1, True, True)
        ci_grad.set_nvec(1)
        ci_grad.init_io_files(True)

    # Loop for onestep
    for mcscf_iter in one_step_iters:

        # Transform integrals and update the MCSCF object
        ciwfn.transform_mcscf_integrals(ciwfn.H(), False)
        ciwfn.form_opdm()
        ciwfn.form_tpdm()

        # Update MCSCF object
        Cocc = ciwfn.get_orbitals("DOCC")
        Cact = ciwfn.get_orbitals("ACT")
        Cvir = ciwfn.get_orbitals("VIR")
        opdm = ciwfn.get_opdm(-1, -1, "SUM", False)
        tpdm = ciwfn.get_tpdm("SUM", True)
        mcscf_obj.update(Cocc, Cact, Cvir, opdm, tpdm)

        orb_grad_rms = mcscf_obj.gradient_rms()

        # Warning! Does not work for SA-MCSCF
        current_energy = mcscf_obj.current_total_energy()
        current_energy += mcscf_nuclear_energy

        core.set_variable("CI ROOT %d TOTAL ENERGY" % 1, current_energy)
        core.set_variable("CURRENT ENERGY", current_energy)

        docc_energy = mcscf_obj.current_docc_energy()
        ci_energy = mcscf_obj.current_ci_energy()

        # Compute CI gradient
        ciwfn.sigma(dvec, ci_grad, 0, 0)
        ci_grad.scale(2.0, 0)
        ci_grad.axpy(-2.0 * ci_energy, dvec, 0, 0)

        ci_grad_rms = ci_grad.norm(0)
        orb_grad_rms = mcscf_obj.gradient().rms()

        ediff = current_energy - eold

        print_iteration(mtype, mcscf_iter, current_energy, ediff, orb_grad_rms, ci_grad_rms,
                        nci_iter, norb_iter, mcscf_current_step_type)
        mcscf_current_step_type = 'OS'

        eold = current_energy

        if (orb_grad_rms < mcscf_orb_grad_conv) and (abs(ediff) < abs(mcscf_e_conv)):

            core.print_out("\n       %s has converged!\n\n" % mtype);
            converged = True
            break

        # Take a step
        converged, norb_iter, nci_iter, step = qc_iteration(dvec, ci_grad, ciwfn, mcscf_obj)

        # Rotate integrals to new frame
        total_step.add(step)
        orbs_mat = mcscf_obj.Ck(ciwfn.get_orbitals("ROT"), step)
        ciwfn.set_orbitals("ROT", orbs_mat)


    core.print_out(mtype + " Final Energy: %20.15f\n" % current_energy)

    # Die if we did not converge
    if (not converged):
        if core.get_global_option("DIE_IF_NOT_CONVERGED"):
            raise p4util.PsiException("MCSCF: Iterations did not converge!")
        else:
            core.print_out("\nWarning! MCSCF iterations did not converge!\n\n")

    # Print out CI vector information
    if mcscf_target_conv_type == 'OS':
        dvec.close_io_files()
        ci_grad.close_io_files()

    # For orbital invariant methods we transform the orbitals to the natural or
    # semicanonical basis. Frozen doubly occupied and virtual orbitals are not
    # modified.
    if core.get_option("DETCI", "WFN") == "CASSCF":
        # Do we diagonalize the opdm?
        if core.get_option("DETCI", "NAT_ORBS"):
            ciwfn.ci_nat_orbs()
        else:
            ciwfn.semicanonical_orbs()

        # Retransform intragrals and update CI coeffs., OPDM, and TPDM
        ciwfn.transform_mcscf_integrals(approx_integrals_only)
        nci_iter = ciwfn.diag_h(abs(ediff) * 1.e-2, orb_grad_rms * 1.e-3)

        ciwfn.set_ci_guess("DFILE")

        ciwfn.form_opdm()
        ciwfn.form_tpdm()

    proc_util.print_ci_results(ciwfn, "MCSCF", scf_energy, current_energy, print_opdm_no=True)

    # Set final energy
    core.set_variable("CURRENT ENERGY", core.variable("MCSCF TOTAL ENERGY"))

    # What do we need to cleanup?
    if core.get_option("DETCI", "MCSCF_CI_CLEANUP"):
        ciwfn.cleanup_ci()
    if core.get_option("DETCI", "MCSCF_DPD_CLEANUP"):
        ciwfn.cleanup_dpd()

    del diis_obj
    del mcscf_obj
    return ciwfn
Exemple #10
0
def run_gaussian_2(name, **kwargs):

    # throw an exception for open-shells
    if (core.get_option('SCF', 'REFERENCE') != 'RHF'):
        raise ValidationError("""g2 computations require "reference rhf".""")

    # stash user options:
    optstash = p4util.OptionsState(['FNOCC', 'COMPUTE_TRIPLES'],
                                   ['FNOCC', 'COMPUTE_MP4_TRIPLES'], ['BASIS'],
                                   ['FREEZE_CORE'], ['MP2_TYPE'], ['SCF_TYPE'])

    # override default scf_type
    core.set_global_option('SCF_TYPE', 'PK')

    # optimize geometry at scf level
    core.clean()
    core.set_global_option('BASIS', "6-31G(D)")
    driver.optimize('scf')
    core.clean()

    # scf frequencies for zpe
    # NOTE This line should not be needed, but without it there's a seg fault
    scf_e, ref = driver.frequency('scf', return_wfn=True)

    # thermodynamic properties
    du = core.variable('THERMAL ENERGY CORRECTION')
    dh = core.variable('ENTHALPY CORRECTION')
    dg = core.variable('GIBBS FREE ENERGY CORRECTION')

    freqs = ref.frequencies()
    nfreq = freqs.dim(0)
    freqsum = 0.0
    for i in range(0, nfreq):
        freqsum += freqs.get(i)
    zpe = freqsum / constants.hartree2wavenumbers * 0.8929 * 0.5
    core.clean()

    # optimize geometry at mp2 (no frozen core) level
    # note: freeze_core isn't an option in MP2
    core.set_global_option('FREEZE_CORE', "FALSE")
    core.set_global_option('MP2_TYPE', 'CONV')
    driver.optimize('mp2')
    core.clean()

    # qcisd(t)
    core.set_local_option('FNOCC', 'COMPUTE_MP4_TRIPLES', "TRUE")
    core.set_global_option('FREEZE_CORE', "TRUE")
    core.set_global_option('BASIS', "6-311G(D_P)")
    ref = driver.proc.run_fnocc('qcisd(t)', return_wfn=True, **kwargs)

    # HLC: high-level correction based on number of valence electrons
    nirrep = ref.nirrep()
    frzcpi = ref.frzcpi()
    nfzc = 0
    for i in range(0, nirrep):
        nfzc += frzcpi[i]
    nalpha = ref.nalpha() - nfzc
    nbeta = ref.nbeta() - nfzc
    # hlc of gaussian-2
    hlc = -0.00481 * nalpha - 0.00019 * nbeta
    # hlc of gaussian-1
    hlc1 = -0.00614 * nalpha

    eqci_6311gdp = core.variable("QCISD(T) TOTAL ENERGY")
    emp4_6311gd = core.variable("MP4 TOTAL ENERGY")
    emp2_6311gd = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # correction for diffuse functions
    core.set_global_option('BASIS', "6-311+G(D_P)")
    driver.energy('mp4')
    emp4_6311pg_dp = core.variable("MP4 TOTAL ENERGY")
    emp2_6311pg_dp = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # correction for polarization functions
    core.set_global_option('BASIS', "6-311G(2DF_P)")
    driver.energy('mp4')
    emp4_6311g2dfp = core.variable("MP4 TOTAL ENERGY")
    emp2_6311g2dfp = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # big basis mp2
    core.set_global_option('BASIS', "6-311+G(3DF_2P)")
    #run_fnocc('_mp2',**kwargs)
    driver.energy('mp2')
    emp2_big = core.variable("MP2 TOTAL ENERGY")
    core.clean()
    eqci = eqci_6311gdp
    e_delta_g2 = emp2_big + emp2_6311gd - emp2_6311g2dfp - emp2_6311pg_dp
    e_plus = emp4_6311pg_dp - emp4_6311gd
    e_2df = emp4_6311g2dfp - emp4_6311gd

    eg2 = eqci + e_delta_g2 + e_plus + e_2df
    eg2_mp2_0k = eqci + (emp2_big - emp2_6311gd) + hlc + zpe

    core.print_out('\n')
    core.print_out('  ==>  G1/G2 Energy Components  <==\n')
    core.print_out('\n')
    core.print_out('        QCISD(T):            %20.12lf\n' % eqci)
    core.print_out('        E(Delta):            %20.12lf\n' % e_delta_g2)
    core.print_out('        E(2DF):              %20.12lf\n' % e_2df)
    core.print_out('        E(+):                %20.12lf\n' % e_plus)
    core.print_out('        E(G1 HLC):           %20.12lf\n' % hlc1)
    core.print_out('        E(G2 HLC):           %20.12lf\n' % hlc)
    core.print_out('        E(ZPE):              %20.12lf\n' % zpe)
    core.print_out('\n')
    core.print_out('  ==>  0 Kelvin Results  <==\n')
    core.print_out('\n')
    eg2_0k = eg2 + zpe + hlc
    core.print_out('        G1:                  %20.12lf\n' %
                   (eqci + e_plus + e_2df + hlc1 + zpe))
    core.print_out('        G2(MP2):             %20.12lf\n' % eg2_mp2_0k)
    core.print_out('        G2:                  %20.12lf\n' % eg2_0k)

    core.set_variable("G1 TOTAL ENERGY", eqci + e_plus + e_2df + hlc1 + zpe)
    core.set_variable("G2 TOTAL ENERGY", eg2_0k)
    core.set_variable("G2(MP2) TOTAL ENERGY", eg2_mp2_0k)

    core.print_out('\n')
    T = core.get_global_option('T')
    core.print_out('  ==>  %3.0lf Kelvin Results  <==\n' % T)
    core.print_out('\n')

    internal_energy = eg2_mp2_0k + du - zpe / 0.8929
    enthalpy = eg2_mp2_0k + dh - zpe / 0.8929
    gibbs = eg2_mp2_0k + dg - zpe / 0.8929

    core.print_out('        G2(MP2) energy:      %20.12lf\n' % internal_energy)
    core.print_out('        G2(MP2) enthalpy:    %20.12lf\n' % enthalpy)
    core.print_out('        G2(MP2) free energy: %20.12lf\n' % gibbs)
    core.print_out('\n')

    core.set_variable("G2(MP2) INTERNAL ENERGY", internal_energy)
    core.set_variable("G2(MP2) ENTHALPY", enthalpy)
    core.set_variable("G2(MP2) FREE ENERGY", gibbs)

    internal_energy = eg2_0k + du - zpe / 0.8929
    enthalpy = eg2_0k + dh - zpe / 0.8929
    gibbs = eg2_0k + dg - zpe / 0.8929

    core.print_out('        G2 energy:           %20.12lf\n' % internal_energy)
    core.print_out('        G2 enthalpy:         %20.12lf\n' % enthalpy)
    core.print_out('        G2 free energy:      %20.12lf\n' % gibbs)

    core.set_variable("CURRENT ENERGY", eg2_0k)

    core.set_variable("G2 INTERNAL ENERGY", internal_energy)
    core.set_variable("G2 ENTHALPY", enthalpy)
    core.set_variable("G2 FREE ENERGY", gibbs)

    core.clean()

    optstash.restore()

    # return 0K g2 results
    return eg2_0k
Exemple #11
0
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
Exemple #12
0
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
Exemple #13
0
def print_ci_results(ciwfn, rname, scf_e, ci_e, print_opdm_no=False):
    """
    Printing for all CI Wavefunctions
    """

    # Print out energetics
    core.print_out("\n   ==> Energetics <==\n\n")
    core.print_out("    SCF energy =         %20.15f\n" % scf_e)
    if "CI" in rname:
        core.print_out("    Total CI energy =    %20.15f\n" % ci_e)
    elif "MP" in rname:
        core.print_out("    Total MP energy =    %20.15f\n" % ci_e)
    elif "ZAPT" in rname:
        core.print_out("    Total ZAPT energy =  %20.15f\n" % ci_e)
    else:
        core.print_out("    Total MCSCF energy = %20.15f\n" % ci_e)

    # Nothing to be done for ZAPT or MP
    if ("MP" in rname) or ("ZAPT" in rname):
        core.print_out("\n")
        return

    # Initial info
    ci_nroots = core.get_option("DETCI", "NUM_ROOTS")
    irrep_labels = ciwfn.molecule().irrep_labels()

    # Grab the D-vector
    dvec = ciwfn.D_vector()
    dvec.init_io_files(True)

    for root in range(ci_nroots):
        core.print_out("\n   ==> %s root %d information <==\n\n" %
                       (rname, root))

        # Print total energy
        root_e = core.variable("CI ROOT %d TOTAL ENERGY" % (root))
        core.print_out("    %s Root %d energy =  %20.15f\n" %
                       (rname, root, root_e))

        # Print natural occupations
        if print_opdm_no:
            core.print_out("\n   Active Space Natural occupation numbers:\n\n")

            occs_list = []
            r_opdm = ciwfn.get_opdm(root, root, "SUM", False)
            for h in range(len(r_opdm.nph)):
                if 0 in r_opdm.nph[h].shape:
                    continue
                nocc, rot = np.linalg.eigh(r_opdm.nph[h])
                for e in nocc:
                    occs_list.append((e, irrep_labels[h]))

            occs_list.sort(key=lambda x: -x[0])

            cnt = 0
            for value, label in occs_list:
                value, label = occs_list[cnt]
                core.print_out("      %4s  % 8.6f" % (label, value))
                cnt += 1
                if (cnt % 3) == 0:
                    core.print_out("\n")

            if (cnt % 3):
                core.print_out("\n")

        # Print CIVector information
        ciwfn.print_vector(dvec, root)

    # True to keep the file
    dvec.close_io_files(True)
Exemple #14
0
def run_gaussian_2(name, **kwargs):

    # throw an exception for open-shells
    if (core.get_option('SCF','REFERENCE') != 'RHF' ):
        raise ValidationError("""g2 computations require "reference rhf".""")

    # stash user options:
    optstash = p4util.OptionsState(
        ['FNOCC','COMPUTE_TRIPLES'],
        ['FNOCC','COMPUTE_MP4_TRIPLES'],
        ['FREEZE_CORE'],
        ['MP2_TYPE'],
        ['SCF_TYPE'])

    # override default scf_type
    core.set_global_option('SCF_TYPE','PK')

    # optimize geometry at scf level
    core.clean()
    core.set_global_option('BASIS',"6-31G(D)")
    driver.optimize('scf')
    core.clean()

    # scf frequencies for zpe
    # NOTE This line should not be needed, but without it there's a seg fault
    scf_e, ref = driver.frequency('scf', return_wfn=True)

    # thermodynamic properties
    du = core.variable('THERMAL ENERGY CORRECTION')
    dh = core.variable('ENTHALPY CORRECTION')
    dg = core.variable('GIBBS FREE ENERGY CORRECTION')

    freqs   = ref.frequencies()
    nfreq   = freqs.dim(0)
    freqsum = 0.0
    for i in range(0, nfreq):
        freqsum += freqs.get(i)
    zpe = freqsum / constants.hartree2wavenumbers * 0.8929 * 0.5
    core.clean()

    # optimize geometry at mp2 (no frozen core) level
    # note: freeze_core isn't an option in MP2
    core.set_global_option('FREEZE_CORE',"FALSE")
    core.set_global_option('MP2_TYPE', 'CONV')
    driver.optimize('mp2')
    core.clean()

    # qcisd(t)
    core.set_local_option('FNOCC','COMPUTE_MP4_TRIPLES',"TRUE")
    core.set_global_option('FREEZE_CORE',"TRUE")
    core.set_global_option('BASIS',"6-311G(D_P)")
    ref = driver.proc.run_fnocc('qcisd(t)', return_wfn=True, **kwargs)

    # HLC: high-level correction based on number of valence electrons
    nirrep = ref.nirrep()
    frzcpi = ref.frzcpi()
    nfzc = 0
    for i in range (0,nirrep):
        nfzc += frzcpi[i]
    nalpha = ref.nalpha() - nfzc
    nbeta  = ref.nbeta() - nfzc
    # hlc of gaussian-2
    hlc = -0.00481 * nalpha -0.00019 * nbeta
    # hlc of gaussian-1
    hlc1 = -0.00614 * nalpha

    eqci_6311gdp = core.variable("QCISD(T) TOTAL ENERGY")
    emp4_6311gd  = core.variable("MP4 TOTAL ENERGY")
    emp2_6311gd  = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # correction for diffuse functions
    core.set_global_option('BASIS',"6-311+G(D_P)")
    driver.energy('mp4')
    emp4_6311pg_dp = core.variable("MP4 TOTAL ENERGY")
    emp2_6311pg_dp = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # correction for polarization functions
    core.set_global_option('BASIS',"6-311G(2DF_P)")
    driver.energy('mp4')
    emp4_6311g2dfp = core.variable("MP4 TOTAL ENERGY")
    emp2_6311g2dfp = core.variable("MP2 TOTAL ENERGY")
    core.clean()

    # big basis mp2
    core.set_global_option('BASIS',"6-311+G(3DF_2P)")
    #run_fnocc('_mp2',**kwargs)
    driver.energy('mp2')
    emp2_big = core.variable("MP2 TOTAL ENERGY")
    core.clean()
    eqci       = eqci_6311gdp
    e_delta_g2 = emp2_big + emp2_6311gd - emp2_6311g2dfp - emp2_6311pg_dp
    e_plus     = emp4_6311pg_dp - emp4_6311gd
    e_2df      = emp4_6311g2dfp - emp4_6311gd

    eg2 = eqci + e_delta_g2 + e_plus + e_2df
    eg2_mp2_0k = eqci + (emp2_big - emp2_6311gd) + hlc + zpe

    core.print_out('\n')
    core.print_out('  ==>  G1/G2 Energy Components  <==\n')
    core.print_out('\n')
    core.print_out('        QCISD(T):            %20.12lf\n' % eqci)
    core.print_out('        E(Delta):            %20.12lf\n' % e_delta_g2)
    core.print_out('        E(2DF):              %20.12lf\n' % e_2df)
    core.print_out('        E(+):                %20.12lf\n' % e_plus)
    core.print_out('        E(G1 HLC):           %20.12lf\n' % hlc1)
    core.print_out('        E(G2 HLC):           %20.12lf\n' % hlc)
    core.print_out('        E(ZPE):              %20.12lf\n' % zpe)
    core.print_out('\n')
    core.print_out('  ==>  0 Kelvin Results  <==\n')
    core.print_out('\n')
    eg2_0k = eg2 + zpe + hlc
    core.print_out('        G1:                  %20.12lf\n' % (eqci + e_plus + e_2df + hlc1 + zpe))
    core.print_out('        G2(MP2):             %20.12lf\n' % eg2_mp2_0k)
    core.print_out('        G2:                  %20.12lf\n' % eg2_0k)

    core.set_variable("G1 TOTAL ENERGY",eqci + e_plus + e_2df + hlc1 + zpe)
    core.set_variable("G2 TOTAL ENERGY",eg2_0k)
    core.set_variable("G2(MP2) TOTAL ENERGY",eg2_mp2_0k)

    core.print_out('\n')
    T = core.get_global_option('T')
    core.print_out('  ==>  %3.0lf Kelvin Results  <==\n'% T)
    core.print_out('\n')

    internal_energy = eg2_mp2_0k + du - zpe / 0.8929
    enthalpy        = eg2_mp2_0k + dh - zpe / 0.8929
    gibbs           = eg2_mp2_0k + dg - zpe / 0.8929

    core.print_out('        G2(MP2) energy:      %20.12lf\n' % internal_energy )
    core.print_out('        G2(MP2) enthalpy:    %20.12lf\n' % enthalpy)
    core.print_out('        G2(MP2) free energy: %20.12lf\n' % gibbs)
    core.print_out('\n')

    core.set_variable("G2(MP2) INTERNAL ENERGY",internal_energy)
    core.set_variable("G2(MP2) ENTHALPY",enthalpy)
    core.set_variable("G2(MP2) FREE ENERGY",gibbs)

    internal_energy = eg2_0k + du - zpe / 0.8929
    enthalpy        = eg2_0k + dh - zpe / 0.8929
    gibbs           = eg2_0k + dg - zpe / 0.8929

    core.print_out('        G2 energy:           %20.12lf\n' % internal_energy )
    core.print_out('        G2 enthalpy:         %20.12lf\n' % enthalpy)
    core.print_out('        G2 free energy:      %20.12lf\n' % gibbs)

    core.set_variable("CURRENT ENERGY",eg2_0k)

    core.set_variable("G2 INTERNAL ENERGY",internal_energy)
    core.set_variable("G2 ENTHALPY",enthalpy)
    core.set_variable("G2 FREE ENERGY",gibbs)

    core.clean()

    optstash.restore()

    # return 0K g2 results
    return eg2_0k
Exemple #15
0
def run_sapt_dft(name, **kwargs):
    optstash = p4util.OptionsState(['SCF_TYPE'], ['SCF', 'REFERENCE'], ['SCF', 'DFT_GRAC_SHIFT'],
                                   ['SCF', 'SAVE_JK'])

    core.tstart()
    # Alter default algorithm
    if not core.has_global_option_changed('SCF_TYPE'):
        core.set_global_option('SCF_TYPE', 'DF')

    core.prepare_options_for_module("SAPT")

    # Get the molecule of interest
    ref_wfn = kwargs.get('ref_wfn', None)
    if ref_wfn is None:
        sapt_dimer = kwargs.pop('molecule', core.get_active_molecule())
    else:
        core.print_out('Warning! SAPT argument "ref_wfn" is only able to use molecule information.')
        sapt_dimer = ref_wfn.molecule()

    sapt_dimer, monomerA, monomerB = proc_util.prepare_sapt_molecule(sapt_dimer, "dimer")

    # Grab overall settings
    mon_a_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_A")
    mon_b_shift = core.get_option("SAPT", "SAPT_DFT_GRAC_SHIFT_B")
    do_delta_hf = core.get_option("SAPT", "SAPT_DFT_DO_DHF")
    sapt_dft_functional = core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL")

    # Print out the title and some information
    core.print_out("\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("         " + "SAPT(DFT) Procedure".center(58) + "\n")
    core.print_out("\n")
    core.print_out("         " + "by Daniel G. A. Smith".center(58) + "\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("\n")

    core.print_out("  !!!  WARNING:  SAPT(DFT) capability is in beta. Please use with caution. !!!\n\n")

    core.print_out("  ==> Algorithm <==\n\n")
    core.print_out("   SAPT DFT Functional     %12s\n" % str(sapt_dft_functional))
    core.print_out("   Monomer A GRAC Shift    %12.6f\n" % mon_a_shift)
    core.print_out("   Monomer B GRAC Shift    %12.6f\n" % mon_b_shift)
    core.print_out("   Delta HF                %12s\n" % ("True" if do_delta_hf else "False"))
    core.print_out("   JK Algorithm            %12s\n" % core.get_global_option("SCF_TYPE"))
    core.print_out("\n")
    core.print_out("   Required computations:\n")
    if (do_delta_hf):
        core.print_out("     HF  (Dimer)\n")
        core.print_out("     HF  (Monomer A)\n")
        core.print_out("     HF  (Monomer B)\n")
    core.print_out("     DFT (Monomer A)\n")
    core.print_out("     DFT (Monomer B)\n")
    core.print_out("\n")

    if (sapt_dft_functional != "HF") and ((mon_a_shift == 0.0) or (mon_b_shift == 0.0)):
        raise ValidationError('SAPT(DFT): must set both "SAPT_DFT_GRAC_SHIFT_A" and "B".')

    if (core.get_option('SCF', 'REFERENCE') != 'RHF'):
        raise ValidationError('SAPT(DFT) currently only supports restricted references.')

    core.IO.set_default_namespace('dimer')
    data = {}

    if (core.get_global_option('SCF_TYPE') == 'DF'):
        # core.set_global_option('DF_INTS_IO', 'LOAD')
        core.set_global_option('DF_INTS_IO', 'SAVE')

    # # Compute dimer wavefunction
    hf_wfn_dimer = None
    if do_delta_hf:
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.set_global_option('DF_INTS_IO', 'SAVE')

        core.timer_on("SAPT(DFT): Dimer SCF")
        hf_data = {}
        hf_wfn_dimer = scf_helper("SCF", molecule=sapt_dimer, banner="SAPT(DFT): delta HF Dimer", **kwargs)
        hf_data["HF DIMER"] = core.variable("CURRENT ENERGY")
        core.timer_off("SAPT(DFT): Dimer SCF")

        core.timer_on("SAPT(DFT): Monomer A SCF")
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'dimer', 'monomerA')

        hf_wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs)
        hf_data["HF MONOMER A"] = core.variable("CURRENT ENERGY")
        core.timer_off("SAPT(DFT): Monomer A SCF")

        core.timer_on("SAPT(DFT): Monomer B SCF")
        core.set_global_option("SAVE_JK", True)
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

        hf_wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs)
        hf_data["HF MONOMER B"] = core.variable("CURRENT ENERGY")
        core.set_global_option("SAVE_JK", False)
        core.timer_off("SAPT(DFT): Monomer B SCF")

        # Grab JK object and set to A (so we do not save many JK objects)
        sapt_jk = hf_wfn_B.jk()
        hf_wfn_A.set_jk(sapt_jk)
        core.set_global_option("SAVE_JK", False)

        # Move it back to monomer A
        if (core.get_global_option('SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerB', 'dimer')

        core.print_out("\n")
        core.print_out("         ---------------------------------------------------------\n")
        core.print_out("         " + "SAPT(DFT): delta HF Segment".center(58) + "\n")
        core.print_out("\n")
        core.print_out("         " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n")
        core.print_out("         ---------------------------------------------------------\n")
        core.print_out("\n")

        # Build cache
        hf_cache = sapt_jk_terms.build_sapt_jk_cache(hf_wfn_A, hf_wfn_B, sapt_jk, True)

        # Electrostatics
        core.timer_on("SAPT(DFT):SAPT:elst")
        elst = sapt_jk_terms.electrostatics(hf_cache, True)
        hf_data.update(elst)
        core.timer_off("SAPT(DFT):SAPT:elst")

        # Exchange
        core.timer_on("SAPT(DFT):SAPT:exch")
        exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True)
        hf_data.update(exch)
        core.timer_off("SAPT(DFT):SAPT:exch")

        # Induction
        core.timer_on("SAPT(DFT):SAPT:ind")
        ind = sapt_jk_terms.induction(
            hf_cache,
            sapt_jk,
            True,
            maxiter=core.get_option("SAPT", "MAXITER"),
            conv=core.get_option("SAPT", "D_CONVERGENCE"),
            Sinf=core.get_option("SAPT", "DO_IND_EXCH_SINF"))
        hf_data.update(ind)
        core.timer_off("SAPT(DFT):SAPT:ind")

        dhf_value = hf_data["HF DIMER"] - hf_data["HF MONOMER A"] - hf_data["HF MONOMER B"]

        core.print_out("\n")
        core.print_out(print_sapt_hf_summary(hf_data, "SAPT(HF)", delta_hf=dhf_value))

        data["Delta HF Correction"] = core.variable("SAPT(DFT) Delta HF")
        sapt_jk.finalize()

        del hf_wfn_A, hf_wfn_B, sapt_jk

    if hf_wfn_dimer is None:
        dimer_wfn = core.Wavefunction.build(sapt_dimer, core.get_global_option("BASIS"))
    else:
        dimer_wfn = hf_wfn_dimer

    # Set the primary functional
    core.set_local_option('SCF', 'REFERENCE', 'RKS')

    # Compute Monomer A wavefunction
    core.timer_on("SAPT(DFT): Monomer A DFT")
    if (core.get_global_option('SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'dimer', 'monomerA')

    if mon_a_shift:
        core.set_global_option("DFT_GRAC_SHIFT", mon_a_shift)

    core.IO.set_default_namespace('monomerA')
    wfn_A = scf_helper(
        sapt_dft_functional, post_scf=False, molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs)
    data["DFT MONOMERA"] = core.variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)
    core.timer_off("SAPT(DFT): Monomer A DFT")

    # Compute Monomer B wavefunction
    core.timer_on("SAPT(DFT): Monomer B DFT")
    if (core.get_global_option('SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

    if mon_b_shift:
        core.set_global_option("DFT_GRAC_SHIFT", mon_b_shift)

    core.set_global_option("SAVE_JK", True)
    core.IO.set_default_namespace('monomerB')
    wfn_B = scf_helper(
        sapt_dft_functional, post_scf=False, molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs)
    data["DFT MONOMERB"] = core.variable("CURRENT ENERGY")

    # Save JK object
    sapt_jk = wfn_B.jk()
    wfn_A.set_jk(sapt_jk)
    core.set_global_option("SAVE_JK", False)

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)
    core.timer_off("SAPT(DFT): Monomer B DFT")

    # Write out header
    scf_alg = core.get_global_option("SCF_TYPE")
    sapt_dft_header(sapt_dft_functional, mon_a_shift, mon_b_shift, bool(do_delta_hf), scf_alg)

    # Call SAPT(DFT)
    sapt_jk = wfn_B.jk()
    sapt_dft(dimer_wfn, wfn_A, wfn_B, sapt_jk=sapt_jk, data=data, print_header=False)

    # Copy data back into globals
    for k, v in data.items():
        core.set_variable(k, v)

    core.tstop()

    optstash.restore()

    return dimer_wfn
Exemple #16
0
def sapt_empirical_dispersion(name, dimer_wfn, **kwargs):
    sapt_dimer = dimer_wfn.molecule()
    sapt_dimer, monomerA, monomerB = prepare_sapt_molecule(sapt_dimer, "dimer")
    disp_name = name.split("-")[1]

    # Get the names right between SAPT0 and FISAPT0
    saptd_name = name.split('-')[0].upper()
    if saptd_name == "SAPT0":
        sapt0_name = "SAPT0"
    else:
        sapt0_name = "SAPT"

    save_pair = (saptd_name == "FISAPT0")

    from .proc import build_disp_functor
    _, _disp_functor = build_disp_functor('hf-' + disp_name,
                                          restricted=True,
                                          save_pairwise_disp=save_pair,
                                          **kwargs)

    ## Dimer dispersion
    dimer_disp_energy = _disp_functor.compute_energy(dimer_wfn.molecule(),
                                                     dimer_wfn)
    ## Monomer dispersion
    mon_disp_energy = _disp_functor.compute_energy(monomerA)
    mon_disp_energy += _disp_functor.compute_energy(monomerB)

    disp_interaction_energy = dimer_disp_energy - mon_disp_energy
    core.set_variable(saptd_name + "-D DISP ENERGY", disp_interaction_energy)
    core.set_variable("SAPT DISP ENERGY", disp_interaction_energy)
    core.set_variable("DISPERSION CORRECTION ENERGY", disp_interaction_energy)
    core.set_variable(saptd_name + "DISPERSION CORRECTION ENERGY",
                      disp_interaction_energy)

    ## Set SAPT0-D3 variables
    total = disp_interaction_energy
    saptd_en = {}
    saptd_en['DISP'] = disp_interaction_energy
    for term in ['ELST', 'EXCH', 'IND']:
        en = core.variable(' '.join([sapt0_name, term, 'ENERGY']))
        saptd_en[term] = en
        core.set_variable(' '.join([saptd_name + '-D', term, 'ENERGY']), en)
        core.set_variable(' '.join(['SAPT', term, 'ENERGY']), en)
        total += en

    core.set_variable(saptd_name + '-D TOTAL ENERGY', total)
    core.set_variable('SAPT TOTAL ENERGY', total)
    core.set_variable('CURRENT ENERGY', total)

    ## Print Energy Summary
    units = (1000.0, constants.hartree2kcalmol, constants.hartree2kJmol)
    core.print_out(f"    => {saptd_name +'-D'} Energy Summary <=\n")

    core.print_out("  " + "-" * 104 + "\n")
    core.print_out(
        "    %-25s % 16.8f [mEh] % 16.8f [kcal/mol] % 16.8f [kJ/mol]\n" %
        ("Electrostatics", saptd_en['ELST'] * units[0],
         saptd_en['ELST'] * units[1], saptd_en['ELST'] * units[2]))
    core.print_out(
        "    %-25s % 16.8f [mEh] % 16.8f [kcal/mol] % 16.8f [kJ/mol]\n" %
        ("Exchange", saptd_en['EXCH'] * units[0], saptd_en['EXCH'] * units[1],
         saptd_en['EXCH'] * units[2]))
    core.print_out(
        "    %-25s % 16.8f [mEh] % 16.8f [kcal/mol] % 16.8f [kJ/mol]\n" %
        ("Induction", saptd_en['IND'] * units[0], saptd_en['IND'] * units[1],
         saptd_en['IND'] * units[2]))
    core.print_out(
        "    %-25s % 16.8f [mEh] % 16.8f [kcal/mol] % 16.8f [kJ/mol]\n" %
        ("Dispersion", saptd_en['DISP'] * units[0],
         saptd_en['DISP'] * units[1], saptd_en['DISP'] * units[2]))
    core.print_out(
        "  %-27s % 16.8f [mEh] % 16.8f [kcal/mol] % 16.8f [kJ/mol]\n" %
        ("Total " + saptd_name + "-D", total * units[0], total * units[1],
         total * units[2]))
    core.print_out("  " + "-" * 104 + "\n")

    if saptd_name == "FISAPT0":
        pw_disp = dimer_wfn.variable("PAIRWISE DISPERSION CORRECTION ANALYSIS")
        pw_disp.name = 'Empirical_Disp'
        filepath = core.get_option("FISAPT", "FISAPT_FSAPT_FILEPATH")
        fisapt_proc._drop(pw_disp, filepath)

    return dimer_wfn
Exemple #17
0
def print_ci_results(ciwfn, rname, scf_e, ci_e, print_opdm_no=False):
    """
    Printing for all CI Wavefunctions
    """

    # Print out energetics
    core.print_out("\n   ==> Energetics <==\n\n")
    core.print_out("    SCF energy =         %20.15f\n" % scf_e)
    if "CI" in rname:
        core.print_out("    Total CI energy =    %20.15f\n" % ci_e)
    elif "MP" in rname:
        core.print_out("    Total MP energy =    %20.15f\n" % ci_e)
    elif "ZAPT" in rname:
        core.print_out("    Total ZAPT energy =  %20.15f\n" % ci_e)
    else:
        core.print_out("    Total MCSCF energy = %20.15f\n" % ci_e)

    # Nothing to be done for ZAPT or MP
    if ("MP" in rname) or ("ZAPT" in rname):
        core.print_out("\n")
        return

    # Initial info
    ci_nroots = core.get_option("DETCI", "NUM_ROOTS")
    irrep_labels = ciwfn.molecule().irrep_labels()

    # Grab the D-vector
    dvec = ciwfn.D_vector()
    dvec.init_io_files(True)

    for root in range(ci_nroots):
        core.print_out("\n   ==> %s root %d information <==\n\n" % (rname, root))

        # Print total energy
        root_e = core.variable("CI ROOT %d TOTAL ENERGY" % (root))
        core.print_out("    %s Root %d energy =  %20.15f\n" % (rname, root, root_e))

        # Print natural occupations
        if print_opdm_no:
            core.print_out("\n   Active Space Natural occupation numbers:\n\n")

            occs_list = []
            r_opdm = ciwfn.get_opdm(root, root, "SUM", False)
            for h in range(len(r_opdm.nph)):
                if 0 in r_opdm.nph[h].shape:
                    continue
                nocc, rot = np.linalg.eigh(r_opdm.nph[h])
                for e in nocc:
                    occs_list.append((e, irrep_labels[h]))

            occs_list.sort(key=lambda x: -x[0])

            cnt = 0
            for value, label in occs_list:
                value, label = occs_list[cnt]
                core.print_out("      %4s  % 8.6f" % (label, value))
                cnt += 1
                if (cnt % 3) == 0:
                    core.print_out("\n")

            if (cnt % 3):
                core.print_out("\n")

        # Print CIVector information
        ciwfn.print_vector(dvec, root)

    # True to keep the file
    dvec.close_io_files(True)
Exemple #18
0
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`
       * :psivar:`db_name DATABASE MEAN ABSOLUTE DEVIATION`
       * :psivar:`db_name DATABASE ROOT-MEAN-SQUARE DEVIATION`
       * 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:`psi4/share/psi4/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: str
    :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:`psi4.driver.energy`.

    :type db_name: str
    :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: str
    :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 ``bsse_type="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: str
    :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: List[str]
    :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: Union[str, List[str]]
    :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 :ref:`sec:availableDatabases` 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