Exemplo n.º 1
0
def format_currentstate_for_input(func, name, allButMol=False, **kwargs):
    """Function to return an input file in preprocessed psithon.
    Captures memory, molecule, options, function, method, and kwargs.
    Used to write distributed (sow/reap) input files.

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

    commands = """\n# This is a psi4 input file auto-generated from the %s() wrapper.\n\n""" % (
        inspect.stack()[1][3])
    commands += """memory %d mb\n\n""" % (int(0.000001 * core.get_memory()))
    if not allButMol:
        molecule = core.get_active_molecule()
        molecule.update_geometry()
        commands += format_molecule_for_input(molecule)
        commands += '\n'
    commands += prepare_options_for_modules(changedOnly=True,
                                            commandsInsteadDict=True)
    commands += """\n%s('%s', """ % (func.__name__, name.lower())
    for key in kwargs.keys():
        commands += """%s=%r, """ % (key, kwargs[key])
    commands += ')\n\n'

    return commands
Exemplo n.º 2
0
def format_currentstate_for_input(func, name, allButMol=False, **kwargs):
    """Function to return an input file in preprocessed psithon.
    Captures memory, molecule, options, function, method, and kwargs.
    Used to write distributed (sow/reap) input files.

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

    commands = """\n# This is a psi4 input file auto-generated from the %s() wrapper.\n\n""" % (inspect.stack()[1][3])
    commands += """memory %d mb\n\n""" % (int(0.000001 * core.get_memory()))
    if not allButMol:
        molecule = core.get_active_molecule()
        molecule.update_geometry()
        commands += format_molecule_for_input(molecule)
        commands += '\n'
    commands += prepare_options_for_modules(changedOnly=True, commandsInsteadDict=True)
    commands += """\n%s('%s', """ % (func.__name__, name.lower())
    for key in kwargs.keys():
        commands += """%s=%r, """ % (key, kwargs[key])
    commands += ')\n\n'

    return commands
Exemplo n.º 3
0
def state_to_atomicinput(*, driver, method, basis=None, molecule=None, function_kwargs=None) -> "AtomicInput":
    """Form a QCSchema for job input from the current state of Psi4 settings."""

    if molecule is None:
        molecule = core.get_active_molecule()

    keywords = {k.lower(): v for k, v in prepare_options_for_set_options().items()}
    if function_kwargs is not None:
        keywords["function_kwargs"] = function_kwargs

    kw_basis = keywords.pop("basis", None)
    basis = basis or kw_basis

    resi = qcel.models.AtomicInput(
         **{
            "driver": driver,
            "extras": {
                "wfn_qcvars_only": True,
            },
            "model": {
                "method": method,
                "basis": basis,
            },
            "keywords": keywords,
            "molecule": molecule.to_schema(dtype=2),
            "provenance": provenance_stamp(__name__),
         })

    return resi
Exemplo n.º 4
0
def initialize_database(database,
                        name,
                        prop,
                        properties_array,
                        additional_kwargs=None):
    """
        Initialize the database for computation of some property
        using distributed finite differences driver

    database: (database) the database object passed from the caller
    name:  (string) name as passed to calling driver
    prop: (string) the property being computed, used to add xxx_computed flag
        to database
    prop_array: (list of strings) properties to go in
        properties kwarg of the property() cmd in each sub-dir
    additional_kwargs: (list of strings) *optional*
        any additional kwargs that should go in the call to the
        property() driver method in each subdir


    Returns: nothing
    Throws: nothing
    """
    database['inputs_generated'] = False
    database['jobs_complete'] = False
    prop_cmd = "property('{0}',".format(name)
    prop_cmd += "properties=[ '{}' ".format(properties_array[0])
    if len(properties_array) > 1:
        for element in properties_array[1:]:
            prop_cmd += ",'{}'".format(element)
    prop_cmd += "]"
    if additional_kwargs is not None:
        for arg in additional_kwargs:
            prop_cmd += ", {}".format(arg)
    prop_cmd += ")"
    database['prop_cmd'] = prop_cmd
    database['job_status'] = collections.OrderedDict()
    # Populate the job_status dict
    molecule = core.get_active_molecule()
    natom = molecule.natom()
    coordinates = ['x', 'y', 'z']
    #step_direction = ['p', 'm'] changing due to change in findif atomic_displacements
    step_direction = ['m', 'p']

    for atom in range(1, natom + 1):
        for coord in coordinates:
            for step in step_direction:
                job_name = '{}_{}_{}'.format(atom, coord, step)
                database['job_status'].update({job_name: 'not_started'})
    database['{}_computed'.format(prop)] = False
Exemplo n.º 5
0
def generate_inputs(db,name):
    """
        Generates the input files in each sub-directory of the
        distributed finite differences property calculation.

    name: ( string ) method name passed to calling driver,
    db:   (database) The database object associated with this property
          calculation. On exit this db['inputs_generated'] has been set True

    Returns: nothing
    Throws: Exception if the number of atomic displacements is not correct.
    """
    molecule = core.get_active_molecule()
    natom = molecule.natom()

    # get list of displacements
    displacement_geoms = core.atomic_displacements(molecule)

    # Sanity Check
    # there should be 3 cords * natoms *2 directions (+/-)
    if not (6 * natom) == len(displacement_geoms):
        raise Exception('The number of atomic displacements should be 6 times'
                        ' the number of atoms!')

    displacement_names = db['job_status'].keys()

    for n, entry in enumerate(displacement_names):
        if not os.path.exists(entry):
            os.makedirs(entry)

        # Setup up input file string
        inp_template = 'molecule {molname}_{disp}'
        inp_template += ' {{\n{molecule_info}\n}}\n{options}\n{jobspec}\n'
        molecule.set_geometry(displacement_geoms[n])
        molecule.fix_orientation(True)
        molecule.fix_com(True)
        inputfile = open('{0}/input.dat'.format(entry), 'w')
        inputfile.write("# This is a psi4 input file auto-generated for"
            "computing properties by finite differences.\n\n")
        inputfile.write(
            inp_template.format(
                molname=molecule.name(),
                disp=entry,
                molecule_info=molecule.create_psi4_string_from_molecule(),
                options=p4util.format_options_for_input(),
                jobspec=db['prop_cmd']))
        inputfile.close()
    db['inputs_generated'] = True
Exemplo n.º 6
0
def generate_inputs(db, name):
    """
        Generates the input files in each sub-directory of the
        distributed finite differences property calculation.

    name: ( string ) method name passed to calling driver,
    db:   (database) The database object associated with this property
          calculation. On exit this db['inputs_generated'] has been set True

    Returns: nothing
    Throws: Exception if the number of atomic displacements is not correct.
    """
    molecule = core.get_active_molecule()
    natom = molecule.natom()

    # get list of displacements
    displacement_geoms = core.atomic_displacements(molecule)

    # Sanity Check
    # there should be 3 cords * natoms *2 directions (+/-)
    if not (6 * natom) == len(displacement_geoms):
        raise Exception('The number of atomic displacements should be 6 times'
                        ' the number of atoms!')

    displacement_names = db['job_status'].keys()

    for n, entry in enumerate(displacement_names):
        if not os.path.exists(entry):
            os.makedirs(entry)

        # Setup up input file string
        inp_template = 'molecule {molname}_{disp}'
        inp_template += ' {{\n{molecule_info}\n}}\n{options}\n{jobspec}\n'
        molecule.set_geometry(displacement_geoms[n])
        molecule.fix_orientation(True)
        molecule.fix_com(True)
        inputfile = open('{0}/input.dat'.format(entry), 'w')
        inputfile.write("# This is a psi4 input file auto-generated for"
                        "computing properties by finite differences.\n\n")
        inputfile.write(
            inp_template.format(
                molname=molecule.name(),
                disp=entry,
                molecule_info=molecule.create_psi4_string_from_molecule(),
                options=p4util.format_options_for_input(),
                jobspec=db['prop_cmd']))
        inputfile.close()
    db['inputs_generated'] = True
Exemplo n.º 7
0
def initialize_database(database, name, prop, properties_array, additional_kwargs=None):
    """
        Initialize the database for computation of some property
        using distributed finite differences driver

    database: (database) the database object passed from the caller
    name:  (string) name as passed to calling driver
    prop: (string) the property being computed, used to add xxx_computed flag
        to database
    prop_array: (list of strings) properties to go in
        properties kwarg of the property() cmd in each sub-dir
    additional_kwargs: (list of strings) *optional*
        any additional kwargs that should go in the call to the
        property() driver method in each subdir


    Returns: nothing
    Throws: nothing
    """
    database['inputs_generated'] = False
    database['jobs_complete'] = False
    prop_cmd ="property('{0}',".format(name)
    prop_cmd += "properties=[ '{}' ".format(properties_array[0])
    if len(properties_array) > 1:
        for element in properties_array[1:]:
            prop_cmd += ",'{}'".format(element)
    prop_cmd += "]"
    if additional_kwargs is not None:
        for arg in additional_kwargs:
            prop_cmd += ", {}".format(arg)
    prop_cmd += ")"
    database['prop_cmd'] = prop_cmd
    database['job_status'] = collections.OrderedDict()
    # Populate the job_status dict
    molecule = core.get_active_molecule()
    natom = molecule.natom()
    coordinates = ['x', 'y', 'z']
    #step_direction = ['p', 'm'] changing due to change in findif atomic_displacements
    step_direction = ['m', 'p']

    for atom in range(1, natom + 1):
        for coord in coordinates:
            for step in step_direction:
                job_name = '{}_{}_{}'.format(atom, coord, step)
                database['job_status'].update({job_name: 'not_started'})
    database['{}_computed'.format(prop)] = False
Exemplo n.º 8
0
def auto_fragments(
    molecule: Optional[core.Molecule] = None,
    seed_atoms: Optional[List[List[int]]] = None,
) -> core.Molecule:
    r"""Detects fragments in unfragmented molecule using BFS algorithm.
    Currently only used for the WebMO implementation of SAPT.

    Parameters
    ----------
    molecule : :ref:`molecule <op_py_molecule>`, optional
        The target molecule, if not the last molecule defined.
    seed_atoms
        List of lists of atoms (0-indexed) belonging to independent fragments.
        Useful to prompt algorithm or to define intramolecular fragments through
        border atoms. Example: `[[1, 0], [2]]`

    Returns
    -------
    :py:class:`~psi4.core.Molecule` |w--w| fragmented molecule in
    Cartesian, fixed-geom (no variable values), no dummy-atom format.

    Examples
    --------
    >>> # [1] prepare unfragmented (and non-adjacent-atom) HHFF into (HF)_2 molecule ready for SAPT
    >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n}
    >>> print mol.nfragments()  # 1
    >>> fragmol = auto_fragments()
    >>> print fragmol.nfragments()  # 2

    """
    # Make sure the molecule the user provided is the active one
    if molecule is None:
        molecule = core.get_active_molecule()
    molecule.update_geometry()
    molname = molecule.name()

    frag, bmol = molecule.BFS(seed_atoms=seed_atoms, return_molecule=True)

    bmol.set_name(molname)
    bmol.print_cluster()
    core.print_out("""  Exiting auto_fragments\n""")

    return bmol
Exemplo n.º 9
0
def auto_fragments(**kwargs):
    r"""Detects fragments in unfragmented molecule using BFS algorithm.
    Currently only used for the WebMO implementation of SAPT.

    Parameters
    ----------
    molecule : :ref:`molecule <op_py_molecule>`, optional
        The target molecule, if not the last molecule defined.
    seed_atoms : list, optional
        List of lists of atoms (0-indexed) belonging to independent fragments.
        Useful to prompt algorithm or to define intramolecular fragments through
        border atoms. Example: `[[1, 0], [2]]`

    Returns
    -------
    :py:class:`~psi4.core.Molecule` |w--w| fragmented molecule in
    Cartesian, fixed-geom (no variable values), no dummy-atom format.

    Examples
    --------
    >>> # [1] prepare unfragmented (and non-adjacent-atom) HHFF into (HF)_2 molecule ready for SAPT
    >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n}
    >>> print mol.nfragments()  # 1
    >>> fragmol = auto_fragments()
    >>> print fragmol.nfragments()  # 2

    """
    # Make sure the molecule the user provided is the active one
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    seeds = kwargs.pop('seed_atoms', None)
    molecule.update_geometry()
    molname = molecule.name()

    frag, bmol = molecule.BFS(seed_atoms=seeds, return_molecule=True)

    bmol.set_name(molname)
    bmol.print_cluster()
    core.print_out("""  Exiting auto_fragments\n""")

    return bmol
Exemplo n.º 10
0
def auto_fragments(**kwargs):
    r"""Detects fragments if the user does not supply them.
    Currently only used for the WebMO implementation of SAPT.

    :returns: :py:class:`~psi4.core.Molecule`) |w--w| fragmented molecule.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :examples:

    >>> # [1] replicates with cbs() the simple model chemistry scf/cc-pVDZ: set basis cc-pVDZ energy('scf')
    >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n}
    >>> print mol.nfragments()  # 1
    >>> fragmol = auto_fragments()
    >>> print fragmol.nfragments()  # 2

    """
    # Make sure the molecule the user provided is the active one
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    molecule.update_geometry()
    molname = molecule.name()

    geom = molecule.save_string_xyz()

    numatoms = molecule.natom()
    VdW = [1.2, 1.7, 1.5, 1.55, 1.52, 1.9, 1.85, 1.8]

    symbol = list(range(numatoms))
    X = [0.0] * numatoms
    Y = [0.0] * numatoms
    Z = [0.0] * numatoms

    Queue = []
    White = []
    Black = []
    F = geom.split('\n')
    for f in range(numatoms):
        A = F[f + 1].split()
        symbol[f] = A[0]
        X[f] = float(A[1])
        Y[f] = float(A[2])
        Z[f] = float(A[3])
        White.append(f)
    Fragment = [[] for i in range(numatoms)]  # stores fragments

    start = 0  # starts with the first atom in the list
    Queue.append(start)
    White.remove(start)

    frag = 0

    while((len(White) > 0) or (len(Queue) > 0)):  # Iterates to the next fragment
        while(len(Queue) > 0):  # BFS within a fragment
            for u in Queue:  # find all nearest Neighbors
                             #   (still coloured white) to vertex u
                for i in White:
                    Distance = math.sqrt((X[i] - X[u]) * (X[i] - X[u]) +
                                         (Y[i] - Y[u]) * (Y[i] - Y[u]) +
                                         (Z[i] - Z[u]) * (Z[i] - Z[u]))
                    if Distance < _autofragment_convert(u, symbol) + _autofragment_convert(i, symbol):
                        Queue.append(i)  # if you find you, put it in the que
                        White.remove(i)  # and remove it from the untouched list
            Queue.remove(u)  # remove focus from Queue
            Black.append(u)
            Fragment[frag].append(int(u + 1))  # add to group (adding 1 to start
                                           #   list at one instead of zero)

        if(len(White) != 0):  # cant move White->Queue if no more exist
            Queue.append(White[0])
            White.remove(White[0])
        frag += 1

    new_geom = """\n"""
    for i in Fragment[0]:
        new_geom = new_geom + F[i].lstrip() + """\n"""
    new_geom = new_geom + """--\n"""
    for j in Fragment[1]:
        new_geom = new_geom + F[j].lstrip() + """\n"""
    new_geom = new_geom + """units angstrom\n"""

    moleculenew = core.Molecule.create_molecule_from_string(new_geom)
    moleculenew.set_name(molname)
    moleculenew.update_geometry()
    moleculenew.print_cluster()
    core.print_out("""  Exiting auto_fragments\n""")

    return moleculenew
