Example #1
0
def test_renamed_qcvars():
    psi4.set_variable("SCS(N)-MP2 TOTAL ENERGY", 3.3)

    with pytest.warns(FutureWarning) as e:
        ans = psi4.variable("SCSN-MP2 TOTAL ENERGY")

    assert ans == 3.3
Example #2
0
def run_vibronic(state1,state2,method1='ccsd',method2='eom-ccsd'):
    """
    Performs the different steps needed to compute the spectrum
    """

    lowermethod1 = method1.lower()
    lowermethod2 = method2.lower()

    # Perform optimizations and save states 
    psi4.set_active_molecule(state1)
    print "Optimizing Ground State..."
    optimize(lowermethod1)
    E1=psi4.get_variable("CURRENT ENERGY")

    psi4.set_active_molecule(state2)
    print "Optimizing Excited State..."
    optimize(lowermethod2)
    E2=psi4.get_variable("CURRENT ENERGY")
    
    # Compute Hessian on first state (for AS model)
    psi4.set_active_molecule(state1)
    frequencies(lowermethod1)
    
    # Select states for vibronic:
    #  active molecule is initial state
    #  secondary molecule is final state
    psi4.set_active_molecule(state1)
    psi4.set_secondary_molecule(state2)
    
    # Compute adiavatic energy
    DE = (E2-E1)
    psi4.set_variable("CURRENT ENERGY",DE)

    # Call the plugin
    psi4.plugin('vibronic.so')
Example #3
0
def test_deprecated_component_dipole():

    #with pytest.warns(FutureWarning) as e:
    psi4.set_variable("current dipole x", 5)

    with pytest.warns(FutureWarning) as e:
        ans = psi4.variable("current dipole x")

    assert ans == 5
Example #4
0
def run_cis(name, **kwargs):
    r"""Function encoding sequence of PSI module and plugin calls so that
    cis can be called via :py:func:`~driver.energy`. For post-scf plugins.

    >>> energy('cis')

    """
    lowername = name.lower()
    kwargs = p4util.kwargs_lower(kwargs)

    # Your plugin's psi4 run sequence goes here
    psi4.set_local_option('CIS', 'PRINT', 1)
    scf_helper(name, **kwargs)
    returnvalue = psi4.plugin('cis.so')
    psi4.set_variable('CURRENT ENERGY', returnvalue)
Example #5
0
def run_main(name, **kwargs):
    r"""Function encoding sequence of PSI module and plugin calls so that
    main can be called via :py:func:`~driver.energy`. For post-scf plugins.

    >>> energy('main')

    """
    lowername = name.lower()
    kwargs = p4util.kwargs_lower(kwargs)

    # Your plugin's psi4 run sequence goes here
    #    psi4.set_global_option('BASIS', 'sto-3g')
    #    psi4.set_local_option('MAIN', 'PRINT', 1)
    scf_helper(name, **kwargs)
    returnvalue = psi4.plugin('main.so')
    psi4.set_variable('CURRENT ENERGY', returnvalue)
def run_dpd_unit_test(name, **kwargs):
    r"""Function encoding sequence of PSI module and plugin calls so that
    dpd_unit_test can be called via :py:func:`~driver.energy`. For post-scf plugins.

    >>> energy('dpd_unit_test')

    """
    lowername = name.lower()
    kwargs = p4util.kwargs_lower(kwargs)

    # Your plugin's psi4 run sequence goes here
    psi4.set_global_option('BASIS', 'sto-3g')
    psi4.set_local_option('DPD_UNIT_TEST', 'PRINT', 1)
    scf_helper(name, **kwargs)
    returnvalue = psi4.plugin('dpd_unit_test.so')
    psi4.set_variable('CURRENT ENERGY', returnvalue)
Example #7
0
def run_dfdcft(name, **kwargs):
    r"""Function encoding sequence of PSI module and plugin calls so that
    dfdcft can be called via :py:func:`~driver.energy`. For post-scf plugins.

    >>> energy('dfdcft')

    """
    lowername = name.lower()
    kwargs = p4util.kwargs_lower(kwargs)

#    psi4.set_local_option('SCF', 'REFERENCE', 'UHF')
#    psi4.set_local_option('DFDCFT', 'REFERENCE', 'UHF')

    # Your plugin's psi4 run sequence goes here
    scf_helper(name, **kwargs)
    returnvalue = psi4.plugin('dfdcft.so')
    psi4.set_variable('CURRENT ENERGY', returnvalue)