Exemplo n.º 11
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
Exemplo n.º 12
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
Exemplo n.º 13
0
Arquivo: frac.py Projeto: yxie326/psi4
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
Exemplo n.º 14
0
def run_sapt_dft(name, **kwargs):
    optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'], ['SCF', 'REFERENCE'],
                                   ['SCF', 'DFT_FUNCTIONAL'],
                                   ['SCF', 'DFT_GRAC_SHIFT'],
                                   ['SCF', 'SAVE_JK'])

    core.tstart()
    # Alter default algorithm
    if not core.has_option_changed('SCF', 'SCF_TYPE'):
        core.set_local_option('SCF', 'SCF_TYPE', 'DF')

    core.prepare_options_for_module("SAPT")

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

    # Shifting to C1 so we need to copy the active molecule
    if sapt_dimer.schoenflies_symbol() != 'c1':
        core.print_out(
            '  SAPT does not make use of molecular symmetry, further calculations in C1 point group.\n'
        )

    # Make sure the geometry doesnt shift or rotate
    sapt_dimer = sapt_dimer.clone()
    sapt_dimer.reset_point_group('c1')
    sapt_dimer.fix_orientation(True)
    sapt_dimer.fix_com(True)
    sapt_dimer.update_geometry()

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

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

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

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

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

    nfrag = sapt_dimer.nfragments()
    if nfrag != 2:
        raise ValidationError(
            'SAPT requires active molecule to have 2 fragments, not %s.' %
            (nfrag))

    monomerA = sapt_dimer.extract_subsets(1, 2)
    monomerA.set_name('monomerA')
    monomerB = sapt_dimer.extract_subsets(2, 1)
    monomerB.set_name('monomerB')

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

    core.set_global_option("SAVE_JK", True)
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        # core.set_global_option('DF_INTS_IO', 'LOAD')
        core.set_global_option('DF_INTS_IO', 'SAVE')

    # # Compute dimer wavefunction
    hf_cache = {}
    hf_wfn_dimer = None
    if do_delta_hf:
        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.set_global_option('DF_INTS_IO', 'SAVE')

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

        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'dimer', 'monomerA')
        hf_wfn_A = scf_helper("SCF",
                              molecule=monomerA,
                              banner="SAPT(DFT): delta HF Monomer A",
                              **kwargs)
        hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY")

        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerA', 'monomerB')
        hf_wfn_B = scf_helper("SCF",
                              molecule=monomerB,
                              banner="SAPT(DFT): delta HF Monomer B",
                              **kwargs)
        hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY")

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

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

        # Build cache and JK
        sapt_jk = hf_wfn_B.jk()

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

        # Electostatics
        elst = sapt_jk_terms.electrostatics(hf_cache, True)
        hf_data.update(elst)

        # Exchange
        exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True)
        hf_data.update(exch)

        # Induction
        ind = sapt_jk_terms.induction(
            hf_cache,
            sapt_jk,
            True,
            maxiter=core.get_option("SAPT", "MAXITER"),
            conv=core.get_option("SAPT", "D_CONVERGENCE"))
        hf_data.update(ind)

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

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

        data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF")

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

    # Set the primary functional
    core.set_global_option("DFT_FUNCTIONAL",
                           core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL"))
    core.set_local_option('SCF', 'REFERENCE', 'RKS')

    # Compute Monomer A wavefunction
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'dimer', 'monomerA')

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

    # Save the JK object
    core.IO.set_default_namespace('monomerA')
    wfn_A = scf_helper("SCF",
                       molecule=monomerA,
                       banner="SAPT(DFT): DFT Monomer A",
                       **kwargs)
    data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)

    # Compute Monomer B wavefunction
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

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

    core.IO.set_default_namespace('monomerB')
    wfn_B = scf_helper("SCF",
                       molecule=monomerB,
                       banner="SAPT(DFT): DFT Monomer B",
                       **kwargs)
    data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)

    # Print out the title and some information
    core.print_out("\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("         " +
                   "SAPT(DFT): Intermolecular Interaction Segment".center(58) +
                   "\n")
    core.print_out("\n")
    core.print_out("         " +
                   "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("\n")

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

    # Build cache and JK
    sapt_jk = wfn_B.jk()

    cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True)

    # Electostatics
    elst = sapt_jk_terms.electrostatics(cache, True)
    data.update(elst)

    # Exchange
    exch = sapt_jk_terms.exchange(cache, sapt_jk, True)
    data.update(exch)

    # Induction
    ind = sapt_jk_terms.induction(cache,
                                  sapt_jk,
                                  True,
                                  maxiter=core.get_option("SAPT", "MAXITER"),
                                  conv=core.get_option("SAPT",
                                                       "D_CONVERGENCE"))
    data.update(ind)

    # Dispersion
    primary_basis = wfn_A.basisset()
    core.print_out("\n")
    aux_basis = core.BasisSet.build(sapt_dimer, "DF_BASIS_MP2",
                                    core.get_option("DFMP2", "DF_BASIS_MP2"),
                                    "RIFIT", core.get_global_option('BASIS'))
    fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis,
                                                  cache)
    data.update(fdds_disp)

    if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT":
        mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A,
                                                           primary_basis,
                                                           aux_basis,
                                                           cache,
                                                           do_print=True)
    else:
        mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion(dimer_wfn,
                                                         wfn_A,
                                                         wfn_B,
                                                         primary_basis,
                                                         aux_basis,
                                                         cache,
                                                         do_print=True)
    data.update(mp2_disp)

    # Print out final data
    core.print_out("\n")
    core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)"))

    core.tstop()

    return dimer_wfn
Exemplo n.º 15
0
def nbody_gufunc(func: Union[str, Callable], method_string: str, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.
    This is a generalized univeral function for computing interaction and total quantities.

    :returns: *return type of func* |w--w| The data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    :type func: Callable
    :param func: ``energy`` || etc.

        Python function that accepts method_string and a molecule. Returns a
        energy, gradient, or Hessian as requested.

    :type method_string: str
    :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc.

        First argument, lowercase and usually unlabeled. Indicates the computational
        method to be passed to func.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :type return_wfn: :ref:`boolean <op_py_boolean>`
    :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr|

        Indicate to additionally return the :py:class:`~psi4.core.Wavefunction`
        calculation result as the second element of a tuple.

    :type bsse_type: str or list
    :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc.

        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this
        list is returned by this function. By default, this function is not called.

    :type max_nbody: int
    :param max_nbody: ``3`` || etc.

        Maximum n-body to compute, cannot exceed the number of fragments in the moleucle.

    :type ptype: str
    :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'``

        Type of the procedure passed in.

    :type return_total_data: :ref:`boolean <op_py_boolean>`
    :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr|

        If True returns the total data (energy/gradient/etc) of the system,
        otherwise returns interaction data.

    :type levels: dict
    :param levels: ``{1: 'ccsd(t)', 2: 'mp2', 'supersystem': 'scf'}`` || ``{1: 2, 2: 'ccsd(t)', 3: 'mp2'}`` || etc

        Dictionary of different levels of theory for different levels of expansion
        Note that method_string is not used in this case.
        supersystem computes all higher order n-body effects up to nfragments.

    :type embedding_charges: dict
    :param embedding_charges: ``{1: [-0.834, 0.417, 0.417], ..}``

        Dictionary of atom-centered point charges. keys: 1-based index of fragment, values: list of charges for each fragment.

    :type charge_method: str
    :param charge_method: ``scf/6-31g`` || ``b3lyp/6-31g*`` || etc

        Method to compute point charges for monomers. Overridden by embedding_charges if both are provided.

    :type charge_type: str
    :param charge_type: ``MULLIKEN_CHARGES`` || ``LOWDIN_CHARGES`` 

        Default is ``MULLIKEN_CHARGES``
    """

    # Initialize dictionaries for easy data passing
    metadata, component_results, nbody_results = {}, {}, {}

    # Parse some kwargs
    kwargs = p4util.kwargs_lower(kwargs)
    if kwargs.get('levels', False):
        return driver_nbody_helper.multi_level(func, **kwargs)
    metadata['ptype'] = kwargs.pop('ptype', None)
    metadata['return_wfn'] = kwargs.pop('return_wfn', False)
    metadata['return_total_data'] = kwargs.pop('return_total_data', False)
    metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule())
    metadata['molecule'].update_geometry()
    metadata['molecule'].fix_com(True)
    metadata['molecule'].fix_orientation(True)
    metadata['embedding_charges'] = kwargs.get('embedding_charges', False)
    metadata['kwargs'] = kwargs
    core.clean_variables()

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

    # Parse bsse_type, raise exception if not provided or unrecognized
    metadata['bsse_type_list'] = kwargs.pop('bsse_type')
    if metadata['bsse_type_list'] is None:
        raise ValidationError("N-Body GUFunc: Must pass a bsse_type")
    if not isinstance(metadata['bsse_type_list'], list):
        metadata['bsse_type_list'] = [metadata['bsse_type_list']]

    for num, btype in enumerate(metadata['bsse_type_list']):
        metadata['bsse_type_list'][num] = btype.lower()
        if btype.lower() not in ['cp', 'nocp', 'vmfc']:
            raise ValidationError(
                "N-Body GUFunc: bsse_type '%s' is not recognized" %
                btype.lower())

    metadata['max_nbody'] = kwargs.get('max_nbody', -1)
    if metadata['molecule'].nfragments() == 1:
        raise ValidationError(
            "N-Body requires active molecule to have more than 1 fragment.")
    metadata['max_frag'] = metadata['molecule'].nfragments()
    if metadata['max_nbody'] == -1:
        metadata['max_nbody'] = metadata['molecule'].nfragments()
    else:
        metadata['max_nbody'] = min(metadata['max_nbody'],
                                    metadata['max_frag'])

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

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

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

    # Get compute list
    metadata = build_nbody_compute_list(metadata)

    # Compute N-Body components
    component_results = compute_nbody_components(func, method_string, metadata)

    # Assemble N-Body quantities
    nbody_results = assemble_nbody_components(metadata, component_results)

    # Build wfn and bind variables
    wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp')
    dicts = [
        'energies', 'ptype', 'intermediates', 'energy_body_dict',
        'gradient_body_dict', 'hessian_body_dict', 'nbody',
        'cp_energy_body_dict', 'nocp_energy_body_dict', 'vmfc_energy_body_dict'
    ]
    if metadata['ptype'] == 'gradient':
        wfn.set_gradient(nbody_results['ret_ptype'])
        nbody_results['gradient_body_dict'] = nbody_results['ptype_body_dict']
    elif metadata['ptype'] == 'hessian':
        nbody_results['hessian_body_dict'] = nbody_results['ptype_body_dict']
        wfn.set_hessian(nbody_results['ret_ptype'])
        component_results_gradient = component_results.copy()
        component_results_gradient['ptype'] = component_results_gradient[
            'gradients']
        metadata['ptype'] = 'gradient'
        nbody_results_gradient = assemble_nbody_components(
            metadata, component_results_gradient)
        wfn.set_gradient(nbody_results_gradient['ret_ptype'])
        nbody_results['gradient_body_dict'] = nbody_results_gradient[
            'ptype_body_dict']

    for r in [component_results, nbody_results]:
        for d in r:
            if d in dicts:
                for var, value in r[d].items():
                    try:
                        wfn.set_scalar_variable(str(var), value)
                        core.set_scalar_variable(str(var), value)
                    except:
                        wfn.set_array_variable(
                            d.split('_')[0].upper() + ' ' + str(var),
                            core.Matrix.from_array(value))

    core.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])
    wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])
    if metadata['ptype'] == 'gradient':
        core.set_variable("CURRENT GRADIENT", nbody_results['ret_ptype'])
    elif metadata['ptype'] == 'hessian':
        core.set_variable("CURRENT HESSIAN", nbody_results['ret_ptype'])

    if metadata['return_wfn']:
        return (nbody_results['ret_ptype'], wfn)
    else:
        return nbody_results['ret_ptype']
Exemplo n.º 16
0
def anharmonicity(rvals, energies, mol = None):
    """Generates spectroscopic constants for a diatomic molecules.
       Fits a diatomic potential energy curve using either a 5 or 9 point Legendre fit, locates the minimum
       energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic
       constants.  The r values provided must bracket the minimum energy point, or an error will result.

       A dictionary with the following keys, which correspond to spectroscopic constants, is returned:

       :type rvals: list
       :param rvals: The bond lengths (in Angstrom) for which energies are
           provided of length either 5 or 9 but must be the same length as
           the energies array

       :type energies: list
       :param energies: The energies (Eh) computed at the bond lengths in the rvals list

       :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De"
                 corresponding to the spectroscopic constants in cm-1
    """

    angstrom_to_bohr = 1.0 / p4const.psi_bohr2angstroms
    angstrom_to_meter = 10e-10;

    if len(rvals) != len(energies):
        raise Exception("The number of energies must match the number of distances")

    npoints = len(rvals)

    if npoints != 5 and npoints != 9:
        raise Exception("Only 5- or 9-point fits are implemented right now")

    core.print_out("\n\nPerforming a %d-point fit\n" % npoints)

    core.print_out("\nOptimizing geometry based on current surface:\n\n");
    if (npoints == 5):
        optx = rvals[2]
    elif (npoints == 9):
        optx = rvals[4]

    # Make sure the molecule the user provided is the active one
    molecule = mol if mol is not None else core.get_active_molecule()
    molecule.update_geometry()

    natoms = molecule.natom()
    if natoms != 2:
        raise Exception("The current molecule must be a diatomic for this code to work!")
    m1 = molecule.mass(0)
    m2 = molecule.mass(1)

    maxit = 30
    thres = 1.0e-9
    for i in range(maxit):
        if (npoints == 5):
            grad= first_deriv_5pt(rvals, energies, optx)
            secd = second_deriv_5pt(rvals, energies, optx)
            energy = function_5pt(rvals, energies, optx)
        elif (npoints == 9):
            grad = first_deriv_9pt(rvals, energies, optx)
            secd = second_deriv_9pt(rvals, energies, optx)
            energy = function_9pt(rvals, energies, optx)
        core.print_out("       E = %20.14f, x = %14.7f, grad = %20.14f\n" % (energy, optx, grad))
        if abs(grad) < thres:
            break
        optx -= grad / secd;
    core.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (function_5pt(rvals, energies, optx), optx, grad));

    if optx < min(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a lower range of r values.")
    if optx > max(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a higher range of r values.")

    if (npoints == 5):
        energy = function_5pt(rvals, energies, optx)
        first = first_deriv_5pt(rvals, energies, optx)
        secd = second_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        third = third_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        fourth = fourth_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
    elif (npoints == 9):
        energy = function_9pt(rvals, energies, optx)
        first = first_deriv_9pt(rvals, energies, optx)
        secd = second_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        third = third_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        fourth = fourth_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ

    core.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % energy)
    core.print_out("Gradient           %20.14f\n" % first)
    core.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % secd)
    core.print_out("Cubic Force Constant     %14.7f MDYNE/A**2\n" % third)
    core.print_out("Quartic Force Constant   %14.7f MDYNE/A**3\n" % fourth)

    hbar = p4const.psi_h / (2.0 * pi)
    mu = ((m1*m2)/(m1+m2))*p4const.psi_amu2kg
    we = 5.3088375e-11*sqrt(secd/mu)
    wexe = (1.2415491e-6)*(we/secd)**2 * ((5.0*third*third)/(3.0*secd)-fourth)

    # Rotational constant: Be
    I = ((m1*m2)/(m1+m2)) * p4const.psi_amu2kg * (optx * angstrom_to_meter)**2
    B = p4const.psi_h / (8.0 * pi**2 * p4const.psi_c * I)

    # alpha_e and quartic centrifugal distortion constant
    ae = -(6.0 * B**2 / we) * ((1.05052209e-3*we*third)/(sqrt(B * secd**3))+1.0)
    de = 4.0*B**3 / we**2

    # B0 and r0 (plus re check using Be)
    B0 = B - ae / 2.0
    r0 = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B0))
    recheck = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B))
    r0 /= angstrom_to_meter;
    recheck /= angstrom_to_meter;

    # Fundamental frequency nu
    nu = we - 2.0 * wexe;
    zpve_nu = 0.5 * we - 0.25 * wexe;

    core.print_out("\nre     = %10.6f A  check: %10.6f\n" % (optx, recheck))
    core.print_out("r0       = %10.6f A\n" % r0)
    core.print_out("we       = %10.4f cm-1\n" % we)
    core.print_out("wexe     = %10.4f cm-1\n" % wexe)
    core.print_out("nu       = %10.4f cm-1\n" % nu)
    core.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu)
    core.print_out("Be       = %10.4f cm-1\n" % B)
    core.print_out("B0       = %10.4f cm-1\n" % B0)
    core.print_out("ae       = %10.4f cm-1\n" % ae)
    core.print_out("De       = %10.7f cm-1\n" % de)
    results = {
               "re"               :  optx,
               "r0"               :  r0,
               "we"               :  we,
               "wexe"             :  wexe,
               "nu"               :  nu,
               "ZPVE(harmonic)"   :  zpve_nu,
               "ZPVE(anharmonic)" :  zpve_nu,
               "Be"               :  B,
               "B0"               :  B0,
               "ae"               :  ae,
               "De"               :  de
              }
    return results
Exemplo n.º 17
0
def anharmonicity(rvals, energies, plot_fit='', mol = None):
    """Generates spectroscopic constants for a diatomic molecules.
       Fits a diatomic potential energy curve using a weighted least squares approach
       (c.f. http://dx.doi.org/10.1063/1.4862157, particularly eqn. 7), locates the minimum
       energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic
       constants.  Any number of points greater than 4 may be provided, and they should bracket the minimum.
       The data need not be evenly spaced, and can be provided in any order.  The data are weighted such that
       those closest to the minimum have highest impact.

       A dictionary with the following keys, which correspond to spectroscopic constants, is returned:

       :type rvals: list
       :param rvals: The bond lengths (in Angstrom) for which energies are
           provided, of length at least 5 and equal to the length of the energies array

       :type energies: list
       :param energies: The energies (Eh) computed at the bond lengths in the rvals list

       :type plot_fit: string
       :param plot_fit: A string describing where to save a plot of the harmonic and anharmonic fits, the
           inputted data points, re, r0 and the first few energy levels, if matplotlib
           is available.  Set to 'screen' to generate an interactive plot on the screen instead. If a filename is
           provided, the image type is determined by the extension; see matplotlib for supported file types.

       :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De"
                 corresponding to the spectroscopic constants in cm-1
    """

    angstrom_to_bohr = 1.0 / constants.bohr2angstroms
    angstrom_to_meter = 10e-10;

    # Make sure the input is valid
    if len(rvals) != len(energies):
        raise ValidationError("The number of energies must match the number of distances")
    npoints = len(rvals)
    if npoints < 5:
        raise ValidationError("At least 5 data points must be provided to compute anharmonicity")
    core.print_out("\n\nPerforming a fit to %d data points\n" % npoints)

    # Make sure the molecule the user provided is the active one
    molecule = mol if mol is not None else core.get_active_molecule()
    molecule.update_geometry()
    natoms = molecule.natom()
    if natoms != 2:
        raise Exception("The current molecule must be a diatomic for this code to work!")
    m1 = molecule.mass(0)
    m2 = molecule.mass(1)

    # Optimize the geometry, refitting the surface around each new geometry
    core.print_out("\nOptimizing geometry based on current surface:\n\n");
    re = np.mean(rvals)
    maxit = 30
    thres = 1.0e-9
    for i in range(maxit):
        derivs = least_squares_fit_polynomial(rvals,energies,localization_point=re)
        e,g,H = derivs[0:3]
        core.print_out("       E = %20.14f, x = %14.7f, grad = %20.14f\n" % (e, re, g))
        if abs(g) < thres:
            break
        re -= g/H;
        if i == maxit-1:
            raise ConvergenceError("diatomic geometry optimization", maxit)
    core.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (e, re, g));
    if re < min(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a lower range of r values.")
    if re > max(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a higher range of r values.")

    # Convert to convenient units, and compute spectroscopic constants
    d0,d1,d2,d3,d4 = derivs*constants.hartree2aJ
    core.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % e)
    core.print_out("Gradient           %20.14f\n" % g)
    core.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % d2)
    core.print_out("Cubic Force Constant     %14.7f MDYNE/A**2\n" % d3)
    core.print_out("Quartic Force Constant   %14.7f MDYNE/A**3\n" % d4)

    hbar = constants.h / (2.0 * np.pi)
    mu = ((m1*m2)/(m1+m2))*constants.amu2kg
    we = 5.3088375e-11 * np.sqrt(d2/mu)
    wexe = (1.2415491e-6)*(we/d2)**2 * ((5.0*d3*d3)/(3.0*d2)-d4)

    # Rotational constant: Be
    I = ((m1*m2)/(m1+m2)) * constants.amu2kg * (re * angstrom_to_meter)**2
    B = constants.h / (8.0 * np.pi**2 * constants.c * I)

    # alpha_e and quartic centrifugal distortion constant
    ae = -(6.0 * B**2 / we) * ((1.05052209e-3*we*d3)/(np.sqrt(B * d2**3))+1.0)
    de = 4.0*B**3 / we**2

    # B0 and r0 (plus re check using Be)
    B0 = B - ae / 2.0
    r0 = np.sqrt(constants.h / (8.0 * np.pi**2 * mu * constants.c * B0))
    recheck = np.sqrt(constants.h / (8.0 * np.pi**2 * mu * constants.c * B))
    r0 /= angstrom_to_meter;
    recheck /= angstrom_to_meter;

    # Fundamental frequency nu
    nu = we - 2.0 * wexe;
    zpve_nu = 0.5 * we - 0.25 * wexe;

    # Generate pretty pictures, if requested
    if(plot_fit):
        try:
            import matplotlib.pyplot as plt
        except ImportError:
            msg = "\n\tPlot not generated; matplotlib is not installed on this machine.\n\n"
            print(msg)
            core.print_out(msg)

        # Correct the derivatives for the missing factorial prefactors
        dvals = np.zeros(5)
        dvals[0:5] = derivs[0:5]
        dvals[2] /= 2
        dvals[3] /= 6
        dvals[4] /= 24

        # Default plot range, before considering energy levels
        minE = np.min(energies)
        maxE = np.max(energies)
        minR = np.min(rvals)
        maxR = np.max(rvals)

        # Plot vibrational energy levels
        we_au = we / constants.hartree2wavenumbers
        wexe_au = wexe / constants.hartree2wavenumbers
        coefs2 = [ dvals[2], dvals[1], dvals[0] ]
        coefs4 = [ dvals[4], dvals[3], dvals[2], dvals[1], dvals[0] ]
        for n in range(3):
            Eharm = we_au*(n+0.5)
            Evpt2 = Eharm - wexe_au*(n+0.5)**2
            coefs2[-1] = -Eharm
            coefs4[-1] = -Evpt2
            roots2 = np.roots(coefs2)
            roots4 = np.roots(coefs4)
            xvals2 = roots2 + re
            xvals4 = np.choose(np.where(np.isreal(roots4)), roots4)[0].real + re
            Eharm += dvals[0]
            Evpt2 += dvals[0]
            plt.plot(xvals2, [Eharm, Eharm], 'b', linewidth=1)
            plt.plot(xvals4, [Evpt2, Evpt2], 'g', linewidth=1)
            maxE = Eharm
            maxR = np.max([xvals2,xvals4])
            minR = np.min([xvals2,xvals4])

        # Find ranges for the plot
        dE = maxE - minE
        minE -= 0.2*dE
        maxE += 0.4*dE
        dR = maxR - minR
        minR -= 0.2*dR
        maxR += 0.2*dR

        # Generate the fitted PES
        xpts = np.linspace(minR, maxR, 1000)
        xrel = xpts - re
        xpows = xrel[:, None] ** range(5)
        fit2 = np.einsum('xd,d', xpows[:,0:3], dvals[0:3])
        fit4 = np.einsum('xd,d', xpows, dvals)

        # Make / display the plot
        plt.plot(xpts, fit2, 'b', linewidth=2.5, label='Harmonic (quadratic) fit')
        plt.plot(xpts, fit4, 'g', linewidth=2.5, label='Anharmonic (quartic) fit')
        plt.plot([re, re], [minE, maxE], 'b--', linewidth=0.5)
        plt.plot([r0, r0], [minE, maxE], 'g--', linewidth=0.5)
        plt.scatter(rvals, energies, c='Black', linewidth=3, label='Input Data')
        plt.legend()

        plt.xlabel('Bond length (Angstroms)')
        plt.ylabel('Energy (Eh)')
        plt.xlim(minR, maxR)
        plt.ylim(minE, maxE)
        if plot_fit == 'screen':
            plt.show()
        else:
            plt.savefig(plot_fit)
            core.print_out("\n\tPES fit saved to %s.\n\n" % plot_fit)

    core.print_out("\nre     = %10.6f A  check: %10.6f\n" % (re, recheck))
    core.print_out("r0       = %10.6f A\n" % r0)
    core.print_out("we       = %10.4f cm-1\n" % we)
    core.print_out("wexe     = %10.4f cm-1\n" % wexe)
    core.print_out("nu       = %10.4f cm-1\n" % nu)
    core.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu)
    core.print_out("Be       = %10.4f cm-1\n" % B)
    core.print_out("B0       = %10.4f cm-1\n" % B0)
    core.print_out("ae       = %10.4f cm-1\n" % ae)
    core.print_out("De       = %10.7f cm-1\n" % de)
    results = {
               "re"               :  re,
               "r0"               :  r0,
               "we"               :  we,
               "wexe"             :  wexe,
               "nu"               :  nu,
               "ZPVE(harmonic)"   :  zpve_nu,
               "ZPVE(anharmonic)" :  zpve_nu,
               "Be"               :  B,
               "B0"               :  B0,
               "ae"               :  ae,
               "De"               :  de
              }
    return results
Exemplo n.º 18
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')

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

        if (core.get_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.get_variable("CURRENT ENERGY")

        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.get_variable("CURRENT ENERGY")
        core.set_global_option("SAVE_JK", False)

        # 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 Segement".center(58) + "\n")
        core.print_out("\n")
        core.print_out("         " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n")
        core.print_out("         ---------------------------------------------------------\n")
        core.print_out("\n")

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

        # Electostatics
        elst = sapt_jk_terms.electrostatics(hf_cache, True)
        hf_data.update(elst)

        # Exchange
        exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True)
        hf_data.update(exch)

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

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

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

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

        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
    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)

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

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)

    # Compute Monomer B wavefunction
    if (core.get_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.get_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)

    # 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
Exemplo n.º 19
0
def run_sf_sapt(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")

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

    core.print_out("  ==> Algorithm <==\n\n")
    core.print_out("   JK Algorithm            %12s\n" % core.get_option("SCF", "SCF_TYPE"))
    core.print_out("\n")
    core.print_out("   Required computations:\n")
    core.print_out("     HF  (Monomer A)\n")
    core.print_out("     HF  (Monomer B)\n")
    core.print_out("\n")

    if (core.get_option('SCF', 'REFERENCE') != 'ROHF'):
        raise ValidationError('Spin-Flip SAPT currently only supports restricted open-shell references.')

    # Run the two monomer computations
    core.IO.set_default_namespace('dimer')
    data = {}

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

    # Compute dimer wavefunction
    wfn_A = scf_helper("SCF", molecule=monomerA, banner="SF-SAPT: HF Monomer A", **kwargs)

    core.set_global_option("SAVE_JK", True)
    wfn_B = scf_helper("SCF", molecule=monomerB, banner="SF-SAPT: HF Monomer B", **kwargs)
    sapt_jk = wfn_B.jk()
    core.set_global_option("SAVE_JK", False)
    core.print_out("\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("         " + "Spin-Flip SAPT Exchange and Electrostatics".center(58) + "\n")
    core.print_out("\n")
    core.print_out("         " + "by Daniel G. A. Smith and Konrad Patkowski".center(58) + "\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("\n")

    sf_data = sapt_sf_terms.compute_sapt_sf(sapt_dimer, sapt_jk, wfn_A, wfn_B)

    # Print the results
    core.print_out("   Spin-Flip SAPT Results\n")
    core.print_out("  " + "-" * 103 + "\n")

    for key, value in sf_data.items():
        value = sf_data[key]
        print_vals = (key, value * 1000, value * constants.hartree2kcalmol, value * constants.hartree2kJmol)
        string = "    %-26s % 15.8f [mEh] % 15.8f [kcal/mol] % 15.8f [kJ/mol]\n" % print_vals
        core.print_out(string)
    core.print_out("  " + "-" * 103 + "\n\n")

    dimer_wfn = core.Wavefunction.build(sapt_dimer, wfn_A.basisset())

    # Set variables
    psivar_tanslator = {
        "Elst10": "SAPT ELST ENERGY",
        "Exch10(S^2) [diagonal]": "SAPT EXCH10(S^2),DIAGONAL ENERGY",
        "Exch10(S^2) [off-diagonal]": "SAPT EXCH10(S^2),OFF-DIAGONAL ENERGY",
        "Exch10(S^2) [highspin]": "SAPT EXCH10(S^2),HIGHSPIN ENERGY",
    }

    for k, v in sf_data.items():
        psi_k = psivar_tanslator[k]
        
        dimer_wfn.set_variable(psi_k, v)
        core.set_variable(psi_k, v)

    # Copy over highspin
    core.set_variable("SAPT EXCH ENERGY", sf_data["Exch10(S^2) [highspin]"])

    core.tstop()

    return dimer_wfn
Exemplo n.º 20
0
def ip_fitting(name, omega_l=0.05, omega_r=2.5, omega_convergence=1.0e-3, maxiter=20, **kwargs):
    """Optimize DFT omega parameter for molecular system.

    Parameters
    ----------
    name : string or function
        DFT functional string name or function defining functional
        whose omega is to be optimized.
    omega_l : float, optional
        Minimum omega to be considered during fitting.
    omega_r : float, optional
        Maximum omega to be considered during fitting.
    molecule : :ref:`molecule <op_py_molecule>`, optional
        Target molecule (neutral) for which omega is to be tuned, if not last defined.
    omega_convergence : float, optional
        Threshold below which to consider omega converged. (formerly omega_tolerance)
    maxiter : int, optional
        Maximum number of iterations towards omega convergence.

    Returns
    -------
    float
        Optimal omega parameter.

    """
    optstash = p4util.OptionsState(
        ['SCF', 'REFERENCE'],
        ['SCF', 'GUESS'],
        ['SCF', 'DF_INTS_IO'],
        ['SCF', 'DFT_OMEGA'],
        ['DOCC'],
        ['SOCC'])

    kwargs = p4util.kwargs_lower(kwargs)

    # By default, do not read previous 180 orbitals file
    read = False
    read180 = ''
    if 'read' in kwargs:
        read = True
        read180 = kwargs['read']

    if core.get_option('SCF', 'REFERENCE') != 'UKS':
        core.print_out("""  Requested procedure `ip_fitting` runs further calculations with UKS reference.\n""")
        core.set_local_option('SCF', 'REFERENCE', 'UKS')

    # Make sure the molecule the user provided is the active one, and neutral
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    molecule.update_geometry()

    if molecule.molecular_charge() != 0:
        raise ValidationError("""IP Fitting requires neutral molecule to start.""")
    if molecule.schoenflies_symbol() != 'c1':
        core.print_out("""  Requested procedure `ip_fitting` does not make use of molecular symmetry: """
                       """further calculations in C1 point group.\n""")
    molecule = molecule.clone()
    molecule.reset_point_group('c1')
    molecule.update_geometry()

    charge0 = molecule.molecular_charge()
    mult0 = molecule.multiplicity()

    # How many electrons are there?
    N = 0
    for A in range(molecule.natom()):
        N += molecule.Z(A)
    N -= charge0
    N = int(N)
    Nb = int((N - mult0 + 1) / 2)
    Na = int(N - Nb)

    # Work in the ot namespace for this procedure
    core.IO.set_default_namespace("ot")

    # Burn in to determine orbital eigenvalues
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        copy_file_to_scratch(read180, 'psi', 'ot', 180)
    core.set_local_option("SCF", "DF_INTS_IO", "SAVE")
    E, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule,
                           banner='IP Fitting SCF: Burn-in', **kwargs)
    core.set_local_option("SCF", "DF_INTS_IO", "LOAD")

    if not wfn.functional().is_x_lrc():
        raise ValidationError("""Not sensible to optimize omega for non-long-range-correction functional.""")

    # Determine H**O, to determine mult1
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Na == Nb:
        H**O = -Nb
    elif Nb == 0:
        H**O = Na
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        if E_a >= E_b:
            H**O = Na
        else:
            H**O = -Nb

    Na1 = Na
    Nb1 = Nb
    if H**O > 0:
        Na1 -= 1
    else:
        Nb1 -= 1

    charge1 = charge0 + 1
    mult1 = Na1 - Nb1 + 1

    omegas = []
    E0s = []
    E1s = []
    kIPs = []
    IPs = []
    types = []

    # Right endpoint
    core.set_local_option('SCF', 'DFT_OMEGA', omega_r)

    # Neutral
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180)

    molecule.set_molecular_charge(charge0)
    molecule.set_multiplicity(mult0)
    E0r, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule,
                             banner='IP Fitting SCF: Neutral, Right Endpoint', **kwargs)
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Nb == 0:
        E_HOMO = eps_a.np[int(Na - 1)]
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        E_HOMO = max(E_a, E_b)
    E_HOMOr = E_HOMO
    core.IO.change_file_namespace(180, "ot", "neutral")

    # Cation
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180)

    molecule.set_molecular_charge(charge1)
    molecule.set_multiplicity(mult1)
    E1r = driver.energy('scf', dft_functional=name, molecule=molecule,
                               banner='IP Fitting SCF: Cation, Right Endpoint', **kwargs)
    core.IO.change_file_namespace(180, "ot", "cation")

    IPr = E1r - E0r
    kIPr = -E_HOMOr
    delta_r = IPr - kIPr

    if IPr > kIPr:
        raise ValidationError("""\n***IP Fitting Error: Right Omega limit should have kIP > IP: {} !> {}""".format(kIPr, IPr))

    omegas.append(omega_r)
    types.append('Right Limit')
    E0s.append(E0r)
    E1s.append(E1r)
    IPs.append(IPr)
    kIPs.append(kIPr)

    # Use previous orbitals from here out
    core.set_local_option("SCF", "GUESS", "READ")

    # Left endpoint
    core.set_local_option('SCF', 'DFT_OMEGA', omega_l)

    # Neutral
    core.IO.change_file_namespace(180, "neutral", "ot")
    molecule.set_molecular_charge(charge0)
    molecule.set_multiplicity(mult0)
    core.set_global_option("DOCC", [Nb])
    core.set_global_option("SOCC", [Na - Nb])
    E0l, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule,
                             banner='IP Fitting SCF: Neutral, Left Endpoint', **kwargs)
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Nb == 0:
        E_HOMO = eps_a.np[int(Na - 1)]
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        E_HOMO = max(E_a, E_b)
    E_HOMOl = E_HOMO
    core.IO.change_file_namespace(180, "ot", "neutral")

    # Cation
    core.IO.change_file_namespace(180, "cation", "ot")
    molecule.set_molecular_charge(charge1)
    molecule.set_multiplicity(mult1)
    core.set_global_option("DOCC", [Nb1])
    core.set_global_option("SOCC", [Na1 - Nb1])
    E1l = driver.energy('scf', dft_functional=name, molecule=molecule,
                        banner='IP Fitting SCF: Cation, Left Endpoint', **kwargs)
    core.IO.change_file_namespace(180, "ot", "cation")

    IPl = E1l - E0l
    kIPl = -E_HOMOl
    delta_l = IPl - kIPl

    if IPl < kIPl:
        raise ValidationError("""\n***IP Fitting Error: Left Omega limit should have kIP < IP: {} !< {}""".format(kIPl, IPl))

    omegas.append(omega_l)
    types.append('Left Limit')
    E0s.append(E0l)
    E1s.append(E1l)
    IPs.append(IPl)
    kIPs.append(kIPl)

    converged = False
    repeat_l = 0
    repeat_r = 0
    for step in range(maxiter):

        # Regula Falsi (modified)
        if repeat_l > 1:
            delta_l /= 2.0
        if repeat_r > 1:
            delta_r /= 2.0
        omega = - (omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l
        core.set_local_option('SCF', 'DFT_OMEGA', omega)

        # Neutral
        core.IO.change_file_namespace(180, "neutral", "ot")
        molecule.set_molecular_charge(charge0)
        molecule.set_multiplicity(mult0)
        core.set_global_option("DOCC", [Nb])
        core.set_global_option("SOCC", [Na - Nb])
        E0, wfn = driver.energy('scf', dft_functional=name, return_wfn=True, molecule=molecule,
                                banner='IP Fitting SCF: Neutral, Omega = {:11.3E}'.format(omega), **kwargs)
        eps_a = wfn.epsilon_a()
        eps_b = wfn.epsilon_b()
        if Nb == 0:
            E_HOMO = eps_a.np[int(Na - 1)]
        else:
            E_a = eps_a.np[int(Na - 1)]
            E_b = eps_b.np[int(Nb - 1)]
            E_HOMO = max(E_a, E_b)
        core.IO.change_file_namespace(180, "ot", "neutral")

        # Cation
        core.IO.change_file_namespace(180, "cation", "ot")
        molecule.set_molecular_charge(charge1)
        molecule.set_multiplicity(mult1)
        core.set_global_option("DOCC", [Nb1])
        core.set_global_option("SOCC", [Na1 - Nb1])
        E1 = driver.energy('scf', dft_functional=name, molecule=molecule,
                           banner='IP Fitting SCF: Cation, Omega = {:11.3E}'.format(omega), **kwargs)
        core.IO.change_file_namespace(180, "ot", "cation")

        IP = E1 - E0
        kIP = -E_HOMO
        delta = IP - kIP

        if kIP > IP:
            omega_r = omega
            E0r = E0
            E1r = E1
            IPr = IP
            kIPr = kIP
            delta_r = delta
            repeat_r = 0
            repeat_l += 1
        else:
            omega_l = omega
            E0l = E0
            E1l = E1
            IPl = IP
            kIPl = kIP
            delta_l = delta
            repeat_l = 0
            repeat_r += 1

        omegas.append(omega)
        types.append('Regula-Falsi')
        E0s.append(E0)
        E1s.append(E1)
        IPs.append(IP)
        kIPs.append(kIP)

        # Termination
        if abs(omega_l - omega_r) < omega_convergence:
            converged = True
            break

    core.IO.set_default_namespace("")
    core.print_out("""\n    ==> IP Fitting Results <==\n\n""")

    core.print_out("""     => Occupation Determination <= \n\n""")
    core.print_out("""              %6s %6s %6s %6s %6s %6s\n""" % ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O'))
    core.print_out("""     Neutral: %6d %6d %6d %6d %6d %6d\n""" % (N, Na, Nb, charge0, mult0, H**O))
    core.print_out("""     Cation:  %6d %6d %6d %6d %6d\n\n""" % (N - 1, Na1, Nb1, charge1, mult1))

    core.print_out("""     => Regula Falsi Iterations <=\n\n""")
    core.print_out("""    %3s %11s %14s %14s %14s %s\n""" % ('N','Omega','IP','kIP','Delta','Type'))
    for k in range(len(omegas)):
        core.print_out("""    %3d %11.3E %14.6E %14.6E %14.6E %s\n""" %
                       (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k]))

    optstash.restore()
    if converged:
        core.print_out("""\n    IP Fitting Converged\n""")
        core.print_out("""    Final omega = %14.6E\n""" % ((omega_l + omega_r) / 2))
        core.print_out("""\n    "M,I. does the dying. Fleet just does the flying."\n""")
        core.print_out("""            -Starship Troopers\n""")

    else:
        raise ConvergenceError("""IP Fitting """, step + 1)

    return ((omega_l + omega_r) / 2)
Exemplo n.º 21
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
Exemplo n.º 22
0
def run_cfour(name, **kwargs):
    """Function that prepares environment and input files
    for a calculation calling Stanton and Gauss's CFOUR code.
    Also processes results back into Psi4 format.

    This function is not called directly but is instead called by
    :py:func:`~psi4.driver.energy` or :py:func:`~psi4.driver.optimize` when a Cfour
    method is requested (through *name* argument). In order to function
    correctly, the Cfour executable ``xcfour`` must be present in
    :envvar:`PATH` or :envvar:`PSIPATH`.

    .. hlist::
       :columns: 1

       * Many :ref:`PSI Variables <apdx:cfour_psivar>` extracted from the Cfour output
       * Python dictionary of associated file constants accessible as ``P4C4_INFO['zmat']``, ``P4C4_INFO['output']``, ``P4C4_INFO['grd']``, *etc.*


    :type name: str
    :param name: ``'c4-scf'`` || ``'c4-ccsd(t)'`` || ``'cfour'`` || etc.

        First argument, usually unlabeled. Indicates the computational
        method to be applied to the system.

    :type keep: :ref:`boolean <op_py_boolean>`
    :param keep: ``'on'`` || |dl| ``'off'`` |dr|

        Indicates whether to delete the Cfour scratch directory upon
        completion of the Cfour job.

    :type path: str
    :param path:

        Indicates path to Cfour scratch directory (with respect to Psi4
        scratch directory). Otherwise, the default is a subdirectory
        within the Psi4 scratch directory.

        If specified, GENBAS and/or ZMAT within will be used.

    :type genbas: str
    :param genbas:

        Indicates that contents should be used for GENBAS file.

    GENBAS is a complicated topic. It is quite unnecessary if the
    molecule is from a molecule {...} block and basis is set through
    |Psifours| BASIS keyword. In that case, a GENBAS is written from
    LibMints and all is well. Otherwise, a GENBAS is looked for in
    the usual places: PSIPATH, PATH, PSIDATADIR/basis. If path kwarg is
    specified, also looks there preferentially for a GENBAS. Can
    also specify GENBAS within an input file through a string and
    setting the genbas kwarg. Note that due to the input parser's
    aggression, blank lines need to be replaced by the text blankline.

    """
    lowername = name.lower()
    internal_p4c4_info = {}
    return_wfn = kwargs.pop('return_wfn', False)

    # Make sure the molecule the user provided is the active one
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    molecule.update_geometry()

    optstash = p4util.OptionsState(['CFOUR', 'TRANSLATE_PSI4'])

    # Determine calling function and hence dertype
    calledby = inspect.stack()[1][3]
    dertype = ['energy', 'gradient', 'hessian'].index(calledby)
    #print('I am %s called by %s called by %s.\n' %
    #    (inspect.stack()[0][3], inspect.stack()[1][3], inspect.stack()[2][3]))

    # Save submission directory
    current_directory = os.getcwd()

    # Move into job scratch directory
    psioh = core.IOManager.shared_object()
    psio = core.IO.shared_object()
    os.chdir(psioh.get_default_path())

    # Construct and move into cfour subdirectory of job scratch directory
    cfour_tmpdir = kwargs['path'] if 'path' in kwargs else \
        'psi.' + str(os.getpid()) + '.' + psio.get_default_namespace() + \
        '.cfour.' + str(uuid.uuid4())[:8]
    if not os.path.exists(cfour_tmpdir):
        os.mkdir(cfour_tmpdir)
    os.chdir(cfour_tmpdir)

    # Find environment by merging PSIPATH and PATH environment variables
    lenv = {
        'PATH':
        ':'.join([
            os.path.abspath(x)
            for x in os.environ.get('PSIPATH', '').split(':') if x != ''
        ]) + ':' + os.environ.get('PATH') + ':' + core.get_datadir() +
        '/basis',
        'GENBAS_PATH':
        core.get_datadir() + '/basis',
        'CFOUR_NUM_CORES':
        os.environ.get('CFOUR_NUM_CORES'),
        'MKL_NUM_THREADS':
        os.environ.get('MKL_NUM_THREADS'),
        'OMP_NUM_THREADS':
        os.environ.get('OMP_NUM_THREADS'),
        'LD_LIBRARY_PATH':
        os.environ.get('LD_LIBRARY_PATH')
    }

    if 'path' in kwargs:
        lenv['PATH'] = kwargs['path'] + ':' + lenv['PATH']
    #   Filter out None values as subprocess will fault on them
    lenv = {k: v for k, v in lenv.items() if v is not None}

    # Load the GENBAS file
    genbas_path = qcdb.search_file('GENBAS', lenv['GENBAS_PATH'])
    if genbas_path:
        try:
            shutil.copy2(genbas_path, psioh.get_default_path() + cfour_tmpdir)
        except shutil.Error:  # should only fail if src and dest equivalent
            pass
        core.print_out("\n  GENBAS loaded from %s\n" % (genbas_path))
        core.print_out("  CFOUR to be run from %s\n" %
                       (psioh.get_default_path() + cfour_tmpdir))
    else:
        message = """
  GENBAS file for CFOUR interface not found. Either:
  [1] Supply a GENBAS by placing it in PATH or PSIPATH
      [1a] Use cfour {} block with molecule and basis directives.
      [1b] Use molecule {} block and CFOUR_BASIS keyword.
  [2] Allow Psi4's internal basis sets to convert to GENBAS
      [2a] Use molecule {} block and BASIS keyword.

"""
        core.print_out(message)
        core.print_out('  Search path that was tried:\n')
        core.print_out(lenv['PATH'].replace(':', ', '))

    # Generate the ZMAT input file in scratch
    if 'path' in kwargs and os.path.isfile('ZMAT'):
        core.print_out("  ZMAT loaded from %s\n" %
                       (psioh.get_default_path() + kwargs['path'] + '/ZMAT'))
    else:
        with open('ZMAT', 'w') as cfour_infile:
            cfour_infile.write(write_zmat(lowername, dertype, molecule))

    internal_p4c4_info['zmat'] = open('ZMAT', 'r').read()
    #core.print_out('\n====== Begin ZMAT input for CFOUR ======\n')
    #core.print_out(open('ZMAT', 'r').read())
    #core.print_out('======= End ZMAT input for CFOUR =======\n\n')
    #print('\n====== Begin ZMAT input for CFOUR ======')
    #print(open('ZMAT', 'r').read())
    #print('======= End ZMAT input for CFOUR =======\n')

    if 'genbas' in kwargs:
        with open('GENBAS', 'w') as cfour_basfile:
            cfour_basfile.write(kwargs['genbas'].replace(
                '\nblankline\n', '\n\n'))
        core.print_out('  GENBAS loaded from kwargs string\n')

    # Close psi4 output file and reopen with filehandle
    print('output in', current_directory + '/' + core.outfile_name())
    pathfill = '' if os.path.isabs(
        core.outfile_name()) else current_directory + os.path.sep

    # Handle threading
    #   OMP_NUM_THREADS from env is in lenv from above
    #   threads from psi4 -n (core.get_num_threads()) is ignored
    #   CFOUR_OMP_NUM_THREADS psi4 option takes precedence, handled below
    if core.has_option_changed('CFOUR', 'CFOUR_OMP_NUM_THREADS'):
        lenv['OMP_NUM_THREADS'] = str(
            core.get_option('CFOUR', 'CFOUR_OMP_NUM_THREADS'))

    #print("""\n\n<<<<<  RUNNING CFOUR ...  >>>>>\n\n""")
    # Call executable xcfour, directing cfour output to the psi4 output file
    cfour_executable = kwargs['c4exec'] if 'c4exec' in kwargs else 'xcfour'
    try:
        retcode = subprocess.Popen([cfour_executable],
                                   bufsize=0,
                                   stdout=subprocess.PIPE,
                                   env=lenv)
    except OSError as e:
        sys.stderr.write(
            'Program %s not found in path or execution failed: %s\n' %
            (cfour_executable, e.strerror))
        message = ('Program %s not found in path or execution failed: %s\n' %
                   (cfour_executable, e.strerror))
        raise ValidationError(message)

    c4out = ''
    while True:
        data = retcode.stdout.readline()
        data = data.decode('utf-8')
        if not data:
            break
        core.print_out(data)
        c4out += data
    internal_p4c4_info['output'] = c4out

    c4files = {}
    core.print_out('\n')
    for item in ['GRD', 'FCMFINAL', 'DIPOL']:
        try:
            with open(psioh.get_default_path() + cfour_tmpdir + '/' + item,
                      'r') as handle:
                c4files[item] = handle.read()
                core.print_out('  CFOUR scratch file %s has been read\n' %
                               (item))
                core.print_out('%s\n' % c4files[item])
                internal_p4c4_info[item.lower()] = c4files[item]
        except IOError:
            pass
    core.print_out('\n')

    if molecule.name() == 'blank_molecule_psi4_yo':
        qcdbmolecule = None
    else:
        molecule.update_geometry()
        qcdbmolecule = qcdb.Molecule(
            molecule.create_psi4_string_from_molecule())
        qcdbmolecule.update_geometry()

    # c4mol, if it exists, is dinky, just a clue to geometry of cfour results
    psivar, c4grad, c4mol = qcdb.cfour.harvest(qcdbmolecule, c4out, **c4files)

    # Absorb results into psi4 data structures
    for key in psivar.keys():
        core.set_variable(key.upper(), float(psivar[key]))

    if qcdbmolecule is None and c4mol is not None:

        molrec = qcel.molparse.from_string(
            c4mol.create_psi4_string_from_molecule(),
            enable_qm=True,
            missing_enabled_return_qm='minimal',
            enable_efp=False,
            missing_enabled_return_efp='none',
        )
        molecule = core.Molecule.from_dict(molrec['qm'])
        molecule.set_name('blank_molecule_psi4_yo')
        core.set_active_molecule(molecule)
        molecule.update_geometry()
        # This case arises when no Molecule going into calc (cfour {} block) but want
        #   to know the orientation at which grad, properties, etc. are returned (c4mol).
        #   c4mol is dinky, w/o chg, mult, dummies and retains name
        #   blank_molecule_psi4_yo so as to not interfere with future cfour {} blocks

    if c4grad is not None:
        mat = core.Matrix.from_list(c4grad)
        core.set_gradient(mat)

        #print '    <<<   [3] C4-GRD-GRAD   >>>'
        #mat.print()


#    exit(1)

# # Things needed core.so module to do
# collect c4out string
# read GRD
# read FCMFINAL
# see if theres an active molecule

# # Things delegatable to qcdb
# parsing c4out
# reading GRD and FCMFINAL strings
# reconciling p4 and c4 molecules (orient)
# reconciling c4out and GRD and FCMFINAL results
# transforming frame of results back to p4

# # Things run_cfour needs to have back
# psivar
# qcdb.Molecule of c4?
# coordinates?
# gradient in p4 frame

#    # Process the cfour output
#    psivar, c4coord, c4grad = qcdb.cfour.cfour_harvest(c4out)
#    for key in psivar.keys():
#        core.set_variable(key.upper(), float(psivar[key]))
#
#    # Awful Hack - Go Away TODO
#    if c4grad:
#        molecule = core.get_active_molecule()
#        molecule.update_geometry()
#
#        if molecule.name() == 'blank_molecule_psi4_yo':
#            p4grad = c4grad
#            p4coord = c4coord
#        else:
#            qcdbmolecule = qcdb.Molecule(molecule.create_psi4_string_from_molecule())
#            #p4grad = qcdbmolecule.deorient_array_from_cfour(c4coord, c4grad)
#            #p4coord = qcdbmolecule.deorient_array_from_cfour(c4coord, c4coord)
#
#            with open(psioh.get_default_path() + cfour_tmpdir + '/GRD', 'r') as cfour_grdfile:
#                c4outgrd = cfour_grdfile.read()
#            print('GRD\n',c4outgrd)
#            c4coordGRD, c4gradGRD = qcdb.cfour.cfour_harvest_files(qcdbmolecule, grd=c4outgrd)
#
#        p4mat = core.Matrix.from_list(p4grad)
#        core.set_gradient(p4mat)

#    print('    <<<  P4 PSIVAR  >>>')
#    for item in psivar:
#        print('       %30s %16.8f' % (item, psivar[item]))
#print('    <<<  P4 COORD   >>>')
#for item in p4coord:
#    print('       %16.8f %16.8f %16.8f' % (item[0], item[1], item[2]))
#    print('    <<<   P4 GRAD   >>>')
#    for item in c4grad:
#        print('       %16.8f %16.8f %16.8f' % (item[0], item[1], item[2]))

# Clean up cfour scratch directory unless user instructs otherwise
    keep = yes.match(str(kwargs['keep'])) if 'keep' in kwargs else False
    os.chdir('..')
    try:
        if keep or ('path' in kwargs):
            core.print_out('\n  CFOUR scratch files have been kept in %s\n' %
                           (psioh.get_default_path() + cfour_tmpdir))
        else:
            shutil.rmtree(cfour_tmpdir)
    except OSError as e:
        print('Unable to remove CFOUR temporary directory %s' % e,
              file=sys.stderr)
        exit(1)

    # Return to submission directory and reopen output file
    os.chdir(current_directory)

    core.print_out('\n')
    p4util.banner(' Cfour %s %s Results ' %
                  (name.lower(), calledby.capitalize()))
    core.print_variables()
    if c4grad is not None:
        core.get_gradient().print_out()

    core.print_out('\n')
    p4util.banner(' Cfour %s %s Results ' %
                  (name.lower(), calledby.capitalize()))
    core.print_variables()
    if c4grad is not None:
        core.get_gradient().print_out()

    # Quit if Cfour threw error
    if 'CFOUR ERROR CODE' in core.variables():
        raise ValidationError("""Cfour exited abnormally.""")

    P4C4_INFO.clear()
    P4C4_INFO.update(internal_p4c4_info)

    optstash.restore()

    # new skeleton wavefunction w/mol, highest-SCF basis (just to choose one), & not energy
    #   Feb 2017 hack. Could get proper basis in skel wfn even if not through p4 basis kw
    if core.get_global_option('BASIS') in ["", "(AUTO)"]:
        gobas = "sto-3g"
    else:
        gobas = core.get_global_option('BASIS')
    basis = core.BasisSet.build(molecule, "ORBITAL", gobas)
    if basis.has_ECP():
        raise ValidationError("""ECPs not hooked up for Cfour""")
    wfn = core.Wavefunction(molecule, basis)
    for k, v in psivar.items():
        wfn.set_variable(k.upper(), float(v))

    optstash.restore()

    if dertype == 0:
        finalquantity = psivar['CURRENT ENERGY']
    elif dertype == 1:
        finalquantity = core.get_gradient()
        wfn.set_gradient(finalquantity)
        if finalquantity.rows(0) < 20:
            core.print_out('CURRENT GRADIENT')
            finalquantity.print_out()
    elif dertype == 2:
        pass
        #finalquantity = finalhessian
        #wfn.set_hessian(finalquantity)
        #if finalquantity.rows(0) < 20:
        #    core.print_out('CURRENT HESSIAN')
        #    finalquantity.print_out()

    return wfn
Exemplo n.º 23
0
Arquivo: roa.py Projeto: bennybp/psi4
def run_roa(name, **kwargs):
    """
        Main driver for managing Raman Optical activity computations with
        CC response theory.

        Uses distributed finite differences approach -->
            1. Sets up a database to keep track of running/finished/waiting
                computations.
            2. Generates separate input files for displaced geometries.
            3. When all displacements are run, collects the necessary information
                from each displaced computation, and computes final result.
    """

    # Get list of omega values -> Make sure we only have one wavelength
    # Catch this now before any real work gets done
    omega = core.get_option('CCRESPONSE', 'OMEGA')
    if len(omega) > 2:
        raise Exception('ROA scattering can only be performed for one wavelength.')
    else:
        pass

    core.print_out(
        'Running ROA computation. Subdirectories for each '
        'required displaced geometry have been created.\n\n')

    dbno = 0
    # Initialize database
    db = shelve.open('database', writeback=True)
    # Check if final result is in here
    # ->if we have already computed roa, back up the dict
    # ->copy it setting this flag to false and continue
    if ('roa_computed' in db) and ( db['roa_computed'] ):
        db2 = shelve.open('.database.bak{}'.format(dbno), writeback=True)
        dbno += 1
        for key,value in db.items():
            db2[key]=value

        db2.close()
        db['roa_computed'] = False
    else:
        db['roa_computed'] = False

    if 'inputs_generated' not in db:
        findif_response_utils.initialize_database(db,name,"roa", ["roa_tensor"])

    # Generate input files
    if not db['inputs_generated']:
        findif_response_utils.generate_inputs(db,name)
        # handled by helper db['inputs_generated'] = True

    # Check job status
    if db['inputs_generated'] and not db['jobs_complete']:
        print('Checking status')
        findif_response_utils.stat(db)
        for job, status in db['job_status'].items():
            print("{} --> {}".format(job, status))

    # Compute ROA Scattering
    if db['jobs_complete']:
        mygauge = core.get_option('CCRESPONSE', 'GAUGE')
        consider_gauge = {
            'LENGTH': ['Length Gauge'],
            'VELOCITY': ['Modified Velocity Gauge'],
            'BOTH': ['Length Gauge', 'Modified Velocity Gauge']
        }
        gauge_list = ["{} Results".format(x) for x in consider_gauge[mygauge]]
        # Gather data
        dip_polar_list = findif_response_utils.collect_displaced_matrix_data(
            db, 'Dipole Polarizability', 3)
        opt_rot_list = [
            x for x in (
                findif_response_utils.collect_displaced_matrix_data(
                    db,
                    "Optical Rotation Tensor ({})".format(gauge),
                    3
                )
                for gauge in consider_gauge[mygauge]
            )
        ]
        dip_quad_polar_list = findif_response_utils.collect_displaced_matrix_data(
            db, "Electric-Dipole/Quadrupole Polarizability", 9)
        # Compute Scattering
        # Run new function (src/bin/ccresponse/scatter.cc)
        core.print_out('Running scatter function')
        step = core.get_local_option('FINDIF', 'DISP_SIZE')
        for g_idx, gauge in enumerate(opt_rot_list):
            print('\n\n----------------------------------------------------------------------')
            print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx]))
            print('----------------------------------------------------------------------\n\n')
            core.print_out('\n\n----------------------------------------------------------------------\n')
            core.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format(gauge_list[g_idx]))
            core.print_out('----------------------------------------------------------------------\n\n')
            print('roa.py:85 I am not being passed a molecule, grabbing from global :(')
            core.scatter(core.get_active_molecule(), step, dip_polar_list, gauge, dip_quad_polar_list)

        db['roa_computed'] = True

    db.close()
Exemplo n.º 24
0
def nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.
    This is a generalized univeral function for computing interaction quantities.

    :returns: *return type of func* |w--w| The interaction data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    :type func: function
    :param func: ``energy`` || etc.

        Python function that accepts method_string and a molecule. Returns a
        energy, gradient, or Hessian as requested.

    :type method_string: string
    :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc.

        First argument, lowercase and usually unlabeled. Indicates the computational
        method to be passed to func.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :type return_wfn: :ref:`boolean <op_py_boolean>`
    :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr|

        Indicate to additionally return the :py:class:`~psi4.core.Wavefunction`
        calculation result as the second element of a tuple.

    :type bsse_type: string or list
    :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc.

        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this
        list is returned by this function. By default, this function is not called.

    :type max_nbody: int
    :param max_nbody: ``3`` || etc.

        Maximum n-body to compute, cannot exceed the number of fragments in the moleucle.

    :type ptype: string
    :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'``

        Type of the procedure passed in.

    :type return_total_data: :ref:`boolean <op_py_boolean>`
    :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr|

        If True returns the total data (energy/gradient/etc) of the system,
        otherwise returns interaction data.
    """

    # Initialize dictionaries for easy data passing
    metadata, component_results, nbody_results = {}, {}, {}

    # Parse some kwargs
    kwargs = p4util.kwargs_lower(kwargs)
    metadata['ptype'] = kwargs.pop('ptype', None)
    metadata['return_wfn'] = kwargs.pop('return_wfn', False)
    metadata['return_total_data'] = kwargs.pop('return_total_data', False)
    metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule())
    metadata['molecule'].update_geometry()
    metadata['kwargs'] = kwargs
    core.clean_variables()

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

    # Parse bsse_type, raise exception if not provided or unrecognized
    metadata['bsse_type_list'] = kwargs.pop('bsse_type')
    if metadata['bsse_type_list'] is None:
        raise ValidationError("N-Body GUFunc: Must pass a bsse_type")
    if not isinstance(metadata['bsse_type_list'], list):
        metadata['bsse_type_list'] = [metadata['bsse_type_list']]

    for num, btype in enumerate(metadata['bsse_type_list']):
        metadata['bsse_type_list'][num] = btype.lower()
        if btype.lower() not in ['cp', 'nocp', 'vmfc']:
            raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower())

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

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

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

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

    # Get compute list
    metadata = build_nbody_compute_list(metadata)

    # Compute N-Body components
    component_results = compute_nbody_components(func, method_string, metadata)

    # Assemble N-Body quantities
    nbody_results = assemble_nbody_components(metadata, component_results)

    # Figure out returns
    if metadata['ptype'] != 'energy':
        if metadata['return_total_data']:
            np_final_ptype = nbody_results['ptype_body_dict'][metadata['max_nbody']].copy()
        else:
            np_final_ptype = nbody_results['ptype_body_dict'][metadata['max_nbody']].copy()
            np_final_ptype -= ptype_body_dict[1]

        nbody_results['ret_ptype'] = core.Matrix.from_array(np_final_ptype)
    else:
        nbody_results['ret_ptype'] = nbody_results['ret_energy']

    # Build wfn and bind variables
    wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp')
    dicts = ['energies', 'ptype', 'intermediates', 'energy_body_dict', 'ptype_body_dict', 'nbody']
    for r in [component_results, nbody_results]:
        for d in r:
            if d in dicts:
                for var, value in r[d].items():
                    wfn.set_variable(str(var), value)
                    core.set_variable(str(var), value)

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

    core.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])
    wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])

    if metadata['return_wfn']:
        return (nbody_results['ret_ptype'], wfn)
    else:
        return nbody_results['ret_ptype']