Example #8
0
def run_gaussian_2(name, **kwargs):

    # throw an exception for open-shells
    if (psi4.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','SCF_TYPE'])

    # override default scf_type
    psi4.set_local_option('SCF','SCF_TYPE','PK')

    # optimize geometry at scf level
    psi4.clean()
    psi4.set_global_option('BASIS',"6-31G(D)")
    driver.optimize('scf')
    psi4.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 = psi4.get_variable('INTERNAL ENERGY CORRECTION')
    dh = psi4.get_variable('ENTHALPY CORRECTION')
    dg = psi4.get_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 / p4const.psi_hartree2wavenumbers * 0.8929 * 0.5
    psi4.clean()

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

    # qcisd(t)
    psi4.set_local_option('FNOCC','COMPUTE_MP4_TRIPLES',"TRUE")
    psi4.set_global_option('FREEZE_CORE',"TRUE")
    psi4.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 = psi4.get_variable("QCISD(T) TOTAL ENERGY")
    emp4_6311gd  = psi4.get_variable("MP4 TOTAL ENERGY")
    emp2_6311gd  = psi4.get_variable("MP2 TOTAL ENERGY")
    psi4.clean()

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

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

    # big basis mp2
    psi4.set_global_option('BASIS',"6-311+G(3DF_2P)")
    #run_fnocc('_mp2',**kwargs)
    driver.energy('mp2')
    emp2_big = psi4.get_variable("MP2 TOTAL ENERGY")
    psi4.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

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

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

    psi4.print_out('\n')
    T = psi4.get_global_option('T')
    psi4.print_out('  ==>  %3.0lf Kelvin Results  <==\n'% T)
    psi4.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

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

    psi4.set_variable("G2(MP2) INTERNAL ENERGY",internal_energy)
    psi4.set_variable("G2(MP2) ENTHALPY",enthalpy)
    psi4.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

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

    psi4.set_variable("CURRENT ENERGY",eg2_0k)

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

    psi4.clean()

    optstash.restore()

    # return 0K g2 results
    return eg2_0k
Example #9
0
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None):
    """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/)
    to compute the -D correction of level *dashlvl* using parameters for
    the functional *func*. The dictionary *dashparam* can be used to supply
    a full set of dispersion parameters in the absense of *func* or to supply
    individual overrides in the presence of *func*. Returns energy if *dertype* is 0,
    gradient if *dertype* is 1, else tuple of energy and gradient if *dertype*
    unspecified. The dftd3 executable must be independently compiled and found in
    :envvar:`PATH`.

    """
    # Validate arguments
    if self is None:
        self = psi4.get_active_molecule()

    dashlvl = dashlvl.lower()
    dashlvl = dash_alias['-' + dashlvl][1:] if (
        '-' + dashlvl) in dash_alias.keys() else dashlvl
    if dashlvl not in dashcoeff.keys():
        raise ValidationError(
            """-D correction level %s is not available. Choose among %s.""" %
            (dashlvl, dashcoeff.keys()))

    if dertype is None:
        dertype = -1
    elif der0th.match(str(dertype)):
        dertype = 0
    elif der1st.match(str(dertype)):
        dertype = 1
    elif der2nd.match(str(dertype)):
        raise ValidationError(
            'Requested derivative level \'dertype\' %s not valid for run_dftd3.'
            % (dertype))
    else:
        raise ValidationError(
            'Requested derivative level \'dertype\' %s not valid for run_dftd3.'
            % (dertype))

    if func is None:
        if dashparam is None:
            # defunct case
            raise ValidationError(
                """Parameters for -D correction missing. Provide a func or a dashparam kwarg."""
            )
        else:
            # case where all param read from dashparam dict (which must have all correct keys)
            func = 'custom'
            dashcoeff[dashlvl][func] = {}
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]
                else:
                    raise ValidationError(
                        """Parameter %s is missing from dashparam dict %s.""" %
                        (key, dashparam))
    else:
        func = func.lower()
        if func not in dashcoeff[dashlvl].keys():
            raise ValidationError(
                """Functional %s is not available for -D level %s.""" %
                (func, dashlvl))
        if dashparam is None:
            # (normal) case where all param taken from dashcoeff above
            pass
        else:
            # case where items in dashparam dict can override param taken from dashcoeff above
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]

    # Move ~/.dftd3par.<hostname> out of the way so it won't interfere
    defaultfile = os.path.expanduser(
        '~') + '/.dftd3par.' + socket.gethostname()
    defmoved = False
    if os.path.isfile(defaultfile):
        os.rename(defaultfile, defaultfile + '_hide')
        defmoved = True

    # Setup unique scratch directory and move in
    current_directory = os.getcwd()
    psioh = psi4.IOManager.shared_object()
    psio = psi4.IO.shared_object()
    os.chdir(psioh.get_default_path())
    dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \
        '.dftd3.' + str(random.randint(0, 99999))
    if os.path.exists(dftd3_tmpdir) is False:
        os.mkdir(dftd3_tmpdir)
    os.chdir(dftd3_tmpdir)

    # Write dftd3_parameters file that governs dispersion calc
    paramfile = './dftd3_parameters'
    pfile = open(paramfile, 'w')
    pfile.write(dash_server(func, dashlvl, 'dftd3'))
    pfile.close()

    # Write dftd3_geometry file that supplies geometry to dispersion calc
    geomfile = './dftd3_geometry.xyz'
    gfile = open(geomfile, 'w')
    numAtoms = self.natom()
    geom = self.save_string_xyz()
    reals = []
    for line in geom.splitlines():
        if line.split()[0] == 'Gh':
            numAtoms -= 1
        else:
            reals.append(line)

    gfile.write(str(numAtoms) + '\n')
    for line in reals:
        gfile.write(line.strip() + '\n')
    gfile.close()

    # Call dftd3 program
    try:
        dashout = subprocess.Popen(['dftd3', geomfile, '-grad'],
                                   stdout=subprocess.PIPE)
    except OSError:
        raise ValidationError('Program dftd3 not found in path.')
    out, err = dashout.communicate()

    # Parse output (could go further and break into E6, E8, E10 and Cn coeff)
    success = False
    for line in out.splitlines():
        if re.match(' Edisp /kcal,au', line):
            sline = line.split()
            dashd = float(sline[3])
        if re.match(' normal termination of dftd3', line):
            success = True

    if not success:
        raise ValidationError('Program dftd3 did not complete successfully.')

    # Parse grad output
    derivfile = './dftd3_gradient'
    dfile = open(derivfile, 'r')
    dashdderiv = []
    i = 0
    for line in geom.splitlines():
        if i == 0:
            i += 1
        else:
            if line.split()[0] == 'Gh':
                dashdderiv.append([0.0, 0.0, 0.0])
            else:
                temp = dfile.readline()
                dashdderiv.append(
                    [float(x.replace('D', 'E')) for x in temp.split()])
    dfile.close()

    if len(dashdderiv) != self.natom():
        raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \
            (len(dashdderiv), self.natom()))
    psi_dashdderiv = psi4.Matrix(self.natom(), 3)
    psi_dashdderiv.set(dashdderiv)

    # Print program output to file if verbose
    verbose = psi4.get_option('SCF', 'PRINT')
    if verbose >= 3:
        psi4.print_out('\n  ==> DFTD3 Output <==\n')
        psi4.print_out(out)
        dfile = open(derivfile, 'r')
        psi4.print_out(dfile.read().replace('D', 'E'))
        dfile.close()
        psi4.print_out('\n')

    # Clean up files and remove scratch directory
    os.unlink(paramfile)
    os.unlink(geomfile)
    os.unlink(derivfile)
    if defmoved is True:
        os.rename(defaultfile + '_hide', defaultfile)

    os.chdir('..')
    try:
        shutil.rmtree(dftd3_tmpdir)
    except OSError as e:
        ValidationError('Unable to remove dftd3 temporary directory %s' % e,
                        file=sys.stderr)
    os.chdir(current_directory)

    # return -D & d(-D)/dx
    psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd)
    if dertype == -1:
        return dashd, dashdderiv
    elif dertype == 0:
        return dashd
    elif dertype == 1:
        return psi_dashdderiv