Exemplo n.º 25
0
def anharmonicity(rvals, energies, mol = None):
    """Generates spectroscopic constants for a diatomic molecules.
       Fits a diatomic potential energy curve using either a 5 or 9 point Legendre fit, locates the minimum
       energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic
       constants.  The r values provided must bracket the minimum energy point, or an error will result.

       A dictionary with the following keys, which correspond to spectroscopic constants, is returned:

       :type rvals: list
       :param rvals: The bond lengths (in Angstrom) for which energies are
           provided of length either 5 or 9 but must be the same length as
           the energies array

       :type energies: list
       :param energies: The energies (Eh) computed at the bond lengths in the rvals list

       :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De"
                 corresponding to the spectroscopic constants in cm-1
    """

    angstrom_to_bohr = 1.0 / p4const.psi_bohr2angstroms
    angstrom_to_meter = 10e-10;

    if len(rvals) != len(energies):
        raise Exception("The number of energies must match the number of distances")

    npoints = len(rvals)

    if npoints != 5 and npoints != 9:
        raise Exception("Only 5- or 9-point fits are implemented right now")

    core.print_out("\n\nPerforming a %d-point fit\n" % npoints)

    core.print_out("\nOptimizing geometry based on current surface:\n\n");
    if (npoints == 5):
        optx = rvals[2]
    elif (npoints == 9):
        optx = rvals[4]

    # Make sure the molecule the user provided is the active one
    molecule = mol if mol is not None else core.get_active_molecule()
    molecule.update_geometry()

    natoms = molecule.natom()
    if natoms != 2:
        raise Exception("The current molecule must be a diatomic for this code to work!")
    m1 = molecule.mass(0)
    m2 = molecule.mass(1)

    maxit = 30
    thres = 1.0e-9
    for i in range(maxit):
        if (npoints == 5):
            grad= first_deriv_5pt(rvals, energies, optx)
            secd = second_deriv_5pt(rvals, energies, optx)
            energy = function_5pt(rvals, energies, optx)
        elif (npoints == 9):
            grad = first_deriv_9pt(rvals, energies, optx)
            secd = second_deriv_9pt(rvals, energies, optx)
            energy = function_9pt(rvals, energies, optx)
        core.print_out("       E = %20.14f, x = %14.7f, grad = %20.14f\n" % (energy, optx, grad))
        if abs(grad) < thres:
            break
        optx -= grad / secd;
    core.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (function_5pt(rvals, energies, optx), optx, grad));

    if optx < min(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a lower range of r values.")
    if optx > max(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a higher range of r values.")

    if (npoints == 5):
        energy = function_5pt(rvals, energies, optx)
        first = first_deriv_5pt(rvals, energies, optx)
        secd = second_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        third = third_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        fourth = fourth_deriv_5pt(rvals, energies, optx) * p4const.psi_hartree2aJ
    elif (npoints == 9):
        energy = function_9pt(rvals, energies, optx)
        first = first_deriv_9pt(rvals, energies, optx)
        secd = second_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        third = third_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ
        fourth = fourth_deriv_9pt(rvals, energies, optx) * p4const.psi_hartree2aJ

    core.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % energy)
    core.print_out("Gradient           %20.14f\n" % first)
    core.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % secd)
    core.print_out("Cubic Force Constant     %14.7f MDYNE/A**2\n" % third)
    core.print_out("Quartic Force Constant   %14.7f MDYNE/A**3\n" % fourth)

    hbar = p4const.psi_h / (2.0 * pi)
    mu = ((m1*m2)/(m1+m2))*p4const.psi_amu2kg
    we = 5.3088375e-11*sqrt(secd/mu)
    wexe = (1.2415491e-6)*(we/secd)**2 * ((5.0*third*third)/(3.0*secd)-fourth)

    # Rotational constant: Be
    I = ((m1*m2)/(m1+m2)) * p4const.psi_amu2kg * (optx * angstrom_to_meter)**2
    B = p4const.psi_h / (8.0 * pi**2 * p4const.psi_c * I)

    # alpha_e and quartic centrifugal distortion constant
    ae = -(6.0 * B**2 / we) * ((1.05052209e-3*we*third)/(sqrt(B * secd**3))+1.0)
    de = 4.0*B**3 / we**2

    # B0 and r0 (plus re check using Be)
    B0 = B - ae / 2.0
    r0 = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B0))
    recheck = sqrt(p4const.psi_h / (8.0 * pi**2 * mu * p4const.psi_c * B))
    r0 /= angstrom_to_meter;
    recheck /= angstrom_to_meter;

    # Fundamental frequency nu
    nu = we - 2.0 * wexe;
    zpve_nu = 0.5 * we - 0.25 * wexe;

    core.print_out("\nre     = %10.6f A  check: %10.6f\n" % (optx, recheck))
    core.print_out("r0       = %10.6f A\n" % r0)
    core.print_out("we       = %10.4f cm-1\n" % we)
    core.print_out("wexe     = %10.4f cm-1\n" % wexe)
    core.print_out("nu       = %10.4f cm-1\n" % nu)
    core.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu)
    core.print_out("Be       = %10.4f cm-1\n" % B)
    core.print_out("B0       = %10.4f cm-1\n" % B0)
    core.print_out("ae       = %10.4f cm-1\n" % ae)
    core.print_out("De       = %10.7f cm-1\n" % de)
    results = {
               "re"               :  optx,
               "r0"               :  r0,
               "we"               :  we,
               "wexe"             :  wexe,
               "nu"               :  nu,
               "ZPVE(harmonic)"   :  zpve_nu,
               "ZPVE(anharmonic)" :  zpve_nu,
               "Be"               :  B,
               "B0"               :  B0,
               "ae"               :  ae,
               "De"               :  de
              }
    return results
Exemplo n.º 26
0
def run_roa(name, **kwargs):
    """
        Main driver for managing Raman Optical activity computations with
        CC response theory.

        Uses distributed finite differences approach -->
            1. Sets up a database to keep track of running/finished/waiting
                computations.
            2. Generates separate input files for displaced geometries.
            3. When all displacements are run, collects the necessary information
                from each displaced computation, and computes final result.
    """

    # Get list of omega values -> Make sure we only have one wavelength
    # Catch this now before any real work gets done
    omega = core.get_option('CCRESPONSE', 'OMEGA')
    if len(omega) > 2:
        raise Exception('ROA scattering can only be performed for one wavelength.')
    else:
        pass

    core.print_out(
        'Running ROA computation. Subdirectories for each '
        'required displaced geometry have been created.\n\n')

    dbno = 0
    # Initialize database
    db = shelve.open('database', writeback=True)
    # Check if final result is in here
    # ->if we have already computed roa, back up the dict
    # ->copy it setting this flag to false and continue
    if ('roa_computed' in db) and ( db['roa_computed'] ):
        db2 = shelve.open('.database.bak{}'.format(dbno), writeback=True)
        dbno += 1
        for key,value in db.items():
            db2[key]=value

        db2.close()
        db['roa_computed'] = False
    else:
        db['roa_computed'] = False

    if 'inputs_generated' not in db:
        findif_response_utils.initialize_database(db,name,"roa", ["roa_tensor"])

    # Generate input files
    if not db['inputs_generated']:
        findif_response_utils.generate_inputs(db,name)
        # handled by helper db['inputs_generated'] = True

    # Check job status
    if db['inputs_generated'] and not db['jobs_complete']:
        print('Checking status')
        findif_response_utils.stat(db)
        for job, status in db['job_status'].items():
            print("{} --> {}".format(job, status))

    # Compute ROA Scattering
    if db['jobs_complete']:
        mygauge = core.get_option('CCRESPONSE', 'GAUGE')
        consider_gauge = {
            'LENGTH': ['Length Gauge'],
            'VELOCITY': ['Modified Velocity Gauge'],
            'BOTH': ['Length Gauge', 'Modified Velocity Gauge']
        }
        gauge_list = ["{} Results".format(x) for x in consider_gauge[mygauge]]
        # Gather data
        dip_polar_list = findif_response_utils.collect_displaced_matrix_data(
            db, 'Dipole Polarizability', 3)
        opt_rot_list = [
            x for x in (
                findif_response_utils.collect_displaced_matrix_data(
                    db,
                    "Optical Rotation Tensor ({})".format(gauge),
                    3
                )
                for gauge in consider_gauge[mygauge]
            )
        ]
        dip_quad_polar_list = findif_response_utils.collect_displaced_matrix_data(
            db, "Electric-Dipole/Quadrupole Polarizability", 9)
        # Compute Scattering
        # Run new function (src/bin/ccresponse/scatter.cc)
        core.print_out('Running scatter function')
        step = core.get_local_option('FINDIF', 'DISP_SIZE')
        for g_idx, gauge in enumerate(opt_rot_list):
            print('\n\n----------------------------------------------------------------------')
            print('\t%%%%%%%%%% {} %%%%%%%%%%'.format(gauge_list[g_idx]))
            print('----------------------------------------------------------------------\n\n')
            core.print_out('\n\n----------------------------------------------------------------------\n')
            core.print_out('\t%%%%%%%%%% {} %%%%%%%%%%\n'.format(gauge_list[g_idx]))
            core.print_out('----------------------------------------------------------------------\n\n')
            print('roa.py:85 I am not being passed a molecule, grabbing from global :(')
            core.scatter(core.get_active_molecule(), step, dip_polar_list, gauge, dip_quad_polar_list)

        db['roa_computed'] = True

    db.close()