Example #10
0
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None, verbose=False):
    """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/)
    to compute the -D correction of level *dashlvl* using parameters for
    the functional *func*. The dictionary *dashparam* can be used to supply
    a full set of dispersion parameters in the absense of *func* or to supply
    individual overrides in the presence of *func*. Returns energy if *dertype* is 0,
    gradient if *dertype* is 1, else tuple of energy and gradient if *dertype*
    unspecified. The dftd3 executable must be independently compiled and found in
    :envvar:`PATH` or :envvar:`PSIPATH`.
    *self* may be either a qcdb.Molecule (sensibly) or a psi4.Molecule
    (works b/c psi4.Molecule has been extended by this method py-side and
    only public interface fns used) or a string that can be instantiated
    into a qcdb.Molecule.

    """
    # Create (if necessary) and update qcdb.Molecule
    if isinstance(self, Molecule):
        # called on a qcdb.Molecule
        pass
    elif isinstance(self, psi4.Molecule):
        # called on a python export of a psi4.Molecule (py-side through Psi4's driver)
        self.create_psi4_string_from_molecule()
    elif isinstance(self, basestring):
        # called on a string representation of a psi4.Molecule (c-side through psi4.Dispersion)
        self = Molecule(self)
    else:
        raise ValidationError("""Argument mol must be psi4string or qcdb.Molecule""")
    self.update_geometry()

    # Validate arguments
    dashlvl = dashlvl.lower()
    dashlvl = dash_alias['-' + dashlvl][1:] if ('-' + dashlvl) in dash_alias.keys() else dashlvl
    if dashlvl not in dashcoeff.keys():
        raise ValidationError("""-D correction level %s is not available. Choose among %s.""" % (dashlvl, dashcoeff.keys()))

    if dertype is None:
        dertype = -1
    elif der0th.match(str(dertype)):
        dertype = 0
    elif der1st.match(str(dertype)):
        dertype = 1
    elif der2nd.match(str(dertype)):
        raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype))
    else:
        raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype))

    if func is None:
        if dashparam is None:
            # defunct case
            raise ValidationError("""Parameters for -D correction missing. Provide a func or a dashparam kwarg.""")
        else:
            # case where all param read from dashparam dict (which must have all correct keys)
            func = 'custom'
            dashcoeff[dashlvl][func] = {}
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]
                else:
                    raise ValidationError("""Parameter %s is missing from dashparam dict %s.""" % (key, dashparam))
    else:
        func = func.lower()
        if func not in dashcoeff[dashlvl].keys():
            raise ValidationError("""Functional %s is not available for -D level %s.""" % (func, dashlvl))
        if dashparam is None:
            # (normal) case where all param taken from dashcoeff above
            pass
        else:
            # case where items in dashparam dict can override param taken from dashcoeff above
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]

    # Move ~/.dftd3par.<hostname> out of the way so it won't interfere
    defaultfile = os.path.expanduser('~') + '/.dftd3par.' + socket.gethostname()
    defmoved = False
    if os.path.isfile(defaultfile):
        os.rename(defaultfile, defaultfile + '_hide')
        defmoved = True

    # Find environment by merging PSIPATH and PATH environment variables
    lenv = {
        'PATH': ':'.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '']) + \
                ':' + os.environ.get('PATH'),
        'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH')
        }

    # Find out if running from Psi4 for scratch details and such
    try:
        psi4.version()
    except NameError:
        isP4regime = False
    else:
        isP4regime = True

    # Setup unique scratch directory and move in
    current_directory = os.getcwd()
    if isP4regime:
        psioh = psi4.IOManager.shared_object()
        psio = psi4.IO.shared_object()
        os.chdir(psioh.get_default_path())
        dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \
            '.dftd3.' + str(random.randint(0, 99999))
    else:
        dftd3_tmpdir = os.environ['HOME'] + os.sep + 'dftd3_' + str(random.randint(0, 99999))
    if os.path.exists(dftd3_tmpdir) is False:
        os.mkdir(dftd3_tmpdir)
    os.chdir(dftd3_tmpdir)

    # Write dftd3_parameters file that governs dispersion calc
    paramcontents = dash_server(func, dashlvl, 'dftd3')
    paramfile1 = 'dftd3_parameters'  # older patched name
    with open(paramfile1, 'w') as handle:
        handle.write(paramcontents)
    paramfile2 = '.dftd3par.local'  # new mainline name
    with open(paramfile2, 'w') as handle:
        handle.write(paramcontents)

    # Write dftd3_geometry file that supplies geometry to dispersion calc
    numAtoms = self.natom()
    geom = self.save_string_xyz()
    reals = []
    for line in geom.splitlines():
        lline = line.split()
        if len(lline) != 4:
            continue
        if lline[0] == 'Gh':
            numAtoms -= 1
        else:
            reals.append(line)

    geomtext = str(numAtoms) + '\n\n'
    for line in reals:
        geomtext += line.strip() + '\n'
    geomfile = './dftd3_geometry.xyz'
    with open(geomfile, 'w') as handle:
        handle.write(geomtext)
    # TODO somehow the variations on save_string_xyz and
    #   whether natom and chgmult does or doesn't get written
    #   have gotten all tangled. I fear this doesn't work
    #   the same btwn libmints and qcdb or for ghosts

    # Call dftd3 program
    command = ['dftd3', geomfile]
    if dertype != 0:
        command.append('-grad')
    try:
        dashout = subprocess.Popen(command, stdout=subprocess.PIPE, env=lenv)
    except OSError as e:
        raise ValidationError('Program dftd3 not found in path. %s' % e)
    out, err = dashout.communicate()

    # Parse output (could go further and break into E6, E8, E10 and Cn coeff)
    success = False
    for line in out.splitlines():
        if re.match(' Edisp /kcal,au', line):
            sline = line.split()
            dashd = float(sline[3])
        if re.match(' normal termination of dftd3', line):
            success = True

    if not success:
        os.chdir(current_directory)
        raise Dftd3Error("""Unsuccessful run. Possibly -D variant not available in dftd3 version.""")

    # Parse grad output
    if dertype != 0:
        derivfile = './dftd3_gradient'
        dfile = open(derivfile, 'r')
        dashdderiv = []
        for line in geom.splitlines():
            lline = line.split()
            if len(lline) != 4:
                continue
            if lline[0] == 'Gh':
                dashdderiv.append([0.0, 0.0, 0.0])
            else:
                dashdderiv.append([float(x.replace('D', 'E')) for x in dfile.readline().split()])
        dfile.close()

        if len(dashdderiv) != self.natom():
            raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \
                (len(dashdderiv), self.natom()))

    # Prepare results for Psi4
    if isP4regime and dertype != 0:
        psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd)
        psi_dashdderiv = psi4.Matrix(self.natom(), 3)
        psi_dashdderiv.set(dashdderiv)

    # Print program output to file if verbose
    if isP4regime:
        verbose = True if psi4.get_option('SCF', 'PRINT') >= 3 else False
    if verbose:

        text = '\n  ==> DFTD3 Output <==\n'
        text += out
        if dertype != 0:
            with open(derivfile, 'r') as handle:
                text += handle.read().replace('D', 'E')
            text += '\n'
        if isP4regime:
            psi4.print_out(text)
        else:
            print(text)

    # Clean up files and remove scratch directory
    os.unlink(paramfile1)
    os.unlink(paramfile2)
    os.unlink(geomfile)
    if dertype != 0:
        os.unlink(derivfile)
    if defmoved is True:
        os.rename(defaultfile + '_hide', defaultfile)

    os.chdir('..')
    try:
        shutil.rmtree(dftd3_tmpdir)
    except OSError as e:
        ValidationError('Unable to remove dftd3 temporary directory %s' % e)
    os.chdir(current_directory)

    # return -D & d(-D)/dx
    if dertype == -1:
        return dashd, dashdderiv
    elif dertype == 0:
        return dashd
    elif dertype == 1:
        return psi_dashdderiv