Exemplo n.º 27
0
def run_sapt_dft(name, **kwargs):
    optstash = p4util.OptionsState(['SCF', 'SCF_TYPE'],
                                   ['SCF', 'REFERENCE'],
                                   ['SCF', 'DFT_FUNCTIONAL'],
                                   ['SCF', 'DFT_GRAC_SHIFT'],
                                   ['SCF', 'SAVE_JK'])

    core.tstart()
    # Alter default algorithm
    if not core.has_option_changed('SCF', 'SCF_TYPE'):
        core.set_local_option('SCF', 'SCF_TYPE', 'DF')

    core.prepare_options_for_module("SAPT")

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

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

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

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

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

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

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


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

    core.set_global_option("SAVE_JK", True)
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        # core.set_global_option('DF_INTS_IO', 'LOAD')
        core.set_global_option('DF_INTS_IO', 'SAVE')

    # # Compute dimer wavefunction
    hf_cache = {}
    hf_wfn_dimer = None
    if do_delta_hf:
        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.set_global_option('DF_INTS_IO', 'SAVE')

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

        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'dimer', 'monomerA')
        hf_wfn_A = scf_helper(
            "SCF", molecule=monomerA, banner="SAPT(DFT): delta HF Monomer A", **kwargs)
        hf_data["HF MONOMER A"] = core.get_variable("CURRENT ENERGY")

        if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
            core.IO.change_file_namespace(97, 'monomerA', 'monomerB')
        hf_wfn_B = scf_helper(
            "SCF", molecule=monomerB, banner="SAPT(DFT): delta HF Monomer B", **kwargs)
        hf_data["HF MONOMER B"] = core.get_variable("CURRENT ENERGY")

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

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

        # Build cache and JK
        sapt_jk = hf_wfn_B.jk()

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

        # Electostatics
        elst = sapt_jk_terms.electrostatics(hf_cache, True)
        hf_data.update(elst)

        # Exchange
        exch = sapt_jk_terms.exchange(hf_cache, sapt_jk, True)
        hf_data.update(exch)

        # Induction
        ind = sapt_jk_terms.induction(
            hf_cache,
            sapt_jk,
            True,
            maxiter=core.get_option("SAPT", "MAXITER"),
            conv=core.get_option("SAPT", "D_CONVERGENCE"))
        hf_data.update(ind)

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

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

        data["Delta HF Correction"] = core.get_variable("SAPT(DFT) Delta HF")

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

    # Set the primary functional
    core.set_global_option("DFT_FUNCTIONAL", core.get_option("SAPT", "SAPT_DFT_FUNCTIONAL"))
    core.set_local_option('SCF', 'REFERENCE', 'RKS')

    # Compute Monomer A wavefunction
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'dimer', 'monomerA')

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

    # Save the JK object
    core.IO.set_default_namespace('monomerA')
    wfn_A = scf_helper("SCF", molecule=monomerA, banner="SAPT(DFT): DFT Monomer A", **kwargs)
    data["DFT MONOMERA"] = core.get_variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)

    # Compute Monomer B wavefunction
    if (core.get_option('SCF', 'SCF_TYPE') == 'DF'):
        core.IO.change_file_namespace(97, 'monomerA', 'monomerB')

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

    core.IO.set_default_namespace('monomerB')
    wfn_B = scf_helper("SCF", molecule=monomerB, banner="SAPT(DFT): DFT Monomer B", **kwargs)
    data["DFT MONOMERB"] = core.get_variable("CURRENT ENERGY")

    core.set_global_option("DFT_GRAC_SHIFT", 0.0)

    # Print out the title and some information
    core.print_out("\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("         " + "SAPT(DFT): Intermolecular Interaction Segment".center(58) + "\n")
    core.print_out("\n")
    core.print_out("         " + "by Daniel G. A. Smith and Rob Parrish".center(58) + "\n")
    core.print_out("         ---------------------------------------------------------\n")
    core.print_out("\n")

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

    # Build cache and JK
    sapt_jk = wfn_B.jk()

    cache = sapt_jk_terms.build_sapt_jk_cache(wfn_A, wfn_B, sapt_jk, True)

    # Electostatics
    elst = sapt_jk_terms.electrostatics(cache, True)
    data.update(elst)

    # Exchange
    exch = sapt_jk_terms.exchange(cache, sapt_jk, True)
    data.update(exch)

    # Induction
    ind = sapt_jk_terms.induction(
        cache,
        sapt_jk,
        True,
        maxiter=core.get_option("SAPT", "MAXITER"),
        conv=core.get_option("SAPT", "D_CONVERGENCE"))
    data.update(ind)

    # Dispersion
    primary_basis = wfn_A.basisset()
    core.print_out("\n")
    aux_basis = core.BasisSet.build(sapt_dimer, "DF_BASIS_MP2",
                                    core.get_option("DFMP2", "DF_BASIS_MP2"), "RIFIT",
                                    core.get_global_option('BASIS'))
    fdds_disp = sapt_mp2_terms.df_fdds_dispersion(primary_basis, aux_basis, cache)
    data.update(fdds_disp)

    if core.get_option("SAPT", "SAPT_DFT_MP2_DISP_ALG") == "FISAPT":
        mp2_disp = sapt_mp2_terms.df_mp2_fisapt_dispersion(wfn_A, primary_basis, aux_basis, cache, do_print=True)
    else:
        mp2_disp = sapt_mp2_terms.df_mp2_sapt_dispersion(
            dimer_wfn, wfn_A, wfn_B, primary_basis, aux_basis, cache, do_print=True)
    data.update(mp2_disp)

    # Print out final data
    core.print_out("\n")
    core.print_out(print_sapt_dft_summary(data, "SAPT(DFT)"))

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

    core.tstop()

    return dimer_wfn
Exemplo n.º 28
0
def nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.
    This is a generalized univeral function for computing interaction and total quantities.

    :returns: *return type of func* |w--w| The data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    :type func: function
    :param func: ``energy`` || etc.

        Python function that accepts method_string and a molecule. Returns a
        energy, gradient, or Hessian as requested.

    :type method_string: string
    :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc.

        First argument, lowercase and usually unlabeled. Indicates the computational
        method to be passed to func.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :type return_wfn: :ref:`boolean <op_py_boolean>`
    :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr|

        Indicate to additionally return the :py:class:`~psi4.core.Wavefunction`
        calculation result as the second element of a tuple.

    :type bsse_type: string or list
    :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc.

        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this
        list is returned by this function. By default, this function is not called.

    :type max_nbody: int
    :param max_nbody: ``3`` || etc.

        Maximum n-body to compute, cannot exceed the number of fragments in the moleucle.

    :type ptype: string
    :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'``

        Type of the procedure passed in.

    :type return_total_data: :ref:`boolean <op_py_boolean>`
    :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr|

        If True returns the total data (energy/gradient/etc) of the system,
        otherwise returns interaction data.

    :type levels: dict
    :param levels: ``{1: 'ccsd(t)', 2: 'mp2', 'supersystem': 'scf'}`` || ``{1: 2, 2: 'ccsd(t)', 3: 'mp2'}`` || etc

        Dictionary of different levels of theory for different levels of expansion
        Note that method_string is not used in this case.
        supersystem computes all higher order n-body effects up to nfragments.

    :type embedding_charges: dict
    :param embedding_charges: ``{1: [-0.834, 0.417, 0.417], ..}``

        Dictionary of atom-centered point charges. keys: 1-based index of fragment, values: list of charges for each fragment.

    :type charge_method: string
    :param charge_method: ``scf/6-31g`` || ``b3lyp/6-31g*`` || etc

        Method to compute point charges for monomers. Overridden by embedding_charges if both are provided.

    :type charge_type: string
    :param charge_type: ``MULLIKEN_CHARGES`` || ``LOWDIN_CHARGES`` 

        Default is ``MULLIKEN_CHARGES``
    """

    # Initialize dictionaries for easy data passing
    metadata, component_results, nbody_results = {}, {}, {}

    # Parse some kwargs
    kwargs = p4util.kwargs_lower(kwargs)
    if kwargs.get('levels', False):
        return driver_nbody_helper.multi_level(func, **kwargs)
    metadata['ptype'] = kwargs.pop('ptype', None)
    metadata['return_wfn'] = kwargs.pop('return_wfn', False)
    metadata['return_total_data'] = kwargs.pop('return_total_data', False)
    metadata['molecule'] = kwargs.pop('molecule', core.get_active_molecule())
    metadata['molecule'].update_geometry()
    metadata['molecule'].fix_com(True)
    metadata['molecule'].fix_orientation(True)
    metadata['embedding_charges'] = kwargs.get('embedding_charges', False)
    metadata['kwargs'] = kwargs
    core.clean_variables()

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

    # Parse bsse_type, raise exception if not provided or unrecognized
    metadata['bsse_type_list'] = kwargs.pop('bsse_type')
    if metadata['bsse_type_list'] is None:
        raise ValidationError("N-Body GUFunc: Must pass a bsse_type")
    if not isinstance(metadata['bsse_type_list'], list):
        metadata['bsse_type_list'] = [metadata['bsse_type_list']]

    for num, btype in enumerate(metadata['bsse_type_list']):
        metadata['bsse_type_list'][num] = btype.lower()
        if btype.lower() not in ['cp', 'nocp', 'vmfc']:
            raise ValidationError("N-Body GUFunc: bsse_type '%s' is not recognized" % btype.lower())

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

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

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

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

    # Get compute list
    metadata = build_nbody_compute_list(metadata)

    # Compute N-Body components
    component_results = compute_nbody_components(func, method_string, metadata)

    # Assemble N-Body quantities
    nbody_results = assemble_nbody_components(metadata, component_results)

    # Build wfn and bind variables
    wfn = core.Wavefunction.build(metadata['molecule'], 'def2-svp')
    dicts = [
        'energies', 'ptype', 'intermediates', 'energy_body_dict', 'gradient_body_dict', 'hessian_body_dict', 'nbody',
        'cp_energy_body_dict', 'nocp_energy_body_dict', 'vmfc_energy_body_dict'
    ]
    if metadata['ptype'] == 'gradient':
        wfn.set_gradient(nbody_results['ret_ptype'])
        nbody_results['gradient_body_dict'] = nbody_results['ptype_body_dict']
    elif metadata['ptype'] == 'hessian':
        nbody_results['hessian_body_dict'] = nbody_results['ptype_body_dict']
        wfn.set_hessian(nbody_results['ret_ptype'])
        component_results_gradient = component_results.copy()
        component_results_gradient['ptype'] = component_results_gradient['gradients']
        metadata['ptype'] = 'gradient'
        nbody_results_gradient = assemble_nbody_components(metadata, component_results_gradient)
        wfn.set_gradient(nbody_results_gradient['ret_ptype'])
        nbody_results['gradient_body_dict'] = nbody_results_gradient['ptype_body_dict']

    for r in [component_results, nbody_results]:
        for d in r:
            if d in dicts:
                for var, value in r[d].items():
                    try:
                        wfn.set_scalar_variable(str(var), value)
                        core.set_scalar_variable(str(var), value)
                    except:
                        wfn.set_array_variable(d.split('_')[0].upper() + ' ' + str(var), core.Matrix.from_array(value))

    core.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])
    wfn.set_variable("CURRENT ENERGY", nbody_results['ret_energy'])

    if metadata['return_wfn']:
        return (nbody_results['ret_ptype'], wfn)
    else:
        return nbody_results['ret_ptype']
Exemplo n.º 29
0
def multi_level(func, **kwargs):
    """
    Use different levels of theory for different expansion levels
    See kwargs description in driver_nbody.nbody_gufunc

    :returns: *return type of func* |w--w| The data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    """
    from psi4.driver.driver_nbody import _print_nbody_energy, nbody_gufunc

    ptype = kwargs['ptype']
    return_wfn = kwargs.get('return_wfn', False)
    kwargs['return_wfn'] = True
    levels = {}
    for k, v in kwargs.pop('levels').items():
        if isinstance(k, str):
            levels[k.lower()] = v
        else:
            levels[k] = v
    supersystem = levels.pop('supersystem', False)
    molecule = kwargs.get('molecule', core.get_active_molecule())
    kwargs['bsse_type'] = [kwargs['bsse_type']] if isinstance(kwargs['bsse_type'], str) else kwargs['bsse_type']
    natoms = molecule.natom()

    # Initialize with zeros
    energy_result, gradient_result, hessian_result = 0, None, None
    energy_body_contribution = {b: {} for b in kwargs['bsse_type']}
    energy_body_dict = {b: {} for b in kwargs['bsse_type']}
    wfns = {}
    if ptype in ['gradient', 'hessian']:
        gradient_result = np.zeros((natoms, 3))
    if ptype == 'hessian':
        hessian_result = np.zeros((natoms * 3, natoms * 3))

    if kwargs.get('charge_method', False) and not kwargs.get('embedding_charges', False):
        kwargs['embedding_charges'] = compute_charges(kwargs['charge_method'],
                                      kwargs.get('charge_type', 'MULLIKEN_CHARGES').upper(), molecule)

    for n in sorted(levels)[::-1]:
        molecule.set_name('%i' % n)
        kwargs_copy = kwargs.copy()
        kwargs_copy['max_nbody'] = n
        energy_bsse_dict = {b: 0 for b in kwargs['bsse_type']}
        if isinstance(levels[n], str):
            # If a new level of theory is provided, compute contribution
            ret, wfn = nbody_gufunc(func, levels[n], **kwargs_copy)
            wfns[n] = wfn
        else:
            # For the n-body contribution, use available data from the higher order levels[n]-body
            wfn = wfns[levels[n]]

        for m in range(n - 1, n + 1):
            if m == 0: continue
            # Subtract the (n-1)-body contribution from the n-body contribution to get the n-body effect
            sign = (-1)**(1 - m // n)
            for b in kwargs['bsse_type']:
                energy_bsse_dict[b] += sign * wfn.variable('%i%s' % (m, b.lower()))
            if ptype in ['gradient', 'hessian']:
                gradient_result += sign * np.array(wfn.variable('GRADIENT ' + str(m)))
                # Keep 1-body contribution to compute interaction data
                if n == 1:
                    gradient1 = np.array(wfn.variable('GRADIENT ' + str(m)))
            if ptype == 'hessian':
                hessian_result += sign * np.array(wfn.variable('HESSIAN ' + str(m)))
                if n == 1:
                    hessian1 = np.array(wfn.variable('HESSIAN ' + str(m)))
        energy_result += energy_bsse_dict[kwargs['bsse_type'][0]]
        for b in kwargs['bsse_type']:
            energy_body_contribution[b][n] = energy_bsse_dict[b]

    if supersystem:
        # Super system recovers higher order effects at a lower level
        molecule.set_name('supersystem')
        kwargs_copy = kwargs.copy()
        kwargs_copy.pop('bsse_type')
        kwargs_copy.pop('ptype')
        ret, wfn_super = func(supersystem, **kwargs_copy)
        core.clean()
        kwargs_copy = kwargs.copy()
        kwargs_copy['bsse_type'] = 'nocp'
        kwargs_copy['max_nbody'] = max(levels)
        # Subtract lower order effects to avoid double counting
        ret, wfn = nbody_gufunc(func, supersystem, **kwargs_copy)
        energy_result += wfn_super.energy() - wfn.variable(str(max(levels)))
        for b in kwargs['bsse_type']:
            energy_body_contribution[b][molecule.nfragments()] = wfn_super.energy() - wfn.variable(
                str(max(levels)))

        if ptype in ['gradient', 'hessian']:
            gradient_result += np.array(wfn_super.gradient()) - np.array(wfn.variable('GRADIENT ' + str(max(levels))))
        if ptype == 'hessian':
            hessian_result += np.array(wfn_super.hessian()) - np.array(wfn.variable('HESSIAN ' + str(max(levels))))
        levels['supersystem'] = supersystem

    for b in kwargs['bsse_type']:
        for n in energy_body_contribution[b]:
            energy_body_dict[b][n] = sum(
                [energy_body_contribution[b][i] for i in range(1, n + 1) if i in energy_body_contribution[b]])

    is_embedded = kwargs.get('embedding_charges', False) or kwargs.get('charge_method', False)
    for b in kwargs['bsse_type']:
        _print_nbody_energy(energy_body_dict[b], '%s-corrected multilevel many-body expansion' % b.upper(),
                            is_embedded)

    if not kwargs['return_total_data']:
        # Remove monomer contribution for interaction data
        energy_result -= energy_body_dict[kwargs['bsse_type'][0]][1]
        if ptype in ['gradient', 'hessian']:
            gradient_result -= gradient1
        if ptype == 'hessian':
            hessian_result -= hessian1
    wfn_out = core.Wavefunction.build(molecule, 'def2-svp')
    core.set_variable("CURRENT ENERGY", energy_result)
    wfn_out.set_variable("CURRENT ENERGY", energy_result)
    if gradient_result is not None:
        wfn_out.set_gradient(core.Matrix.from_array(gradient_result))
    if hessian_result is not None:
        wfn_out.set_hessian(core.Matrix.from_array(hessian_result) )
    ptype_result = eval(ptype + '_result')
    for b in kwargs['bsse_type']:
        for i in energy_body_dict[b]:
            wfn_out.set_variable(str(i) + b, energy_body_dict[b][i])

    if kwargs['return_wfn']:
        return (ptype_result, wfn_out)
    else:
        return ptype_result
Exemplo n.º 30
0
Arquivo: frac.py Projeto: yxie326/psi4
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
Exemplo n.º 31
0
def multi_level(func, **kwargs):
    """
    Use different levels of theory for different expansion levels
    See kwargs description in driver_nbody.nbody_gufunc

    :returns: *return type of func* |w--w| The data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    """
    from psi4.driver.driver_nbody import nbody_gufunc
    from psi4.driver.driver_nbody import _print_nbody_energy

    ptype = kwargs['ptype']
    return_wfn = kwargs.get('return_wfn', False)
    kwargs['return_wfn'] = True
    levels = kwargs.pop('levels')
    for i in levels:
        if isinstance(i, str): levels[i.lower()] = levels.pop(i)
    supersystem = levels.pop('supersystem', False)
    molecule = kwargs.get('molecule', core.get_active_molecule())
    kwargs['bsse_type'] = [kwargs['bsse_type']] if isinstance(kwargs['bsse_type'], str) else kwargs['bsse_type']
    natoms = molecule.natom()

    # Initialize with zeros
    energy_result, gradient_result, hessian_result = 0, None, None
    energy_body_contribution = {b: {} for b in kwargs['bsse_type']}
    energy_body_dict = {b: {} for b in kwargs['bsse_type']}
    wfns = {}
    if ptype in ['gradient', 'hessian']:
        gradient_result = np.zeros((natoms, 3))
    if ptype == 'hessian':
        hessian_result = np.zeros((natoms * 3, natoms * 3))

    if kwargs.get('charge_method', False) and not kwargs.get('embedding_charges', False):
        kwargs['embedding_charges'] = compute_charges(kwargs['charge_method'],
                                      kwargs.get('charge_type', 'MULLIKEN_CHARGES').upper(), molecule)

    for n in sorted(levels)[::-1]:
        molecule.set_name('%i' %n)
        kwargs_copy = kwargs.copy()
        kwargs_copy['max_nbody'] = n
        energy_bsse_dict = {b: 0 for b in kwargs['bsse_type']}
        if isinstance(levels[n], str):
            # If a new level of theory is provided, compute contribution
            ret, wfn = nbody_gufunc(func, levels[n], **kwargs_copy)
            wfns[n] = wfn
        else:
            # For the n-body contribution, use available data from the higher order levels[n]-body
            wfn = wfns[levels[n]]

        for m in range(n - 1, n + 1):
            if m == 0: continue
            # Subtract the (n-1)-body contribution from the n-body contribution to get the n-body effect
            sign = (-1)**(1 - m // n)
            for b in kwargs['bsse_type']:
                energy_bsse_dict[b] += sign * wfn.variable('%i%s' % (m, b.lower()))
            if ptype in ['gradient', 'hessian']:
                gradient_result += sign * np.array(wfn.variable('GRADIENT ' + str(m)))
                # Keep 1-body contribution to compute interaction data
                if n == 1:
                    gradient1 = np.array(wfn.variable('GRADIENT ' + str(m)))
            if ptype == 'hessian':
                hessian_result += sign * np.array(wfn.variable('HESSIAN ' + str(m)))
                if n == 1:
                    hessian1 = np.array(wfn.variable('HESSIAN ' + str(m)))
        energy_result += energy_bsse_dict[kwargs['bsse_type'][0]]
        for b in kwargs['bsse_type']:
            energy_body_contribution[b][n] = energy_bsse_dict[b]

    if supersystem:
        # Super system recovers higher order effects at a lower level
        molecule.set_name('supersystem')
        kwargs_copy = kwargs.copy()
        kwargs_copy.pop('bsse_type')
        kwargs_copy.pop('ptype')
        ret, wfn_super = func(supersystem, **kwargs_copy)
        core.clean()
        kwargs_copy = kwargs.copy()
        kwargs_copy['bsse_type'] = 'nocp'
        kwargs_copy['max_nbody'] = max(levels)
        # Subtract lower order effects to avoid double counting
        ret, wfn = nbody_gufunc(func, supersystem, **kwargs_copy)
        energy_result += wfn_super.energy() - wfn.variable(str(max(levels)))
        for b in kwargs['bsse_type']:
            energy_body_contribution[b][molecule.nfragments()] = wfn_super.energy() - wfn.variable(
                str(max(levels)))

        if ptype in ['gradient', 'hessian']:
            gradient_result += np.array(wfn_super.gradient()) - np.array(wfn.variable('GRADIENT ' + str(max(levels))))
        if ptype == 'hessian':
            hessian_result += np.array(wfn_super.hessian()) - np.array(wfn.variable('HESSIAN ' + str(max(levels))))
        levels['supersystem'] = supersystem

    for b in kwargs['bsse_type']:
        for n in energy_body_contribution[b]:
            energy_body_dict[b][n] = sum(
                [energy_body_contribution[b][i] for i in range(1, n + 1) if i in energy_body_contribution[b]])

    is_embedded = kwargs.get('embedding_charges', False) or kwargs.get('charge_method', False)
    for b in kwargs['bsse_type']:
        _print_nbody_energy(energy_body_dict[b], '%s-corrected multilevel many-body expansion' % b.upper(),
                            is_embedded)

    if not kwargs['return_total_data']:
        # Remove monomer cotribution for interaction data
        energy_result -= energy_body_dict[kwargs['bsse_type'][0]][1]
        if ptype in ['gradient', 'hessian']:
            gradient_result -= gradient1
        if ptype == 'hessian':
            hessian_result -= hessian1
    wfn_out = core.Wavefunction.build(molecule, 'def2-svp')
    core.set_variable("CURRENT ENERGY", energy_result)
    wfn_out.set_variable("CURRENT ENERGY", energy_result)
    gradient_result = core.Matrix.from_array(gradient_result) if gradient_result is not None else None
    wfn_out.set_gradient(gradient_result)
    hessian_result = core.Matrix.from_array(hessian_result) if hessian_result is not None else None
    wfn_out.set_hessian(hessian_result)
    ptype_result = eval(ptype + '_result')
    for b in kwargs['bsse_type']:
        for i in energy_body_dict[b]:
            wfn_out.set_variable(str(i) + b, energy_body_dict[b][i])

    if kwargs['return_wfn']:
        return (ptype_result, wfn_out)
    else:
        return ptype_result
Exemplo n.º 32
0
Arquivo: frac.py Projeto: yxie326/psi4
def ip_fitting(name,
               omega_l=0.05,
               omega_r=2.5,
               omega_convergence=1.0e-3,
               maxiter=20,
               **kwargs):
    """Optimize DFT omega parameter for molecular system.

    Parameters
    ----------
    name : string or function
        DFT functional string name or function defining functional
        whose omega is to be optimized.
    omega_l : float, optional
        Minimum omega to be considered during fitting.
    omega_r : float, optional
        Maximum omega to be considered during fitting.
    molecule : :ref:`molecule <op_py_molecule>`, optional
        Target molecule (neutral) for which omega is to be tuned, if not last defined.
    omega_convergence : float, optional
        Threshold below which to consider omega converged. (formerly omega_tolerance)
    maxiter : int, optional
        Maximum number of iterations towards omega convergence.

    Returns
    -------
    float
        Optimal omega parameter.

    """
    optstash = p4util.OptionsState(['SCF', 'REFERENCE'], ['SCF', 'GUESS'],
                                   ['SCF', 'DF_INTS_IO'], ['SCF', 'DFT_OMEGA'],
                                   ['DOCC'], ['SOCC'])

    kwargs = p4util.kwargs_lower(kwargs)

    # By default, do not read previous 180 orbitals file
    read = False
    read180 = ''
    if 'read' in kwargs:
        read = True
        read180 = kwargs['read']

    if core.get_option('SCF', 'REFERENCE') != 'UKS':
        core.print_out(
            """  Requested procedure `ip_fitting` runs further calculations with UKS reference.\n"""
        )
        core.set_local_option('SCF', 'REFERENCE', 'UKS')

    # Make sure the molecule the user provided is the active one, and neutral
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    molecule.update_geometry()

    if molecule.molecular_charge() != 0:
        raise ValidationError(
            """IP Fitting requires neutral molecule to start.""")
    if molecule.schoenflies_symbol() != 'c1':
        core.print_out(
            """  Requested procedure `ip_fitting` does not make use of molecular symmetry: """
            """further calculations in C1 point group.\n""")
    molecule = molecule.clone()
    molecule.reset_point_group('c1')
    molecule.update_geometry()

    charge0 = molecule.molecular_charge()
    mult0 = molecule.multiplicity()

    # How many electrons are there?
    N = 0
    for A in range(molecule.natom()):
        N += molecule.Z(A)
    N -= charge0
    N = int(N)
    Nb = int((N - mult0 + 1) / 2)
    Na = int(N - Nb)

    # Work in the ot namespace for this procedure
    core.IO.set_default_namespace("ot")

    # Burn in to determine orbital eigenvalues
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        copy_file_to_scratch(read180, 'psi', 'ot', 180)
    core.set_local_option("SCF", "DF_INTS_IO", "SAVE")
    E, wfn = driver.energy('scf',
                           dft_functional=name,
                           return_wfn=True,
                           molecule=molecule,
                           banner='IP Fitting SCF: Burn-in',
                           **kwargs)
    core.set_local_option("SCF", "DF_INTS_IO", "LOAD")

    if not wfn.functional().is_x_lrc():
        raise ValidationError(
            """Not sensible to optimize omega for non-long-range-correction functional."""
        )

    # Determine H**O, to determine mult1
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Na == Nb:
        H**O = -Nb
    elif Nb == 0:
        H**O = Na
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        if E_a >= E_b:
            H**O = Na
        else:
            H**O = -Nb

    Na1 = Na
    Nb1 = Nb
    if H**O > 0:
        Na1 -= 1
    else:
        Nb1 -= 1

    charge1 = charge0 + 1
    mult1 = Na1 - Nb1 + 1

    omegas = []
    E0s = []
    E1s = []
    kIPs = []
    IPs = []
    types = []

    # Right endpoint
    core.set_local_option('SCF', 'DFT_OMEGA', omega_r)

    # Neutral
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180)

    molecule.set_molecular_charge(charge0)
    molecule.set_multiplicity(mult0)
    E0r, wfn = driver.energy('scf',
                             dft_functional=name,
                             return_wfn=True,
                             molecule=molecule,
                             banner='IP Fitting SCF: Neutral, Right Endpoint',
                             **kwargs)
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Nb == 0:
        E_HOMO = eps_a.np[int(Na - 1)]
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        E_HOMO = max(E_a, E_b)
    E_HOMOr = E_HOMO
    core.IO.change_file_namespace(180, "ot", "neutral")

    # Cation
    if read:
        core.set_local_option("SCF", "GUESS", "READ")
        p4util.copy_file_to_scratch(read180, 'psi', 'ot', 180)

    molecule.set_molecular_charge(charge1)
    molecule.set_multiplicity(mult1)
    E1r = driver.energy('scf',
                        dft_functional=name,
                        molecule=molecule,
                        banner='IP Fitting SCF: Cation, Right Endpoint',
                        **kwargs)
    core.IO.change_file_namespace(180, "ot", "cation")

    IPr = E1r - E0r
    kIPr = -E_HOMOr
    delta_r = IPr - kIPr

    if IPr > kIPr:
        raise ValidationError(
            """\n***IP Fitting Error: Right Omega limit should have kIP > IP: {} !> {}"""
            .format(kIPr, IPr))

    omegas.append(omega_r)
    types.append('Right Limit')
    E0s.append(E0r)
    E1s.append(E1r)
    IPs.append(IPr)
    kIPs.append(kIPr)

    # Use previous orbitals from here out
    core.set_local_option("SCF", "GUESS", "READ")

    # Left endpoint
    core.set_local_option('SCF', 'DFT_OMEGA', omega_l)

    # Neutral
    core.IO.change_file_namespace(180, "neutral", "ot")
    molecule.set_molecular_charge(charge0)
    molecule.set_multiplicity(mult0)
    core.set_global_option("DOCC", [Nb])
    core.set_global_option("SOCC", [Na - Nb])
    E0l, wfn = driver.energy('scf',
                             dft_functional=name,
                             return_wfn=True,
                             molecule=molecule,
                             banner='IP Fitting SCF: Neutral, Left Endpoint',
                             **kwargs)
    eps_a = wfn.epsilon_a()
    eps_b = wfn.epsilon_b()
    if Nb == 0:
        E_HOMO = eps_a.np[int(Na - 1)]
    else:
        E_a = eps_a.np[int(Na - 1)]
        E_b = eps_b.np[int(Nb - 1)]
        E_HOMO = max(E_a, E_b)
    E_HOMOl = E_HOMO
    core.IO.change_file_namespace(180, "ot", "neutral")

    # Cation
    core.IO.change_file_namespace(180, "cation", "ot")
    molecule.set_molecular_charge(charge1)
    molecule.set_multiplicity(mult1)
    core.set_global_option("DOCC", [Nb1])
    core.set_global_option("SOCC", [Na1 - Nb1])
    E1l = driver.energy('scf',
                        dft_functional=name,
                        molecule=molecule,
                        banner='IP Fitting SCF: Cation, Left Endpoint',
                        **kwargs)
    core.IO.change_file_namespace(180, "ot", "cation")

    IPl = E1l - E0l
    kIPl = -E_HOMOl
    delta_l = IPl - kIPl

    if IPl < kIPl:
        raise ValidationError(
            """\n***IP Fitting Error: Left Omega limit should have kIP < IP: {} !< {}"""
            .format(kIPl, IPl))

    omegas.append(omega_l)
    types.append('Left Limit')
    E0s.append(E0l)
    E1s.append(E1l)
    IPs.append(IPl)
    kIPs.append(kIPl)

    converged = False
    repeat_l = 0
    repeat_r = 0
    for step in range(maxiter):

        # Regula Falsi (modified)
        if repeat_l > 1:
            delta_l /= 2.0
        if repeat_r > 1:
            delta_r /= 2.0
        omega = -(omega_r - omega_l) / (delta_r - delta_l) * delta_l + omega_l
        core.set_local_option('SCF', 'DFT_OMEGA', omega)

        # Neutral
        core.IO.change_file_namespace(180, "neutral", "ot")
        molecule.set_molecular_charge(charge0)
        molecule.set_multiplicity(mult0)
        core.set_global_option("DOCC", [Nb])
        core.set_global_option("SOCC", [Na - Nb])
        E0, wfn = driver.energy(
            'scf',
            dft_functional=name,
            return_wfn=True,
            molecule=molecule,
            banner='IP Fitting SCF: Neutral, Omega = {:11.3E}'.format(omega),
            **kwargs)
        eps_a = wfn.epsilon_a()
        eps_b = wfn.epsilon_b()
        if Nb == 0:
            E_HOMO = eps_a.np[int(Na - 1)]
        else:
            E_a = eps_a.np[int(Na - 1)]
            E_b = eps_b.np[int(Nb - 1)]
            E_HOMO = max(E_a, E_b)
        core.IO.change_file_namespace(180, "ot", "neutral")

        # Cation
        core.IO.change_file_namespace(180, "cation", "ot")
        molecule.set_molecular_charge(charge1)
        molecule.set_multiplicity(mult1)
        core.set_global_option("DOCC", [Nb1])
        core.set_global_option("SOCC", [Na1 - Nb1])
        E1 = driver.energy(
            'scf',
            dft_functional=name,
            molecule=molecule,
            banner='IP Fitting SCF: Cation, Omega = {:11.3E}'.format(omega),
            **kwargs)
        core.IO.change_file_namespace(180, "ot", "cation")

        IP = E1 - E0
        kIP = -E_HOMO
        delta = IP - kIP

        if kIP > IP:
            omega_r = omega
            E0r = E0
            E1r = E1
            IPr = IP
            kIPr = kIP
            delta_r = delta
            repeat_r = 0
            repeat_l += 1
        else:
            omega_l = omega
            E0l = E0
            E1l = E1
            IPl = IP
            kIPl = kIP
            delta_l = delta
            repeat_l = 0
            repeat_r += 1

        omegas.append(omega)
        types.append('Regula-Falsi')
        E0s.append(E0)
        E1s.append(E1)
        IPs.append(IP)
        kIPs.append(kIP)

        # Termination
        if abs(omega_l - omega_r) < omega_convergence:
            converged = True
            break

    core.IO.set_default_namespace("")
    core.print_out("""\n    ==> IP Fitting Results <==\n\n""")

    core.print_out("""     => Occupation Determination <= \n\n""")
    core.print_out("""              %6s %6s %6s %6s %6s %6s\n""" %
                   ('N', 'Na', 'Nb', 'Charge', 'Mult', 'H**O'))
    core.print_out("""     Neutral: %6d %6d %6d %6d %6d %6d\n""" %
                   (N, Na, Nb, charge0, mult0, H**O))
    core.print_out("""     Cation:  %6d %6d %6d %6d %6d\n\n""" %
                   (N - 1, Na1, Nb1, charge1, mult1))

    core.print_out("""     => Regula Falsi Iterations <=\n\n""")
    core.print_out("""    %3s %11s %14s %14s %14s %s\n""" %
                   ('N', 'Omega', 'IP', 'kIP', 'Delta', 'Type'))
    for k in range(len(omegas)):
        core.print_out(
            """    %3d %11.3E %14.6E %14.6E %14.6E %s\n""" %
            (k + 1, omegas[k], IPs[k], kIPs[k], IPs[k] - kIPs[k], types[k]))

    optstash.restore()
    if converged:
        core.print_out("""\n    IP Fitting Converged\n""")
        core.print_out("""    Final omega = %14.6E\n""" %
                       ((omega_l + omega_r) / 2))
        core.print_out(
            """\n    "M,I. does the dying. Fleet just does the flying."\n""")
        core.print_out("""            -Starship Troopers\n""")

    else:
        raise ConvergenceError("""IP Fitting """, step + 1)

    return ((omega_l + omega_r) / 2)
Exemplo n.º 33
0
def nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.
    This is a generalized univeral function for computing interaction quantities.

    :returns: *return type of func* |w--w| The interaction data.

    :returns: (*float*, :ref:`Wavefunction<sec:psimod_Wavefunction>`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    :type func: function
    :param func: ``energy`` || etc.

        Python function that accepts method_string and a molecule. Returns a
        energy, gradient, or Hessian as requested.

    :type method_string: string
    :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc.

        First argument, lowercase and usually unlabeled. Indicates the computational
        method to be passed to func.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :type return_wfn: :ref:`boolean <op_py_boolean>`
    :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr|

        Indicate to additionally return the :ref:`Wavefunction<sec:psimod_Wavefunction>`
        calculation result as the second element of a tuple.

    :type bsse_type: string or list
    :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc.

        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this
        list is returned by this function. By default, this function is not called.

    :type max_nbody: int
    :param max_nbody: ``3`` || etc.

        Maximum n-body to compute, cannot exceed the number of fragments in the moleucle.

    :type ptype: string
    :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'``

        Type of the procedure passed in.

    :type return_total_data: :ref:`boolean <op_py_boolean>`
    :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr|

        If True returns the total data (energy/gradient/etc) of the system,
        otherwise returns interaction data.
    """

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    molecule_total_atoms = sum(fragment_size_dict.values())

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

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

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

            core.clean()

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

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

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

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

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

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

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

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

        # Do ptype
        if ptype != 'energy':
            _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n],
                                    fragment_slice_dict, fragment_size_dict,
                                    cp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n],
                                    fragment_slice_dict, fragment_size_dict,
                                    nocp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype,
                                    ptype_dict,
                                    vmfc_level_list[n],
                                    fragment_slice_dict,
                                    fragment_size_dict,
                                    vmfc_ptype_by_level[n],
                                    vmfc=True)

    # Compute cp energy and ptype
    if do_cp:
        for n in nbody_range:
            if n == max_frag:
                cp_energy_body_dict[n] = cp_energy_by_level[n]
                if ptype != 'energy':
                    cp_ptype_body_dict[n][:] = cp_ptype_by_level[n]
                continue

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            ret_ptype = core.Matrix.from_array(np_final_ptype)
    else:
        ret_ptype = ret_energy

    # Build and set a wavefunction
    wfn = core.Wavefunction.build(molecule, 'sto-3g')
    wfn.cdict["nbody_energy"] = energies_dict
    wfn.cdict["nbody_ptype"] = ptype_dict
    wfn.cdict["nbody_body_energy"] = energy_body_dict
    wfn.cdict["nbody_body_ptype"] = ptype_body_dict

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

    core.set_variable("CURRENT ENERGY", ret_energy)

    if return_wfn:
        return (ret_ptype, wfn)
    else:
        return ret_ptype
Exemplo n.º 34
0
def run_sf_sapt(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")

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

    core.print_out("  ==> Algorithm <==\n\n")
    core.print_out("   JK Algorithm            %12s\n" %
                   core.get_option("SCF", "SCF_TYPE"))
    core.print_out("\n")
    core.print_out("   Required computations:\n")
    core.print_out("     HF  (Monomer A)\n")
    core.print_out("     HF  (Monomer B)\n")
    core.print_out("\n")

    if (core.get_option('SCF', 'REFERENCE') != 'ROHF'):
        raise ValidationError(
            'Spin-Flip SAPT currently only supports restricted open-shell references.'
        )

    # Run the two monomer computations
    core.IO.set_default_namespace('dimer')
    data = {}

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

    # Compute dimer wavefunction
    wfn_A = scf_helper("SCF",
                       molecule=monomerA,
                       banner="SF-SAPT: HF Monomer A",
                       **kwargs)

    core.set_global_option("SAVE_JK", True)
    wfn_B = scf_helper("SCF",
                       molecule=monomerB,
                       banner="SF-SAPT: HF Monomer B",
                       **kwargs)
    sapt_jk = wfn_B.jk()
    core.set_global_option("SAVE_JK", False)
    core.print_out("\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("         " +
                   "Spin-Flip SAPT Exchange and Electrostatics".center(58) +
                   "\n")
    core.print_out("\n")
    core.print_out("         " +
                   "by Daniel G. A. Smith and Konrad Patkowski".center(58) +
                   "\n")
    core.print_out(
        "         ---------------------------------------------------------\n")
    core.print_out("\n")

    sf_data = sapt_sf_terms.compute_sapt_sf(sapt_dimer, sapt_jk, wfn_A, wfn_B)

    # Print the results
    core.print_out("   Spin-Flip SAPT Results\n")
    core.print_out("  " + "-" * 103 + "\n")

    for key, value in sf_data.items():
        value = sf_data[key]
        print_vals = (key, value * 1000, value * constants.hartree2kcalmol,
                      value * constants.hartree2kJmol)
        string = "    %-26s % 15.8f [mEh] % 15.8f [kcal/mol] % 15.8f [kJ/mol]\n" % print_vals
        core.print_out(string)
    core.print_out("  " + "-" * 103 + "\n\n")

    dimer_wfn = core.Wavefunction.build(sapt_dimer, wfn_A.basisset())

    # Set variables
    psivar_tanslator = {
        "Elst10": "SAPT ELST ENERGY",
        "Exch10(S^2) [diagonal]": "SAPT EXCH10(S^2),DIAGONAL ENERGY",
        "Exch10(S^2) [off-diagonal]": "SAPT EXCH10(S^2),OFF-DIAGONAL ENERGY",
        "Exch10(S^2) [highspin]": "SAPT EXCH10(S^2),HIGHSPIN ENERGY",
    }

    for k, v in sf_data.items():
        psi_k = psivar_tanslator[k]

        dimer_wfn.set_variable(psi_k, v)
        core.set_variable(psi_k, v)

    # Copy over highspin
    core.set_variable("SAPT EXCH ENERGY", sf_data["Exch10(S^2) [highspin]"])

    core.tstop()

    return dimer_wfn
Exemplo n.º 35
0
def nbody_gufunc(func, method_string, **kwargs):
    """
    Computes the nbody interaction energy, gradient, or Hessian depending on input.
    This is a generalized univeral function for computing interaction quantities.

    :returns: *return type of func* |w--w| The interaction data.

    :returns: (*float*, :py:class:`~psi4.core.Wavefunction`) |w--w| interaction data and wavefunction with energy/gradient/hessian set appropriately when **return_wfn** specified.

    :type func: function
    :param func: ``energy`` || etc.

        Python function that accepts method_string and a molecule. Returns a
        energy, gradient, or Hessian as requested.

    :type method_string: string
    :param method_string: ``'scf'`` || ``'mp2'`` || ``'ci5'`` || etc.

        First argument, lowercase and usually unlabeled. Indicates the computational
        method to be passed to func.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :type return_wfn: :ref:`boolean <op_py_boolean>`
    :param return_wfn: ``'on'`` || |dl| ``'off'`` |dr|

        Indicate to additionally return the :py:class:`~psi4.core.Wavefunction`
        calculation result as the second element of a tuple.

    :type bsse_type: string or list
    :param bsse_type: ``'cp'`` || ``['nocp', 'vmfc']`` || |dl| ``None`` |dr| || etc.

        Type of BSSE correction to compute: CP, NoCP, or VMFC. The first in this
        list is returned by this function. By default, this function is not called.

    :type max_nbody: int
    :param max_nbody: ``3`` || etc.

        Maximum n-body to compute, cannot exceed the number of fragments in the moleucle.

    :type ptype: string
    :param ptype: ``'energy'`` || ``'gradient'`` || ``'hessian'``

        Type of the procedure passed in.

    :type return_total_data: :ref:`boolean <op_py_boolean>`
    :param return_total_data: ``'on'`` || |dl| ``'off'`` |dr|

        If True returns the total data (energy/gradient/etc) of the system,
        otherwise returns interaction data.
    """

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

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

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

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

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

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

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

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

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


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


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

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

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

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

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


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

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

    molecule_total_atoms = sum(fragment_size_dict.values())

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

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

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

            core.clean()

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

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

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

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

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


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

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

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


        # Do ptype
        if ptype != 'energy':
            _sum_cluster_ptype_data(ptype, ptype_dict, cp_compute_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      cp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, nocp_compute_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      nocp_ptype_by_level[n])
            _sum_cluster_ptype_data(ptype, ptype_dict, vmfc_level_list[n],
                                      fragment_slice_dict, fragment_size_dict,
                                      vmfc_ptype_by_level[n], vmfc=True)

    # Compute cp energy and ptype
    if do_cp:
        for n in nbody_range:
            if n == max_frag:
                cp_energy_body_dict[n] = cp_energy_by_level[n]
                if ptype != 'energy':
                    cp_ptype_body_dict[n][:] = cp_ptype_by_level[n]
                continue

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

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

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

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

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

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

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

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

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


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

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

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


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


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

            ret_ptype = core.Matrix.from_array(np_final_ptype)
    else:
        ret_ptype = ret_energy

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

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

    core.set_variable("CURRENT ENERGY", ret_energy)

    if return_wfn:
        return (ret_ptype, wfn)
    else:
        return ret_ptype
Exemplo n.º 36
0
def anharmonicity(rvals: List, energies: List, plot_fit: str = '', mol = None) -> Dict:
    """Generates spectroscopic constants for a diatomic molecules.
       Fits a diatomic potential energy curve using a weighted least squares approach
       (c.f. https://doi.org/10.1063/1.4862157, particularly eqn. 7), locates the minimum
       energy point, and then applies second order vibrational perturbation theory to obtain spectroscopic
       constants.  Any number of points greater than 4 may be provided, and they should bracket the minimum.
       The data need not be evenly spaced, and can be provided in any order.  The data are weighted such that
       those closest to the minimum have highest impact.

       A dictionary with the following keys, which correspond to spectroscopic constants, is returned:

       :param rvals: The bond lengths (in Angstrom) for which energies are
           provided, of length at least 5 and equal to the length of the energies array

       :param energies: The energies (Eh) computed at the bond lengths in the rvals list

       :param plot_fit: A string describing where to save a plot of the harmonic and anharmonic fits, the
           inputted data points, re, r0 and the first few energy levels, if matplotlib
           is available.  Set to 'screen' to generate an interactive plot on the screen instead. If a filename is
           provided, the image type is determined by the extension; see matplotlib for supported file types.

       :returns: (*dict*) Keys: "re", "r0", "we", "wexe", "nu", "ZPVE(harmonic)", "ZPVE(anharmonic)", "Be", "B0", "ae", "De"
                 corresponding to the spectroscopic constants in cm-1
    """

    angstrom_to_bohr = 1.0 / constants.bohr2angstroms
    angstrom_to_meter = 10e-10

    # Make sure the input is valid
    if len(rvals) != len(energies):
        raise ValidationError("The number of energies must match the number of distances")
    npoints = len(rvals)
    if npoints < 5:
        raise ValidationError("At least 5 data points must be provided to compute anharmonicity")
    core.print_out("\n\nPerforming a fit to %d data points\n" % npoints)

    # Sort radii and values first from lowest to highest radius
    indices = np.argsort(rvals)
    rvals = np.array(rvals)[indices]
    energies = np.array(energies)[indices]

    # Make sure the molecule the user provided is the active one
    molecule = mol or core.get_active_molecule()
    molecule.update_geometry()
    natoms = molecule.natom()
    if natoms != 2:
        raise Exception("The current molecule must be a diatomic for this code to work!")
    m1 = molecule.mass(0)
    m2 = molecule.mass(1)

    # Find rval of the minimum of energies, check number of points left and right
    min_index = np.argmin(energies)
    if min_index < 3 :
        core.print_out("\nWarning: fewer than 3 points provided with a r < r(min(E))!\n")
    if min_index >= len(energies) - 3:
        core.print_out("\nWarning: fewer than 3 points provided with a r > r(min(E))!\n")

    # Optimize the geometry, refitting the surface around each new geometry
    core.print_out("\nOptimizing geometry based on current surface:\n\n")
    re = rvals[min_index]
    maxit = 30
    thres = 1.0e-9
    for i in range(maxit):
        derivs = least_squares_fit_polynomial(rvals,energies,localization_point=re)
        e,g,H = derivs[0:3]
        core.print_out("       E = %20.14f, x = %14.7f, grad = %20.14f\n" % (e, re, g))
        if abs(g) < thres:
            break
        re -= g/H
        if i == maxit-1:
            raise ConvergenceError("diatomic geometry optimization", maxit)
    core.print_out(" Final E = %20.14f, x = %14.7f, grad = %20.14f\n" % (e, re, g))
    if re < min(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a lower range of r values.")
    if re > max(rvals):
        raise Exception("Minimum energy point is outside range of points provided.  Use a higher range of r values.")

    # Convert to convenient units, and compute spectroscopic constants
    d0,d1,d2,d3,d4 = derivs*constants.hartree2aJ
    core.print_out("\nEquilibrium Energy %20.14f Hartrees\n" % e)
    core.print_out("Gradient           %20.14f\n" % g)
    core.print_out("Quadratic Force Constant %14.7f MDYNE/A\n" % d2)
    core.print_out("Cubic Force Constant     %14.7f MDYNE/A**2\n" % d3)
    core.print_out("Quartic Force Constant   %14.7f MDYNE/A**3\n" % d4)

    hbar = constants.h / (2.0 * np.pi)
    mu = ((m1*m2)/(m1+m2))*constants.amu2kg
    we = 5.3088375e-11 * np.sqrt(d2/mu)
    wexe = (1.2415491e-6)*(we/d2)**2 * ((5.0*d3*d3)/(3.0*d2)-d4)

    # Rotational constant: Be
    I = ((m1*m2)/(m1+m2)) * constants.amu2kg * (re * angstrom_to_meter)**2
    B = constants.h / (8.0 * np.pi**2 * constants.c * I)

    # alpha_e and quartic centrifugal distortion constant
    ae = -(6.0 * B**2 / we) * ((1.05052209e-3*we*d3)/(np.sqrt(B * d2**3))+1.0)
    de = 4.0*B**3 / we**2

    # B0 and r0 (plus re check using Be)
    B0 = B - ae / 2.0
    r0 = np.sqrt(constants.h / (8.0 * np.pi**2 * mu * constants.c * B0))
    recheck = np.sqrt(constants.h / (8.0 * np.pi**2 * mu * constants.c * B))
    r0 /= angstrom_to_meter
    recheck /= angstrom_to_meter

    # Fundamental frequency nu
    nu = we - 2.0 * wexe
    zpve_nu = 0.5 * we - 0.25 * wexe
    zpve_we = 0.5 * we

    # Generate pretty pictures, if requested
    if(plot_fit):
        try:
            import matplotlib.pyplot as plt
        except ImportError:
            msg = "\n\tPlot not generated; matplotlib is not installed on this machine.\n\n"
            print(msg)
            core.print_out(msg)

        # Correct the derivatives for the missing factorial prefactors
        dvals = np.zeros(5)
        dvals[0:5] = derivs[0:5]
        dvals[2] /= 2
        dvals[3] /= 6
        dvals[4] /= 24

        # Default plot range, before considering energy levels
        minE = np.min(energies)
        maxE = np.max(energies)
        minR = np.min(rvals)
        maxR = np.max(rvals)

        # Plot vibrational energy levels
        we_au = we / constants.hartree2wavenumbers
        wexe_au = wexe / constants.hartree2wavenumbers
        coefs2 = [ dvals[2], dvals[1], dvals[0] ]
        coefs4 = [ dvals[4], dvals[3], dvals[2], dvals[1], dvals[0] ]
        for n in range(3):
            Eharm = we_au*(n+0.5)
            Evpt2 = Eharm - wexe_au*(n+0.5)**2
            coefs2[-1] = -Eharm
            coefs4[-1] = -Evpt2
            roots2 = np.roots(coefs2)
            roots4 = np.roots(coefs4)
            xvals2 = roots2 + re
            xvals4 = np.choose(np.where(np.isreal(roots4)), roots4)[0].real + re
            Eharm += dvals[0]
            Evpt2 += dvals[0]
            plt.plot(xvals2, [Eharm, Eharm], 'b', linewidth=1)
            plt.plot(xvals4, [Evpt2, Evpt2], 'g', linewidth=1)
            maxE = Eharm
            maxR = np.max([xvals2,xvals4])
            minR = np.min([xvals2,xvals4])

        # Find ranges for the plot
        dE = maxE - minE
        minE -= 0.2*dE
        maxE += 0.4*dE
        dR = maxR - minR
        minR -= 0.2*dR
        maxR += 0.2*dR

        # Generate the fitted PES
        xpts = np.linspace(minR, maxR, 1000)
        xrel = xpts - re
        xpows = xrel[:, None] ** range(5)
        fit2 = np.einsum('xd,d', xpows[:,0:3], dvals[0:3])
        fit4 = np.einsum('xd,d', xpows, dvals)

        # Make / display the plot
        plt.plot(xpts, fit2, 'b', linewidth=2.5, label='Harmonic (quadratic) fit')
        plt.plot(xpts, fit4, 'g', linewidth=2.5, label='Anharmonic (quartic) fit')
        plt.plot([re, re], [minE, maxE], 'b--', linewidth=0.5)
        plt.plot([r0, r0], [minE, maxE], 'g--', linewidth=0.5)
        plt.scatter(rvals, energies, c='Black', linewidth=3, label='Input Data')
        plt.legend()

        plt.xlabel('Bond length (Angstroms)')
        plt.ylabel('Energy (Eh)')
        plt.xlim(minR, maxR)
        plt.ylim(minE, maxE)
        if plot_fit == 'screen':
            plt.show()
        else:
            plt.savefig(plot_fit)
            core.print_out("\n\tPES fit saved to %s.\n\n" % plot_fit)

    core.print_out("\nre       = %10.6f A  check: %10.6f\n" % (re, recheck))
    core.print_out("r0       = %10.6f A\n" % r0)
    core.print_out("E at re  = %17.10f Eh\n" % e)
    core.print_out("we       = %10.4f cm-1\n" % we)
    core.print_out("wexe     = %10.4f cm-1\n" % wexe)
    core.print_out("nu       = %10.4f cm-1\n" % nu)
    core.print_out("ZPVE(we) = %10.4f cm-1\n" % zpve_we)
    core.print_out("ZPVE(nu) = %10.4f cm-1\n" % zpve_nu)
    core.print_out("Be       = %10.4f cm-1\n" % B)
    core.print_out("B0       = %10.4f cm-1\n" % B0)
    core.print_out("ae       = %10.4f cm-1\n" % ae)
    core.print_out("De       = %10.7f cm-1\n" % de)
    results = {
               "re"               :  re,
               "r0"               :  r0,
               "we"               :  we,
               "wexe"             :  wexe,
               "nu"               :  nu,
               "E(re)"            :  e,
               "ZPVE(harmonic)"   :  zpve_we,
               "ZPVE(anharmonic)" :  zpve_nu,
               "Be"               :  B,
               "B0"               :  B0,
               "ae"               :  ae,
               "De"               :  de
              }
    return results
Exemplo n.º 37
0
def auto_fragments(**kwargs):
    r"""Detects fragments if the user does not supply them.
    Currently only used for the WebMO implementation of SAPT.

    :returns: :py:class:`~psi4.core.Molecule`) |w--w| fragmented molecule.

    :type molecule: :ref:`molecule <op_py_molecule>`
    :param molecule: ``h2o`` || etc.

        The target molecule, if not the last molecule defined.

    :examples:

    >>> # [1] replicates with cbs() the simple model chemistry scf/cc-pVDZ: set basis cc-pVDZ energy('scf')
    >>> molecule mol {\nH 0.0 0.0 0.0\nH 2.0 0.0 0.0\nF 0.0 1.0 0.0\nF 2.0 1.0 0.0\n}
    >>> print mol.nfragments()  # 1
    >>> fragmol = auto_fragments()
    >>> print fragmol.nfragments()  # 2

    """
    # Make sure the molecule the user provided is the active one
    molecule = kwargs.pop('molecule', core.get_active_molecule())
    molecule.update_geometry()
    molname = molecule.name()

    geom = molecule.save_string_xyz()

    numatoms = molecule.natom()
    VdW = [1.2, 1.7, 1.5, 1.55, 1.52, 1.9, 1.85, 1.8]

    symbol = list(range(numatoms))
    X = [0.0] * numatoms
    Y = [0.0] * numatoms
    Z = [0.0] * numatoms

    Queue = []
    White = []
    Black = []
    F = geom.split('\n')
    for f in range(numatoms):
        A = F[f + 1].split()
        symbol[f] = A[0]
        X[f] = float(A[1])
        Y[f] = float(A[2])
        Z[f] = float(A[3])
        White.append(f)
    Fragment = [[] for i in range(numatoms)]  # stores fragments

    start = 0  # starts with the first atom in the list
    Queue.append(start)
    White.remove(start)

    frag = 0

    while ((len(White) > 0)
           or (len(Queue) > 0)):  # Iterates to the next fragment
        while (len(Queue) > 0):  # BFS within a fragment
            for u in Queue:  # find all nearest Neighbors
                #   (still coloured white) to vertex u
                for i in White:
                    Distance = math.sqrt((X[i] - X[u]) * (X[i] - X[u]) +
                                         (Y[i] - Y[u]) * (Y[i] - Y[u]) +
                                         (Z[i] - Z[u]) * (Z[i] - Z[u]))
                    if Distance < _autofragment_convert(
                            u, symbol) + _autofragment_convert(i, symbol):
                        Queue.append(i)  # if you find you, put it in the que
                        White.remove(
                            i)  # and remove it from the untouched list
            Queue.remove(u)  # remove focus from Queue
            Black.append(u)
            Fragment[frag].append(int(u +
                                      1))  # add to group (adding 1 to start
            #   list at one instead of zero)

        if (len(White) != 0):  # cant move White->Queue if no more exist
            Queue.append(White[0])
            White.remove(White[0])
        frag += 1

    new_geom = """\n"""
    for i in Fragment[0]:
        new_geom = new_geom + F[i].lstrip() + """\n"""
    new_geom = new_geom + """--\n"""
    for j in Fragment[1]:
        new_geom = new_geom + F[j].lstrip() + """\n"""
    new_geom = new_geom + """units angstrom\n"""

    moleculenew = core.Molecule.create_molecule_from_string(new_geom)
    moleculenew.set_name(molname)
    moleculenew.update_geometry()
    moleculenew.print_cluster()
    core.print_out("""  Exiting auto_fragments\n""")

    return moleculenew