Example #11
0
def _nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.

    Parameters
    ----------
    func : python function
        Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian.
    method_string : str
        Lowername to be passed to function
    molecule : psi4.Molecule (default: Global Molecule)
        Molecule to use in all computations
    return_wfn : bool (default: False)
        Return a wavefunction or not
    bsse_type : str or list (default: None, this function is not called)
        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function.
    max_nbody : int
        Maximum n-body to compute, cannot exceede the number of fragments in the moleucle
    ptype : str
        Type of the procedure passed in
    return_total_data : bool (default: False)
        If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data

    Returns
    -------
    data : return type of func
        The interaction data
    wfn : psi4.Wavefunction (optional)
        A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains 

    Notes
    -----
    This is a generalized univeral function for compute interaction quantities.

    Examples
    --------
    """

    ### ==> Parse some kwargs <==
    kwargs = p4util.kwargs_lower(kwargs)
    return_wfn = kwargs.pop('return_wfn', False)
    ptype = kwargs.pop('ptype', None)
    return_total_data = kwargs.pop('return_total_data', False)
    molecule = kwargs.pop('molecule', psi4.get_active_molecule())
    molecule.update_geometry()
    psi4.clean_variables()

    if ptype not in ['energy', 'gradient', 'hessian']:
        raise ValidationError("""N-Body driver: The ptype '%s' is not regonized.""" % ptype)

    # Figure out BSSE types
    do_cp = False
    do_nocp = False
    do_vmfc = False
    return_method = False

    # Must be passed bsse_type
    bsse_type_list = kwargs.pop('bsse_type')
    if bsse_type_list is None:
        raise ValidationError("N-Body GUFunc: Must pass a bsse_type")
    if not isinstance(bsse_type_list, list):
        bsse_type_list = [bsse_type_list]

    for num, btype in enumerate(bsse_type_list):
        if btype.lower() == 'cp':
            do_cp = True
            if (num == 0): return_method = 'cp'
        elif btype.lower() == 'nocp':
            do_nocp = True
            if (num == 0): return_method = 'nocp'
        elif btype.lower() == 'vmfc':
            do_vmfc = True
            if (num == 0): return_method = 'vmfc'
        else:
            raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower())

    max_nbody = kwargs.get('max_nbody', -1)
    max_frag = molecule.nfragments()
    if max_nbody == -1:
        max_nbody = molecule.nfragments()
    else:
        max_nbody = min(max_nbody, max_frag)

    # What levels do we need?
    nbody_range = range(1, max_nbody + 1)
    fragment_range = range(1, max_frag + 1)

    # If we are doing CP lets save them integrals
    if 'cp' in bsse_type_list and (len(bsse_type_list) == 1):
        # Set to save RI integrals for repeated full-basis computations
        ri_ints_io = psi4.get_global_option('DF_INTS_IO')

        # inquire if above at all applies to dfmp2 or just scf
        psi4.set_global_option('DF_INTS_IO', 'SAVE')
        psioh = psi4.IOManager.shared_object()
        psioh.set_specific_retention(97, True)


    bsse_str = bsse_type_list[0]
    if len(bsse_type_list) >1:
        bsse_str =  str(bsse_type_list)
    psi4.print_out("\n\n")
    psi4.print_out("   ===> N-Body Interaction Abacus <===\n")
    psi4.print_out("        BSSE Treatment:                     %s\n" % bsse_str)


    cp_compute_list = {x:set() for x in nbody_range}
    nocp_compute_list = {x:set() for x in nbody_range}
    vmfc_compute_list = {x:set() for x in nbody_range}
    vmfc_level_list = {x:set() for x in nbody_range} # Need to sum something slightly different

    # Build up compute sets
    if do_cp:
        # Everything is in dimer basis
        basis_tuple = tuple(fragment_range)
        for nbody in nbody_range:
            for x in it.combinations(fragment_range, nbody):
                cp_compute_list[nbody].add( (x, basis_tuple) )

    if do_nocp:
        # Everything in monomer basis
        for nbody in nbody_range:
            for x in it.combinations(fragment_range, nbody):
                nocp_compute_list[nbody].add( (x, x) )

    if do_vmfc:
        # Like a CP for all combinations of pairs or greater
        for nbody in nbody_range:
            for cp_combos in it.combinations(fragment_range, nbody):
                basis_tuple = tuple(cp_combos)
                for interior_nbody in nbody_range:
                    for x in it.combinations(cp_combos, interior_nbody):
                        combo_tuple = (x, basis_tuple)
                        vmfc_compute_list[interior_nbody].add( combo_tuple )
                        vmfc_level_list[len(basis_tuple)].add( combo_tuple )

    # Build a comprehensive compute_range
    compute_list = {x:set() for x in nbody_range}
    for n in nbody_range:
        compute_list[n] |= cp_compute_list[n]
        compute_list[n] |= nocp_compute_list[n]
        compute_list[n] |= vmfc_compute_list[n]
        psi4.print_out("        Number of %d-body computations:     %d\n" % (n, len(compute_list[n])))


    # Build size and slices dictionaries
    fragment_size_dict = {frag: molecule.extract_subsets(frag).natom() for
                                           frag in range(1, max_frag+1)}

    start = 0
    fragment_slice_dict = {}
    for k, v in fragment_size_dict.items():
        fragment_slice_dict[k] = slice(start, start + v)
        start += v

    molecule_total_atoms = sum(fragment_size_dict.values())

    # Now compute the energies
    energies_dict = {}
    ptype_dict = {}
    for n in compute_list.keys():
        psi4.print_out("\n   ==> N-Body: Now computing %d-body complexes <==\n\n" % n)
        print("\n   ==> N-Body: Now computing %d-body complexes <==\n" % n)
        total = len(compute_list[n])
        for num, pair in enumerate(compute_list[n]):
            psi4.print_out("\n       N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n" %
                                                                    (num + 1, total, str(pair[0]), str(pair[1])))
            ghost = list(set(pair[1]) - set(pair[0]))

            current_mol = molecule.extract_subsets(list(pair[0]), ghost)
            ptype_dict[pair] = func(method_string, molecule=current_mol, **kwargs)
            energies_dict[pair] = psi4.get_variable("CURRENT ENERGY")
            psi4.print_out("\n       N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n" % 
                                                                (str(pair[0]), str(pair[1]), energies_dict[pair]))

            if 'cp' in bsse_type_list and (len(bsse_type_list) == 1):
                psi4.set_global_option('DF_INTS_IO', 'LOAD')

            psi4.clean()

    # Final dictionaries
    cp_energy_by_level   = {n: 0.0 for n in nbody_range}
    nocp_energy_by_level = {n: 0.0 for n in nbody_range}

    cp_energy_body_dict =   {n: 0.0 for n in nbody_range}
    nocp_energy_body_dict = {n: 0.0 for n in nbody_range}
    vmfc_energy_body_dict = {n: 0.0 for n in nbody_range}

    # Build out ptype dictionaries if needed
    if ptype != 'energy':
        if ptype == 'gradient':
            arr_shape = (molecule_total_atoms, 3)
        elif ptype == 'hessian':
            arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3)
        else:
            raise KeyError("N-Body: ptype '%s' not recognized" % ptype)

        cp_ptype_by_level   =  {n: np.zeros(arr_shape) for n in nbody_range}
        nocp_ptype_by_level =  {n: np.zeros(arr_shape) for n in nbody_range}

        cp_ptype_body_dict   = {n: np.zeros(arr_shape) for n in nbody_range}
        nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range}
        vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range}
    else:
        cp_ptype_by_level, cp_ptype_body_dict = None, None
        nocp_ptype_by_level, nocp_ptype_body_dict = None, None
        vmfc_ptype_by_level= None


    # Sum up all of the levels
    for n in nbody_range:

        # Energy
        cp_energy_by_level[n]   = sum(energies_dict[v] for v in cp_compute_list[n])
        nocp_energy_by_level[n] = sum(energies_dict[v] for v in nocp_compute_list[n])

        # Special vmfc case
        if n > 1:
            vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1]
        for tup in vmfc_level_list[n]:
            vmfc_energy_body_dict[n] += ((-1) ** (n - len(tup[0]))) * energies_dict[tup]


        # Do ptype
        if ptype != 'energy':
            _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      cp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      nocp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      vmfc_ptype_by_level[n], vmfc=True)
    # Compute cp energy and ptype
    if do_cp:
        for n in nbody_range:
            if n == max_frag:
                cp_energy_body_dict[n] = cp_energy_by_level[n]
                if ptype != 'energy':
                    cp_ptype_body_dict[n][:] = cp_ptype_by_level[n]
                continue

            for k in range(1, n + 1):
                take_nk =  nCr(max_frag - k - 1, n - k)
                sign = ((-1) ** (n - k))
                value = cp_energy_by_level[k]
                cp_energy_body_dict[n] += take_nk * sign * value

                if ptype != 'energy':
                    value = cp_ptype_by_level[k]
                    cp_ptype_body_dict[n] += take_nk * sign * value

        _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)")
        cp_interaction_energy = cp_energy_body_dict[max_nbody] - cp_energy_body_dict[1]
        psi4.set_variable('Counterpoise Corrected Total Energy', cp_energy_body_dict[max_nbody])
        psi4.set_variable('Counterpoise Corrected Interaction Energy', cp_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(var_key, cp_energy_body_dict[n] - cp_energy_body_dict[1])

    # Compute nocp energy and ptype
    if do_nocp:
        for n in nbody_range:
            if n == max_frag:
                nocp_energy_body_dict[n] = nocp_energy_by_level[n]
                if ptype != 'energy':
                    nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n]
                continue

            for k in range(1, n + 1):
                take_nk =  nCr(max_frag - k - 1, n - k)
                sign = ((-1) ** (n - k))
                value = nocp_energy_by_level[k]
                nocp_energy_body_dict[n] += take_nk * sign * value

                if ptype != 'energy':
                    value = nocp_ptype_by_level[k]
                    nocp_ptype_body_dict[n] += take_nk * sign * value

        _print_nbody_energy(nocp_energy_body_dict, "Non-Counterpoise Corrected (NoCP)")
        nocp_interaction_energy = nocp_energy_body_dict[max_nbody] - nocp_energy_body_dict[1]
        psi4.set_variable('Non-Counterpoise Corrected Total Energy', nocp_energy_body_dict[max_nbody])
        psi4.set_variable('Non-Counterpoise Corrected Interaction Energy', nocp_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1])


    # Compute vmfc energy and ptype
    if do_vmfc:
        _print_nbody_energy(vmfc_energy_body_dict, "Valiron-Mayer Function Couterpoise (VMFC)")
        vmfc_interaction_energy = vmfc_energy_body_dict[max_nbody] - vmfc_energy_body_dict[1]
        psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy', vmfc_energy_body_dict[max_nbody])
        psi4.set_variable('Valiron-Mayer Function Couterpoise Interaction Energy', vmfc_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1])

    if return_method == 'cp':
        ptype_body_dict = cp_ptype_body_dict
        energy_body_dict = cp_energy_body_dict
    elif return_method == 'nocp':
        ptype_body_dict = nocp_ptype_body_dict
        energy_body_dict = nocp_energy_body_dict
    elif return_method == 'vmfc':
        ptype_body_dict = vmfc_ptype_body_dict
        energy_body_dict = vmfc_energy_body_dict
    else:
        raise ValidationError("N-Body Wrapper: Invalid return type. Should never be here, please post this error on github.")


    # Figure out and build return types
    if return_total_data:
        ret_energy = energy_body_dict[max_nbody]
    else:
        ret_energy = energy_body_dict[max_nbody]
        ret_energy -= energy_body_dict[1]


    if ptype != 'energy':
        if return_total_data:
            np_final_ptype = ptype_body_dict[max_nbody].copy()
        else:
            np_final_ptype = ptype_body_dict[max_nbody].copy()
            np_final_ptype -= ptype_body_dict[1]

            ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape)
            ret_ptype_view = np.asarray(final_ptype)
            ret_ptype_view[:] = np_final_ptype
    else:
        ret_ptype = ret_energy

    # Build and set a wavefunction
    wfn = psi4.new_wavefunction(molecule, 'sto-3g')
    wfn.nbody_energy = energies_dict
    wfn.nbody_ptype = ptype_dict
    wfn.nbody_body_energy = energy_body_dict
    wfn.nbody_body_ptype = ptype_body_dict

    if ptype == 'gradient':
        wfn.set_gradient(ret_ptype)
    elif ptype == 'hessian':
        wfn.set_hessian(ret_ptype)

    psi4.set_variable("CURRENT ENERGY", ret_energy)

    if return_wfn:
        return (ret_ptype, wfn)
    else:
        return ret_ptype
Example #12
0
def run_dftd3(self,
              func=None,
              dashlvl=None,
              dashparam=None,
              dertype=None,
              verbose=False):
    """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/)
    to compute the -D correction of level *dashlvl* using parameters for
    the functional *func*. The dictionary *dashparam* can be used to supply
    a full set of dispersion parameters in the absense of *func* or to supply
    individual overrides in the presence of *func*. Returns energy if *dertype* is 0,
    gradient if *dertype* is 1, else tuple of energy and gradient if *dertype*
    unspecified. The dftd3 executable must be independently compiled and found in
    :envvar:`PATH` or :envvar:`PSIPATH`.
    *self* may be either a qcdb.Molecule (sensibly) or a psi4.Molecule
    (works b/c psi4.Molecule has been extended by this method py-side and
    only public interface fns used) or a string that can be instantiated
    into a qcdb.Molecule.

    """
    # Create (if necessary) and update qcdb.Molecule
    if isinstance(self, Molecule):
        # called on a qcdb.Molecule
        pass
    elif isinstance(self, psi4.Molecule):
        # called on a python export of a psi4.Molecule (py-side through Psi4's driver)
        self.create_psi4_string_from_molecule()
    elif isinstance(self, basestring):
        # called on a string representation of a psi4.Molecule (c-side through psi4.Dispersion)
        self = Molecule(self)
    else:
        raise ValidationError(
            """Argument mol must be psi4string or qcdb.Molecule""")
    self.update_geometry()

    # Validate arguments
    dashlvl = dashlvl.lower()
    dashlvl = dash_alias['-' + dashlvl][1:] if (
        '-' + dashlvl) in dash_alias.keys() else dashlvl
    if dashlvl not in dashcoeff.keys():
        raise ValidationError(
            """-D correction level %s is not available. Choose among %s.""" %
            (dashlvl, dashcoeff.keys()))

    if dertype is None:
        dertype = -1
    elif der0th.match(str(dertype)):
        dertype = 0
    elif der1st.match(str(dertype)):
        dertype = 1
    elif der2nd.match(str(dertype)):
        raise ValidationError(
            'Requested derivative level \'dertype\' %s not valid for run_dftd3.'
            % (dertype))
    else:
        raise ValidationError(
            'Requested derivative level \'dertype\' %s not valid for run_dftd3.'
            % (dertype))

    if func is None:
        if dashparam is None:
            # defunct case
            raise ValidationError(
                """Parameters for -D correction missing. Provide a func or a dashparam kwarg."""
            )
        else:
            # case where all param read from dashparam dict (which must have all correct keys)
            func = 'custom'
            dashcoeff[dashlvl][func] = {}
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]
                else:
                    raise ValidationError(
                        """Parameter %s is missing from dashparam dict %s.""" %
                        (key, dashparam))
    else:
        func = func.lower()
        if func not in dashcoeff[dashlvl].keys():
            raise ValidationError(
                """Functional %s is not available for -D level %s.""" %
                (func, dashlvl))
        if dashparam is None:
            # (normal) case where all param taken from dashcoeff above
            pass
        else:
            # case where items in dashparam dict can override param taken from dashcoeff above
            dashparam = dict((k.lower(), v) for k, v in dashparam.iteritems())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]

    # Move ~/.dftd3par.<hostname> out of the way so it won't interfere
    defaultfile = os.path.expanduser(
        '~') + '/.dftd3par.' + socket.gethostname()
    defmoved = False
    if os.path.isfile(defaultfile):
        os.rename(defaultfile, defaultfile + '_hide')
        defmoved = True

    # Find environment by merging PSIPATH and PATH environment variables
    lenv = {
        'PATH': ':'.join([os.path.abspath(x) for x in os.environ.get('PSIPATH', '').split(':') if x != '']) + \
                ':' + os.environ.get('PATH'),
        'LD_LIBRARY_PATH': os.environ.get('LD_LIBRARY_PATH')
        }
    #   Filter out None values as subprocess will fault on them
    lenv = {k: v for k, v in lenv.items() if v is not None}

    # Find out if running from Psi4 for scratch details and such
    try:
        psi4.version()
    except NameError:
        isP4regime = False
    else:
        isP4regime = True

    # Setup unique scratch directory and move in
    current_directory = os.getcwd()
    if isP4regime:
        psioh = psi4.IOManager.shared_object()
        psio = psi4.IO.shared_object()
        os.chdir(psioh.get_default_path())
        dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \
            '.dftd3.' + str(random.randint(0, 99999))
    else:
        dftd3_tmpdir = os.environ['HOME'] + os.sep + 'dftd3_' + str(
            random.randint(0, 99999))
    if os.path.exists(dftd3_tmpdir) is False:
        os.mkdir(dftd3_tmpdir)
    os.chdir(dftd3_tmpdir)

    # Write dftd3_parameters file that governs dispersion calc
    paramcontents = dash_server(func, dashlvl, 'dftd3')
    paramfile1 = 'dftd3_parameters'  # older patched name
    with open(paramfile1, 'w') as handle:
        handle.write(paramcontents)
    paramfile2 = '.dftd3par.local'  # new mainline name
    with open(paramfile2, 'w') as handle:
        handle.write(paramcontents)

    # Write dftd3_geometry file that supplies geometry to dispersion calc
    numAtoms = self.natom()
    geom = self.save_string_xyz()
    reals = []
    for line in geom.splitlines():
        lline = line.split()
        if len(lline) != 4:
            continue
        if lline[0] == 'Gh':
            numAtoms -= 1
        else:
            reals.append(line)

    geomtext = str(numAtoms) + '\n\n'
    for line in reals:
        geomtext += line.strip() + '\n'
    geomfile = './dftd3_geometry.xyz'
    with open(geomfile, 'w') as handle:
        handle.write(geomtext)
    # TODO somehow the variations on save_string_xyz and
    #   whether natom and chgmult does or doesn't get written
    #   have gotten all tangled. I fear this doesn't work
    #   the same btwn libmints and qcdb or for ghosts

    # Call dftd3 program
    command = ['dftd3', geomfile]
    if dertype != 0:
        command.append('-grad')
    try:
        dashout = subprocess.Popen(command, stdout=subprocess.PIPE, env=lenv)
    except OSError as e:
        raise ValidationError('Program dftd3 not found in path. %s' % e)
    out, err = dashout.communicate()

    # Parse output (could go further and break into E6, E8, E10 and Cn coeff)
    success = False
    for line in out.splitlines():
        if re.match(' Edisp /kcal,au', line):
            sline = line.split()
            dashd = float(sline[3])
        if re.match(' normal termination of dftd3', line):
            success = True

    if not success:
        os.chdir(current_directory)
        raise Dftd3Error(
            """Unsuccessful run. Possibly -D variant not available in dftd3 version."""
        )

    # Parse grad output
    if dertype != 0:
        derivfile = './dftd3_gradient'
        dfile = open(derivfile, 'r')
        dashdderiv = []
        for line in geom.splitlines():
            lline = line.split()
            if len(lline) != 4:
                continue
            if lline[0] == 'Gh':
                dashdderiv.append([0.0, 0.0, 0.0])
            else:
                dashdderiv.append([
                    float(x.replace('D', 'E'))
                    for x in dfile.readline().split()
                ])
        dfile.close()

        if len(dashdderiv) != self.natom():
            raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \
                (len(dashdderiv), self.natom()))

    # Prepare results for Psi4
    if isP4regime and dertype != 0:
        psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd)
        psi_dashdderiv = psi4.Matrix(self.natom(), 3)
        psi_dashdderiv.set(dashdderiv)

    # Print program output to file if verbose
    if isP4regime:
        verbose = True if psi4.get_option('SCF', 'PRINT') >= 3 else False
    if verbose:

        text = '\n  ==> DFTD3 Output <==\n'
        text += out
        if dertype != 0:
            with open(derivfile, 'r') as handle:
                text += handle.read().replace('D', 'E')
            text += '\n'
        if isP4regime:
            psi4.print_out(text)
        else:
            print(text)

    # Clean up files and remove scratch directory
    os.unlink(paramfile1)
    os.unlink(paramfile2)
    os.unlink(geomfile)
    if dertype != 0:
        os.unlink(derivfile)
    if defmoved is True:
        os.rename(defaultfile + '_hide', defaultfile)

    os.chdir('..')
    try:
        shutil.rmtree(dftd3_tmpdir)
    except OSError as e:
        ValidationError('Unable to remove dftd3 temporary directory %s' % e)
    os.chdir(current_directory)

    # return -D & d(-D)/dx
    if dertype == -1:
        return dashd, dashdderiv
    elif dertype == 0:
        return dashd
    elif dertype == 1:
        return psi_dashdderiv
Example #13
0
def exampleFN(name, **kwargs):
    psi4.set_variable('CURRENT ENERGY', -74.94550962)
    # Your Python code goes here
    pass
Example #14
0
def run_gaussian_2(name, **kwargs):

    # throw an exception for open-shells
    if (psi4.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', 'SCF_TYPE'])

    # override default scf_type
    psi4.set_local_option('SCF', 'SCF_TYPE', 'PK')

    # optimize geometry at scf level
    psi4.clean()
    psi4.set_global_option('BASIS', "6-31G(D)")
    driver.optimize('scf')
    psi4.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 = psi4.get_variable('INTERNAL ENERGY CORRECTION')
    dh = psi4.get_variable('ENTHALPY CORRECTION')
    dg = psi4.get_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 / p4const.psi_hartree2wavenumbers * 0.8929 * 0.5
    psi4.clean()

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

    # qcisd(t)
    psi4.set_local_option('FNOCC', 'COMPUTE_MP4_TRIPLES', "TRUE")
    psi4.set_global_option('FREEZE_CORE', "TRUE")
    psi4.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 = psi4.get_variable("QCISD(T) TOTAL ENERGY")
    emp4_6311gd = psi4.get_variable("MP4 TOTAL ENERGY")
    emp2_6311gd = psi4.get_variable("MP2 TOTAL ENERGY")
    psi4.clean()

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

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

    # big basis mp2
    psi4.set_global_option('BASIS', "6-311+G(3DF_2P)")
    #run_fnocc('_mp2',**kwargs)
    driver.energy('mp2')
    emp2_big = psi4.get_variable("MP2 TOTAL ENERGY")
    psi4.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

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

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

    psi4.print_out('\n')
    T = psi4.get_global_option('T')
    psi4.print_out('  ==>  %3.0lf Kelvin Results  <==\n' % T)
    psi4.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

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

    psi4.set_variable("G2(MP2) INTERNAL ENERGY", internal_energy)
    psi4.set_variable("G2(MP2) ENTHALPY", enthalpy)
    psi4.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

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

    psi4.set_variable("CURRENT ENERGY", eg2_0k)

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

    psi4.clean()

    optstash.restore()

    # return 0K g2 results
    return eg2_0k
Example #15
0
def run_dftd3(self, func=None, dashlvl=None, dashparam=None, dertype=None):
    """Function to call Grimme's dftd3 program (http://toc.uni-muenster.de/DFTD3/)
    to compute the -D correction of level *dashlvl* using parameters for
    the functional *func*. The dictionary *dashparam* can be used to supply
    a full set of dispersion parameters in the absense of *func* or to supply
    individual overrides in the presence of *func*. Returns energy if *dertype* is 0,
    gradient if *dertype* is 1, else tuple of energy and gradient if *dertype*
    unspecified. The dftd3 executable must be independently compiled and found in
    :envvar:`PATH`.

    """
    # Validate arguments
    if self is None:
        self = psi4.get_active_molecule()

    dashlvl = dashlvl.lower()
    dashlvl = dash_alias['-' + dashlvl][1:] if ('-' + dashlvl) in dash_alias.keys() else dashlvl
    if dashlvl not in dashcoeff.keys():
        raise ValidationError("""-D correction level %s is not available. Choose among %s.""" % (dashlvl, dashcoeff.keys()))

    if dertype is None:
        dertype = -1
    elif der0th.match(str(dertype)):
        dertype = 0
    elif der1st.match(str(dertype)):
        dertype = 1
    elif der2nd.match(str(dertype)):
        raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype))
    else:
        raise ValidationError('Requested derivative level \'dertype\' %s not valid for run_dftd3.' % (dertype))

    if func is None:
        if dashparam is None:
            # defunct case
            raise ValidationError("""Parameters for -D correction missing. Provide a func or a dashparam kwarg.""")
        else:
            # case where all param read from dashparam dict (which must have all correct keys)
            func = 'custom'
            dashcoeff[dashlvl][func] = {}
            dashparam = dict((k.lower(), v) for k, v in dashparam.items())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]
                else:
                    raise ValidationError("""Parameter %s is missing from dashparam dict %s.""" % (key, dashparam))
    else:
        func = func.lower()
        if func not in dashcoeff[dashlvl].keys():
            raise ValidationError("""Functional %s is not available for -D level %s.""" % (func, dashlvl))
        if dashparam is None:
            # (normal) case where all param taken from dashcoeff above
            pass
        else:
            # case where items in dashparam dict can override param taken from dashcoeff above
            dashparam = dict((k.lower(), v) for k, v in dashparam.items())
            for key in dashcoeff[dashlvl]['b3lyp'].keys():
                if key in dashparam.keys():
                    dashcoeff[dashlvl][func][key] = dashparam[key]

    # Move ~/.dftd3par.<hostname> out of the way so it won't interfere
    defaultfile = os.path.expanduser('~') + '/.dftd3par.' + socket.gethostname()
    defmoved = False
    if os.path.isfile(defaultfile):
        os.rename(defaultfile, defaultfile + '_hide')
        defmoved = True

    # Setup unique scratch directory and move in
    current_directory = os.getcwd()
    psioh = psi4.IOManager.shared_object()
    psio = psi4.IO.shared_object()
    os.chdir(psioh.get_default_path())
    dftd3_tmpdir = 'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \
        '.dftd3.' + str(random.randint(0, 99999))
    if os.path.exists(dftd3_tmpdir) is False:
        os.mkdir(dftd3_tmpdir)
    os.chdir(dftd3_tmpdir)

    # Write dftd3_parameters file that governs dispersion calc
    paramfile = './dftd3_parameters'
    pfile = open(paramfile, 'w')
    pfile.write(dash_server(func, dashlvl, 'dftd3'))
    pfile.close()

    # Write dftd3_geometry file that supplies geometry to dispersion calc
    geomfile = './dftd3_geometry.xyz'
    gfile = open(geomfile, 'w')
    numAtoms = self.natom()
    geom = self.save_string_xyz()
    reals = []
    for line in geom.splitlines():
      if line.split()[0] == 'Gh':
        numAtoms -= 1
      else:
        reals.append(line)
        
    gfile.write(str(numAtoms)+'\n')
    for line in reals:
      gfile.write(line.strip()+'\n')
    gfile.close()

    # Call dftd3 program
    try:
        dashout = subprocess.Popen(['dftd3', geomfile, '-grad'], stdout=subprocess.PIPE)
    except OSError:
        raise ValidationError('Program dftd3 not found in path.')
    out, err = dashout.communicate()

    # Parse output (could go further and break into E6, E8, E10 and Cn coeff)
    success = False
    for line in out.splitlines():
        # communicate in python 3 returns a byte array rather than a string.
        # Convert to string--only need a simple encoding for comparison.
        line = line.decode('utf-8')        
        if re.match(' Edisp /kcal,au', line):
            sline = line.split()
            dashd = float(sline[3])
        if re.match(' normal termination of dftd3', line):
            success = True

    if not success:
        raise ValidationError('Program dftd3 did not complete successfully.')

    # Parse grad output
    derivfile = './dftd3_gradient'
    dfile = open(derivfile, 'r')
    dashdderiv = []
    i = 0
    for line in geom.splitlines():
      if i == 0:
        i += 1
      else:
        if line.split()[0] == 'Gh':
          dashdderiv.append([0.0, 0.0, 0.0])
        else:
          temp = dfile.readline()
          dashdderiv.append([float(x.replace('D', 'E')) for x in temp.split()])
    dfile.close()

    if len(dashdderiv) != self.natom():
        raise ValidationError('Program dftd3 gradient file has %d atoms- %d expected.' % \
            (len(dashdderiv), self.natom()))
    psi_dashdderiv = psi4.Matrix(self.natom(), 3)
    psi_dashdderiv.set(dashdderiv)

    # Print program output to file if verbose
    verbose = psi4.get_option('SCF', 'PRINT')
    if verbose >= 3:
        psi4.print_out('\n  ==> DFTD3 Output <==\n')
        psi4.print_out(out)
        dfile = open(derivfile, 'r')
        psi4.print_out(dfile.read().replace('D', 'E'))
        dfile.close()
        psi4.print_out('\n')

    # Clean up files and remove scratch directory
    os.unlink(paramfile)
    os.unlink(geomfile)
    os.unlink(derivfile)
    if defmoved is True:
        os.rename(defaultfile + '_hide', defaultfile)

    os.chdir('..')
    try:
        shutil.rmtree(dftd3_tmpdir)
    except OSError as e:
        ValidationError('Unable to remove dftd3 temporary directory %s' % e, file=sys.stderr)
    os.chdir(current_directory)

    # return -D & d(-D)/dx
    psi4.set_variable('DISPERSION CORRECTION ENERGY', dashd)
    if dertype == -1:
        return dashd, dashdderiv
    elif dertype == 0:
        return dashd
    elif dertype == 1:
        return psi_dashdderiv
Example #16
0
def _nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.

    Parameters
    ----------
    func : python function
        Python function that accepts method_string and a molecule and returns a energy, gradient, or Hessian.
    method_string : str
        Lowername to be passed to function
    molecule : psi4.Molecule (default: Global Molecule)
        Molecule to use in all computations
    return_wfn : bool (default: False)
        Return a wavefunction or not
    bsse_type : str or list (default: None, this function is not called)
        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this list is returned by this function.
    max_nbody : int
        Maximum n-body to compute, cannot exceede the number of fragments in the moleucle
    ptype : str
        Type of the procedure passed in
    return_total_data : bool (default: False)
        If True returns the total data (energy/gradient/etc) of the system otherwise returns interaction data

    Returns
    -------
    data : return type of func
        The interaction data
    wfn : psi4.Wavefunction (optional)
        A wavefunction with energy/gradient/hessian set appropriotely. This wavefunction also contains 

    Notes
    -----
    This is a generalized univeral function for compute interaction quantities.

    Examples
    --------
    """

    ### ==> Parse some kwargs <==
    kwargs = p4util.kwargs_lower(kwargs)
    return_wfn = kwargs.pop('return_wfn', False)
    ptype = kwargs.pop('ptype', None)
    return_total_data = kwargs.pop('return_total_data', False)
    molecule = kwargs.pop('molecule', psi4.get_active_molecule())
    molecule.update_geometry()
    psi4.clean_variables()

    if ptype not in ['energy', 'gradient', 'hessian']:
        raise ValidationError(
            """N-Body driver: The ptype '%s' is not regonized.""" % ptype)

    # Figure out BSSE types
    do_cp = False
    do_nocp = False
    do_vmfc = False
    return_method = False

    # Must be passed bsse_type
    bsse_type_list = kwargs.pop('bsse_type')
    if bsse_type_list is None:
        raise ValidationError("N-Body GUFunc: Must pass a bsse_type")
    if not isinstance(bsse_type_list, list):
        bsse_type_list = [bsse_type_list]

    for num, btype in enumerate(bsse_type_list):
        if btype.lower() == 'cp':
            do_cp = True
            if (num == 0): return_method = 'cp'
        elif btype.lower() == 'nocp':
            do_nocp = True
            if (num == 0): return_method = 'nocp'
        elif btype.lower() == 'vmfc':
            do_vmfc = True
            if (num == 0): return_method = 'vmfc'
        else:
            raise ValidationError(
                "N-Body GUFunc: bsse_type '%s' is not recognized" %
                btype.lower())

    max_nbody = kwargs.get('max_nbody', -1)
    max_frag = molecule.nfragments()
    if max_nbody == -1:
        max_nbody = molecule.nfragments()
    else:
        max_nbody = min(max_nbody, max_frag)

    # What levels do we need?
    nbody_range = range(1, max_nbody + 1)
    fragment_range = range(1, max_frag + 1)

    # Flip this off for now, needs more testing
    # If we are doing CP lets save them integrals
    #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1):
    #    # Set to save RI integrals for repeated full-basis computations
    #    ri_ints_io = psi4.get_global_option('DF_INTS_IO')

    #    # inquire if above at all applies to dfmp2 or just scf
    #    psi4.set_global_option('DF_INTS_IO', 'SAVE')
    #    psioh = psi4.IOManager.shared_object()
    #    psioh.set_specific_retention(97, True)

    bsse_str = bsse_type_list[0]
    if len(bsse_type_list) > 1:
        bsse_str = str(bsse_type_list)
    psi4.print_out("\n\n")
    psi4.print_out("   ===> N-Body Interaction Abacus <===\n")
    psi4.print_out("        BSSE Treatment:                     %s\n" %
                   bsse_str)

    cp_compute_list = {x: set() for x in nbody_range}
    nocp_compute_list = {x: set() for x in nbody_range}
    vmfc_compute_list = {x: set() for x in nbody_range}
    vmfc_level_list = {x: set()
                       for x in nbody_range
                       }  # Need to sum something slightly different

    # Build up compute sets
    if do_cp:
        # Everything is in dimer basis
        basis_tuple = tuple(fragment_range)
        for nbody in nbody_range:
            for x in it.combinations(fragment_range, nbody):
                cp_compute_list[nbody].add((x, basis_tuple))

    if do_nocp:
        # Everything in monomer basis
        for nbody in nbody_range:
            for x in it.combinations(fragment_range, nbody):
                nocp_compute_list[nbody].add((x, x))

    if do_vmfc:
        # Like a CP for all combinations of pairs or greater
        for nbody in nbody_range:
            for cp_combos in it.combinations(fragment_range, nbody):
                basis_tuple = tuple(cp_combos)
                for interior_nbody in nbody_range:
                    for x in it.combinations(cp_combos, interior_nbody):
                        combo_tuple = (x, basis_tuple)
                        vmfc_compute_list[interior_nbody].add(combo_tuple)
                        vmfc_level_list[len(basis_tuple)].add(combo_tuple)

    # Build a comprehensive compute_range
    compute_list = {x: set() for x in nbody_range}
    for n in nbody_range:
        compute_list[n] |= cp_compute_list[n]
        compute_list[n] |= nocp_compute_list[n]
        compute_list[n] |= vmfc_compute_list[n]
        psi4.print_out("        Number of %d-body computations:     %d\n" %
                       (n, len(compute_list[n])))

    # Build size and slices dictionaries
    fragment_size_dict = {
        frag: molecule.extract_subsets(frag).natom()
        for frag in range(1, max_frag + 1)
    }

    start = 0
    fragment_slice_dict = {}
    for k, v in fragment_size_dict.items():
        fragment_slice_dict[k] = slice(start, start + v)
        start += v

    molecule_total_atoms = sum(fragment_size_dict.values())

    # Now compute the energies
    energies_dict = {}
    ptype_dict = {}
    for n in compute_list.keys():
        psi4.print_out(
            "\n   ==> N-Body: Now computing %d-body complexes <==\n\n" % n)
        print("\n   ==> N-Body: Now computing %d-body complexes <==\n" % n)
        total = len(compute_list[n])
        for num, pair in enumerate(compute_list[n]):
            psi4.print_out(
                "\n       N-Body: Computing complex (%d/%d) with fragments %s in the basis of fragments %s.\n\n"
                % (num + 1, total, str(pair[0]), str(pair[1])))
            ghost = list(set(pair[1]) - set(pair[0]))

            current_mol = molecule.extract_subsets(list(pair[0]), ghost)
            ptype_dict[pair] = func(method_string,
                                    molecule=current_mol,
                                    **kwargs)
            energies_dict[pair] = psi4.get_variable("CURRENT ENERGY")
            psi4.print_out(
                "\n       N-Body: Complex Energy (fragments = %s, basis = %s: %20.14f)\n"
                % (str(pair[0]), str(pair[1]), energies_dict[pair]))

            # Flip this off for now, needs more testing
            #if 'cp' in bsse_type_list and (len(bsse_type_list) == 1):
            #    psi4.set_global_option('DF_INTS_IO', 'LOAD')

            psi4.clean()

    # Final dictionaries
    cp_energy_by_level = {n: 0.0 for n in nbody_range}
    nocp_energy_by_level = {n: 0.0 for n in nbody_range}

    cp_energy_body_dict = {n: 0.0 for n in nbody_range}
    nocp_energy_body_dict = {n: 0.0 for n in nbody_range}
    vmfc_energy_body_dict = {n: 0.0 for n in nbody_range}

    # Build out ptype dictionaries if needed
    if ptype != 'energy':
        if ptype == 'gradient':
            arr_shape = (molecule_total_atoms, 3)
        elif ptype == 'hessian':
            arr_shape = (molecule_total_atoms * 3, molecule_total_atoms * 3)
        else:
            raise KeyError("N-Body: ptype '%s' not recognized" % ptype)

        cp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range}
        nocp_ptype_by_level = {n: np.zeros(arr_shape) for n in nbody_range}

        cp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range}
        nocp_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range}
        vmfc_ptype_body_dict = {n: np.zeros(arr_shape) for n in nbody_range}
    else:
        cp_ptype_by_level, cp_ptype_body_dict = None, None
        nocp_ptype_by_level, nocp_ptype_body_dict = None, None
        vmfc_ptype_body_dict = None

    # Sum up all of the levels
    for n in nbody_range:

        # Energy
        cp_energy_by_level[n] = sum(energies_dict[v]
                                    for v in cp_compute_list[n])
        nocp_energy_by_level[n] = sum(energies_dict[v]
                                      for v in nocp_compute_list[n])

        # Special vmfc case
        if n > 1:
            vmfc_energy_body_dict[n] = vmfc_energy_body_dict[n - 1]
        for tup in vmfc_level_list[n]:
            vmfc_energy_body_dict[n] += (
                (-1)**(n - len(tup[0]))) * energies_dict[tup]

        # Do ptype
        if ptype != 'energy':
            _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n],
                                    fragment_slice_dict, fragment_size_dict,
                                    cp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n],
                                    fragment_slice_dict, fragment_size_dict,
                                    nocp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype,
                                    ptype_dict,
                                    vmfc_level_list[n],
                                    fragment_slice_dict,
                                    fragment_size_dict,
                                    vmfc_ptype_by_level[n],
                                    vmfc=True)
    # Compute cp energy and ptype
    if do_cp:
        for n in nbody_range:
            if n == max_frag:
                cp_energy_body_dict[n] = cp_energy_by_level[n]
                if ptype != 'energy':
                    cp_ptype_body_dict[n][:] = cp_ptype_by_level[n]
                continue

            for k in range(1, n + 1):
                take_nk = nCr(max_frag - k - 1, n - k)
                sign = ((-1)**(n - k))
                value = cp_energy_by_level[k]
                cp_energy_body_dict[n] += take_nk * sign * value

                if ptype != 'energy':
                    value = cp_ptype_by_level[k]
                    cp_ptype_body_dict[n] += take_nk * sign * value

        _print_nbody_energy(cp_energy_body_dict, "Counterpoise Corrected (CP)")
        cp_interaction_energy = cp_energy_body_dict[
            max_nbody] - cp_energy_body_dict[1]
        psi4.set_variable('Counterpoise Corrected Total Energy',
                          cp_energy_body_dict[max_nbody])
        psi4.set_variable('Counterpoise Corrected Interaction Energy',
                          cp_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'CP-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(var_key,
                              cp_energy_body_dict[n] - cp_energy_body_dict[1])

    # Compute nocp energy and ptype
    if do_nocp:
        for n in nbody_range:
            if n == max_frag:
                nocp_energy_body_dict[n] = nocp_energy_by_level[n]
                if ptype != 'energy':
                    nocp_ptype_body_dict[n][:] = nocp_ptype_by_level[n]
                continue

            for k in range(1, n + 1):
                take_nk = nCr(max_frag - k - 1, n - k)
                sign = ((-1)**(n - k))
                value = nocp_energy_by_level[k]
                nocp_energy_body_dict[n] += take_nk * sign * value

                if ptype != 'energy':
                    value = nocp_ptype_by_level[k]
                    nocp_ptype_body_dict[n] += take_nk * sign * value

        _print_nbody_energy(nocp_energy_body_dict,
                            "Non-Counterpoise Corrected (NoCP)")
        nocp_interaction_energy = nocp_energy_body_dict[
            max_nbody] - nocp_energy_body_dict[1]
        psi4.set_variable('Non-Counterpoise Corrected Total Energy',
                          nocp_energy_body_dict[max_nbody])
        psi4.set_variable('Non-Counterpoise Corrected Interaction Energy',
                          nocp_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'NOCP-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(
                var_key, nocp_energy_body_dict[n] - nocp_energy_body_dict[1])

    # Compute vmfc energy and ptype
    if do_vmfc:
        _print_nbody_energy(vmfc_energy_body_dict,
                            "Valiron-Mayer Function Couterpoise (VMFC)")
        vmfc_interaction_energy = vmfc_energy_body_dict[
            max_nbody] - vmfc_energy_body_dict[1]
        psi4.set_variable('Valiron-Mayer Function Couterpoise Total Energy',
                          vmfc_energy_body_dict[max_nbody])
        psi4.set_variable(
            'Valiron-Mayer Function Couterpoise Interaction Energy',
            vmfc_interaction_energy)

        for n in nbody_range[1:]:
            var_key = 'VMFC-CORRECTED %d-BODY INTERACTION ENERGY' % n
            psi4.set_variable(
                var_key, vmfc_energy_body_dict[n] - vmfc_energy_body_dict[1])

    if return_method == 'cp':
        ptype_body_dict = cp_ptype_body_dict
        energy_body_dict = cp_energy_body_dict
    elif return_method == 'nocp':
        ptype_body_dict = nocp_ptype_body_dict
        energy_body_dict = nocp_energy_body_dict
    elif return_method == 'vmfc':
        ptype_body_dict = vmfc_ptype_body_dict
        energy_body_dict = vmfc_energy_body_dict
    else:
        raise ValidationError(
            "N-Body Wrapper: Invalid return type. Should never be here, please post this error on github."
        )

    # Figure out and build return types
    if return_total_data:
        ret_energy = energy_body_dict[max_nbody]
    else:
        ret_energy = energy_body_dict[max_nbody]
        ret_energy -= energy_body_dict[1]

    if ptype != 'energy':
        if return_total_data:
            np_final_ptype = ptype_body_dict[max_nbody].copy()
        else:
            np_final_ptype = ptype_body_dict[max_nbody].copy()
            np_final_ptype -= ptype_body_dict[1]

            ret_ptype = psi4.Matrix(*np_cp_final_ptype.shape)
            ret_ptype_view = np.asarray(final_ptype)
            ret_ptype_view[:] = np_final_ptype
    else:
        ret_ptype = ret_energy

    # Build and set a wavefunction
    wfn = psi4.new_wavefunction(molecule, 'sto-3g')
    wfn.nbody_energy = energies_dict
    wfn.nbody_ptype = ptype_dict
    wfn.nbody_body_energy = energy_body_dict
    wfn.nbody_body_ptype = ptype_body_dict

    if ptype == 'gradient':
        wfn.set_gradient(ret_ptype)
    elif ptype == 'hessian':
        wfn.set_hessian(ret_ptype)

    psi4.set_variable("CURRENT ENERGY", ret_energy)

    if return_wfn:
        return (ret_ptype, wfn)
    else:
        return ret_ptype