Ejemplo n.º 1
0
def _validate_damping():
    """Sanity-checks DAMPING control options

    Raises
    ------
    ValidationError
        If any of |scf__damping_percentage|, |scf__damping_convergence|
        don't play well together.

    Returns
    -------
    bool
        Whether DAMPING is enabled during scf.

    """
    # Q: I changed the enabled criterion get_option <-- has_option_changed
    enabled = (core.get_option('SCF', 'DAMPING_PERCENTAGE') > 0.0)
    if enabled:
        parameter = core.get_option('SCF', "DAMPING_PERCENTAGE")
        if parameter < 0.0 or parameter > 100.0:
            raise ValidationError(
                'SCF DAMPING_PERCENTAGE ({}) must be between 0 and 100'.format(
                    parameter))

        stop = core.get_option('SCF', 'DAMPING_CONVERGENCE')
        if stop < 0.0:
            raise ValidationError(
                'SCF DAMPING_CONVERGENCE ({}) must be > 0'.format(stop))

    return enabled
Ejemplo n.º 2
0
def _validate_soscf():
    """Sanity-checks SOSCF control options

    Raises
    ------
    ValidationError
        If any of |scf__soscf|, |scf__soscf_start_convergence|,
        |scf__soscf_min_iter|, |scf__soscf_max_iter| don't play well together.

    Returns
    -------
    bool
        Whether SOSCF is enabled during scf.

    """
    enabled = core.get_option('SCF', 'SOSCF')
    if enabled:
        start = core.get_option('SCF', 'SOSCF_START_CONVERGENCE')
        if start < 0.0:
            raise ValidationError('SCF SOSCF_START_CONVERGENCE ({}) must be positive'.format(start))

        miniter = core.get_option('SCF', 'SOSCF_MIN_ITER')
        if miniter < 1:
            raise ValidationError('SCF SOSCF_MIN_ITER ({}) must be at least 1'.format(miniter))

        maxiter = core.get_option('SCF', 'SOSCF_MAX_ITER')
        if maxiter < miniter:
            raise ValidationError(
                'SCF SOSCF_MAX_ITER ({}) must be at least SOSCF_MIN_ITER ({})'.format(maxiter, miniter))

        conv = core.get_option('SCF', 'SOSCF_CONV')
        if conv < 1.e-10:
            raise ValidationError('SCF SOSCF_CONV ({}) must be achievable'.format(conv))

    return enabled
Ejemplo n.º 3
0
def _validate_diis():
    """Sanity-checks DIIS control options

    Raises
    ------
    ValidationError
        If any of |scf__diis|, |scf__diis_start|,
        |scf__diis_min_vecs|, |scf__diis_max_vecs| don't play well together.

    Returns
    -------
    bool
        Whether DIIS is enabled during scf.

    """
    enabled = bool(core.get_option('SCF', 'DIIS'))
    if enabled:
        start = core.get_option('SCF', 'DIIS_START')
        if start < 1:
            raise ValidationError(
                'SCF DIIS_START ({}) must be at least 1'.format(start))

        minvecs = core.get_option('SCF', 'DIIS_MIN_VECS')
        if minvecs < 1:
            raise ValidationError(
                'SCF DIIS_MIN_VECS ({}) must be at least 1'.format(minvecs))

        maxvecs = core.get_option('SCF', 'DIIS_MAX_VECS')
        if maxvecs < minvecs:
            raise ValidationError(
                'SCF DIIS_MAX_VECS ({}) must be at least DIIS_MIN_VECS ({})'.
                format(maxvecs, minvecs))

    return enabled
Ejemplo n.º 4
0
    def __init__(self, name_hint=None, level_hint=None, param_tweaks=None, **kwargs):
        from .dft import dashcoeff_supplement
        self.dashcoeff_supplement = dashcoeff_supplement

        resolved = intf_dftd3.from_arrays(
            name_hint=name_hint,
            level_hint=level_hint,
            param_tweaks=param_tweaks,
            dashcoeff_supplement=self.dashcoeff_supplement)
        self.fctldash = resolved['fctldash']
        self.dashlevel = resolved['dashlevel']
        self.dashparams = resolved['dashparams']
        self.description = intf_dftd3.dashcoeff[self.dashlevel]['description']
        self.ordered_params = intf_dftd3.dashcoeff[self.dashlevel]['default'].keys()
        self.dashlevel_citation = intf_dftd3.dashcoeff[self.dashlevel]['citation']
        self.dashparams_citation = resolved['dashparams_citation']

        engine = kwargs.pop('engine', None)
        if engine is None:
            self.engine = _capable_engines_for_disp[self.dashlevel][0]
        else:
            if self.dashlevel in _engine_can_do[engine]:
                self.engine = engine
            else:
                raise ValidationError("""This little engine ({}) can't ({})""".format(engine, self.dashlevel))

        if self.engine == 'libdisp':
            self.disp = core.Dispersion.build(self.dashlevel, **resolved['dashparams'])
Ejemplo n.º 5
0
def get_pe_options():
    if core.get_option('SCF', 'PCM'):
        raise ValidationError(
            """Error: 3-layer QM/PE/PCM not implemented.\n""")
    rmin = core.get_option('PE', 'BORDER_RMIN')
    if core.get_option('PE', 'BORDER_RMIN_UNIT').upper() == "AA":
        rmin *= 1.0 / constants.bohr2angstroms

    # potfile option can be filename or contents
    potfile_keyword = core.get_option('PE', 'POTFILE')
    if "@COORDINATES" in potfile_keyword:
        fl = NamedTemporaryFile(mode="w+t", delete=False)
        fl.write(potfile_keyword)
        fl.close()
        potfile_name = fl.name
    else:
        potfile_name = potfile_keyword

    pol_embed_options = {
        "potfile":
        potfile_name,
        "iso_pol":
        core.get_option('PE', 'ISOTROPIC_POL'),
        "induced_thresh":
        core.get_option('PE', 'INDUCED_CONVERGENCE'),
        "maxiter":
        core.get_option('PE', 'MAXITER'),
        # tree options
        "summation_induced_fields":
        core.get_option('PE', 'SUMMATION_FIELDS').lower(),
        "tree_expansion_order":
        core.get_option('PE', 'TREE_EXPANSION_ORDER'),
        "theta":
        core.get_option('PE', 'TREE_THETA'),
        # damping options
        "damp_induced":
        core.get_option('PE', 'DAMP_INDUCED'),
        "damping_factor_induced":
        core.get_option('PE', 'DAMPING_FACTOR_INDUCED'),
        "damp_multipole":
        core.get_option('PE', 'DAMP_MULTIPOLE'),
        "damping_factor_multipole":
        core.get_option('PE', 'DAMPING_FACTOR_MULTIPOLE'),
        "pe_border":
        core.get_option('PE', 'BORDER'),
        "border_type":
        core.get_option('PE', 'BORDER_TYPE').lower(),
        "border_rmin":
        rmin,
        "border_nredist":
        core.get_option('PE', 'BORDER_N_REDIST'),
        "border_redist_order":
        core.get_option('PE', 'BORDER_REDIST_ORDER'),
        "border_redist_pol":
        core.get_option('PE', 'BORDER_REDIST_POL'),
        # PE(ECP)
        "pe_ecp":
        core.get_option('PE', 'PE_ECP'),
    }
    return pol_embed_options
Ejemplo n.º 6
0
def get_pe_options():
    if core.get_option('SCF', 'PCM'):
        raise ValidationError("""Error: 3-layer QM/PE/PCM not implemented.\n""")
    potfile_name = core.get_option('PE', 'POTFILE')
    pol_embed_options = cppe.PeOptions()
    pol_embed_options.potfile = potfile_name
    pol_embed_options.induced_thresh = core.get_option('PE', 'INDUCED_CONVERGENCE')
    pol_embed_options.iso_pol = core.get_option('PE', 'ISOTROPIC_POL')

    pol_embed_options.do_diis = core.get_option('PE', 'DIIS')
    pol_embed_options.maxiter = core.get_option('PE', 'MAXITER')
    pol_embed_options.pe_border = core.get_option('PE', 'BORDER')

    if pol_embed_options.pe_border:
        pol_embed_border_options = cppe.PeBorderOptions()
        pe_btype = core.get_option('PE', 'BORDER_TYPE').upper()
        if pe_btype == "REMOVE":
            pol_embed_border_options.border_type = core.PeBorderOptions.BorderType.rem
        elif pe_btype == "REDIST":
            pol_embed_border_options.border_type = core.PeBorderOptions.BorderType.redist
        pol_embed_border_options.rmin = core.get_option('PE', 'BORDER_RMIN')
        if core.get_option('PE', 'BORDER_RMIN_UNIT').upper() == "AA":
            pol_embed_border_options.rmin *= 1.0 / constants.bohr2angstroms
        pol_embed_border_options.redist_order = core.get_option('PE', 'BORDER_REDIST_ORDER')
        pol_embed_border_options.nredist = core.get_option('PE', 'BORDER_N_REDIST')
        pol_embed_border_options.redist_pol = core.get_option('PE', 'BORDER_REDIST_POL')

        pol_embed_options.border_options = pol_embed_border_options
    return pol_embed_options
Ejemplo n.º 7
0
    def __init__(self, *, name_hint: str = None, level_hint: str = None, param_tweaks: Union[Dict, List] = None, engine: str = None, save_pairwise_disp=False):
        from .dft import dashcoeff_supplement
        self.dashcoeff_supplement = dashcoeff_supplement
        self.save_pairwise_disp = save_pairwise_disp

        resolved = qcng.programs.empirical_dispersion_resources.from_arrays(
            name_hint=name_hint,
            level_hint=level_hint,
            param_tweaks=param_tweaks,
            dashcoeff_supplement=self.dashcoeff_supplement)
        self.fctldash = resolved['fctldash']
        self.dashlevel = resolved['dashlevel']
        self.dashparams = resolved['dashparams']
        self.description = qcng.programs.empirical_dispersion_resources.dashcoeff[self.dashlevel]['description']
        self.ordered_params = qcng.programs.empirical_dispersion_resources.dashcoeff[self.dashlevel]['default'].keys()
        self.dashlevel_citation = qcng.programs.empirical_dispersion_resources.dashcoeff[self.dashlevel]['citation']
        self.dashparams_citation = resolved['dashparams_citation']

        if engine is None:
            self.engine = _capable_engines_for_disp[self.dashlevel][0]
        else:
            if self.dashlevel in _engine_can_do[engine]:
                self.engine = engine
            else:
                raise ValidationError("""This little engine ({}) can't ({})""".format(engine, self.dashlevel))

        if self.engine == 'libdisp':
            self.disp = core.Dispersion.build(self.dashlevel, **resolved['dashparams'])
Ejemplo n.º 8
0
def _validate_diis(self):
    """Sanity-checks DIIS control options

    Raises
    ------
    psi4.driver.p4util.exceptions.ValidationError
        If any of DIIS options don't play well together.

    Returns
    -------
    bool
        Whether some form of DIIS is enabled during SCF.

    """

    restricted_open = self.same_a_b_orbs() and not self.same_a_b_dens()
    aediis_active = core.get_option(
        'SCF', 'SCF_INITIAL_ACCELERATOR') != "NONE" and not restricted_open

    if aediis_active:
        start = core.get_option('SCF', 'SCF_INITIAL_START_DIIS_TRANSITION')
        stop = core.get_option('SCF', 'SCF_INITIAL_FINISH_DIIS_TRANSITION')
        if start < stop:
            raise ValidationError(
                'SCF_INITIAL_START_DIIS_TRANSITION error magnitude cannot be less than SCF_INITIAL_FINISH_DIIS_TRANSITION.'
            )
        elif start < 0:
            raise ValidationError(
                'SCF_INITIAL_START_DIIS_TRANSITION cannot be negative.')
        elif stop < 0:
            raise ValidationError(
                'SCF_INITIAL_FINISH_DIIS_TRANSITION cannot be negative.')

    enabled = bool(core.get_option('SCF', 'DIIS')) or aediis_active
    if enabled:
        start = core.get_option('SCF', 'DIIS_START')
        if start < 1:
            raise ValidationError(
                'SCF DIIS_START ({}) must be at least 1'.format(start))

    return enabled
Ejemplo n.º 9
0
def get_pe_options():
    if core.get_option('SCF', 'PCM'):
        raise ValidationError(
            """Error: 3-layer QM/PE/PCM not implemented.\n""")
    rmin = core.get_option('PE', 'BORDER_RMIN')
    if core.get_option('PE', 'BORDER_RMIN_UNIT').upper() == "AA":
        rmin *= 1.0 / constants.bohr2angstroms
    pol_embed_options = {
        "potfile":
        core.get_option('PE', 'POTFILE'),
        "iso_pol":
        core.get_option('PE', 'ISOTROPIC_POL'),
        "induced_thresh":
        core.get_option('PE', 'INDUCED_CONVERGENCE'),
        "maxiter":
        core.get_option('PE', 'MAXITER'),
        # tree options
        "summation_induced_fields":
        core.get_option('PE', 'SUMMATION_FIELDS').lower(),
        "tree_expansion_order":
        core.get_option('PE', 'TREE_EXPANSION_ORDER'),
        "theta":
        core.get_option('PE', 'TREE_THETA'),
        # damping options
        "damp_induced":
        core.get_option('PE', 'DAMP_INDUCED'),
        "damping_factor_induced":
        core.get_option('PE', 'DAMPING_FACTOR_INDUCED'),
        "damp_multipole":
        core.get_option('PE', 'DAMP_MULTIPOLE'),
        "damping_factor_multipole":
        core.get_option('PE', 'DAMPING_FACTOR_MULTIPOLE'),
        "pe_border":
        core.get_option('PE', 'BORDER'),
        "border_type":
        core.get_option('PE', 'BORDER_TYPE').lower(),
        "border_rmin":
        rmin,
        "border_nredist":
        core.get_option('PE', 'BORDER_N_REDIST'),
        "border_redist_order":
        core.get_option('PE', 'BORDER_REDIST_ORDER'),
        "border_redist_pol":
        core.get_option('PE', 'BORDER_REDIST_POL'),
        # PE(ECP)
        "pe_ecp":
        core.get_option('PE', 'PE_ECP'),
    }
    return pol_embed_options
Ejemplo n.º 10
0
def cubeprop_compute_properties(self):
    """Filesystem wrapper for CubeProperties::raw_compute_properties."""

    filepath = core.get_global_option("CUBEPROP_FILEPATH")

    # Is filepath a valid directory?
    if not os.path.isdir(os.path.abspath(os.path.expandvars(filepath))):
        raise ValidationError("""Filepath "{}" is not valid.  Please create this directory.""".format(filepath))

    geomfile = filepath + os.sep + 'geom.xyz'
    xyz = self.basisset().molecule().to_string(dtype='xyz', units='Angstrom')
    with open(geomfile, 'w') as fh:
        fh.write(xyz)

    self.raw_compute_properties()
Ejemplo n.º 11
0
def register_xtpl_function(func: Callable):
    """Register a user-defined extrapolation function to use like an built-in one.

    Parameters
    ----------
    func
        A Python function that applies a basis set extrapolation formula to scalars and optionally to
        NumPy arrays. See :src:`psi4/driver/driver_cbs_helper.py` and :srcsample:`pywrap-cbs1` for
        examples. The name of the function should follow the pattern ``<scf|corl>_xtpl_<scientist>_<#basis>``.

    """
    if func.__name__.split("_")[-1].isdigit():
        xtpl_procedures[func.__name__] = func
    else:
        raise ValidationError("Extrapolation function names follow <scf|corl>_xtpl_<scientist>_<#basis>")
Ejemplo n.º 12
0
def parse_cotton_irreps(irrep: Union[str, int], point_group: str) -> int:
    """Return validated Cotton ordering index of `irrep` within `point_group`.

    Parameters
    ----------
    irrep
        Irreducible representation. Either label (case-insensitive) or 1-based index (int or str).
    point_group
        Molecular point group label (case-insensitive).

    Returns
    -------
    int
        1-based index for **irrep** within **point_group** in Cotton ordering.

    Raises
    ------
    ValidationError
        If **irrep** out-of-bounds or invalid or if **point_group** doesn't exist.

    """
    cotton = {
        'c1': ['a'],
        'ci': ['ag', 'au'],
        'c2': ['a', 'b'],
        'cs': ['ap', 'app'],
        'd2': ['a', 'b1', 'b2', 'b3'],
        'c2v': ['a1', 'a2', 'b1', 'b2'],
        'c2h': ['ag', 'bg', 'au', 'bu'],
        'd2h': ['ag', 'b1g', 'b2g', 'b3g', 'au', 'b1u', 'b2u', 'b3u'],
    }

    boll = cotton[point_group.lower()]

    if str(irrep).isdigit():
        irrep = int(irrep)
        if irrep > 0 and irrep <= len(boll):
            return irrep
    else:
        if irrep.lower() in boll:
            return boll.index(irrep.lower()) + 1

    raise ValidationError(
        f"""Irrep '{irrep}' not valid for point group '{point_group}'.""")
Ejemplo n.º 13
0
def _validate_frac():
    """Sanity-checks FRAC control options

    Raises
    ------
    ValidationError
        If any of |scf__frac_start| don't play well together.

    Returns
    -------
    bool
        Whether FRAC is enabled during scf.

    """
    enabled = (core.get_option('SCF', 'FRAC_START') != 0)
    if enabled:
        if enabled < 0:
            raise ValidationError('SCF FRAC_START ({}) must be at least 1'.format(enabled))

    return enabled
Ejemplo n.º 14
0
def _validate_MOM():
    """Sanity-checks MOM control options

    Raises
    ------
    ValidationError
        If any of |scf__mom_start|, |scf__mom_occ| don't play well together.

    Returns
    -------
    bool
        Whether excited-state MOM (not just the plain stabilizing MOM) is enabled during scf.

    """
    enabled = (core.get_option('SCF', "MOM_START") != 0 and len(core.get_option('SCF', "MOM_OCC")) > 0)
    if enabled:
        start = core.get_option('SCF', "MOM_START")
        if enabled < 0:
            raise ValidationError('SCF MOM_START ({}) must be at least 1'.format(start))

    return enabled
Ejemplo n.º 15
0
def _initialize_findif(mol,
                       freq_irrep_only,
                       mode,
                       initialize_string,
                       verbose=0):
    """Perform initialization tasks needed by all primary functions.

    Parameters
    ----------
    mol : qcdb.molecule or psi4.core.Molecule
        The molecule to displace
    freq_irrep_only : int
        The Cotton ordered irrep to get frequencies for. Choose -1 for all
        irreps.
    mode : {"1_0", "2_0", "2_1"}
         The first number specifies the derivative level determined from
         displacements, and the second number is the level determined at.
    initialize_string : function
         A function that returns the string to print to show the caller was entered.
         The string is both caller-specific and dependent on values determined
         in this function.
    verbose : int
         Set to 0 to silence extra print information, regardless of the print level.
         Used so the information is printed only during geometry generation, and not
         during the derivative computation as well.

    :returns: *dict*
        Miscellaneous information required by callers.
    """

    core.print_out(
        "\n         ----------------------------------------------------------\n"
    )
    core.print_out("                                   FINDIF\n")
    core.print_out("                     R. A. King and Jonathon Misiewicz\n")
    core.print_out(
        "         ---------------------------------------------------------\n\n"
    )

    print_lvl = core.get_option("FINDIF", "PRINT")
    num_pts = core.get_option("FINDIF", "POINTS")
    disp_size = core.get_option("FINDIF", "DISP_SIZE")

    data = {"print_lvl": print_lvl, "num_pts": num_pts, "disp_size": disp_size}

    if print_lvl:
        core.print_out(initialize_string(data))

    # Get settings for CdSalcList, then get the CdSalcList.
    method_allowed_irreps = 0x1 if mode == "1_0" else 0xFF
    t_project = not core.get_global_option("EXTERN") and (
        not core.get_global_option("PERTURB_H"))
    # core.get_option returns an int, but CdSalcList expect a bool, so re-cast
    r_project = t_project and bool(core.get_option("FINDIF", "FD_PROJECT"))
    salc_list = core.CdSalcList(mol, method_allowed_irreps, t_project,
                                r_project)

    n_atom = mol.natom()
    n_irrep = salc_list.nirrep()
    n_salc = salc_list.ncd()

    if print_lvl and verbose:
        core.print_out("    Number of atoms is {:d}.\n".format(n_atom))
        if method_allowed_irreps != 0x1:
            core.print_out("    Number of irreps is {:d}.\n".format(n_irrep))
        core.print_out("    Number of {!s}SALCs is {:d}.\n".format(
            "" if method_allowed_irreps != 0x1 else "symmetric ", n_salc))
        core.print_out(
            "    Translations projected? {:d}. Rotations projected? {:d}.\n".
            format(t_project, r_project))

    # TODO: Replace with a generator from a stencil to a set of points.
    # Diagonal displacements differ between the totally symmetric irrep, compared to all others.
    # Off-diagonal displacements are the same for both.
    pts_dict = {
        3: {
            "sym_irr": ((-1, ), (1, )),
            "asym_irr": ((-1, ), ),
            "off": ((1, 1), (-1, -1))
        },
        5: {
            "sym_irr": ((-2, ), (-1, ), (1, ), (2, )),
            "asym_irr": ((-2, ), (-1, )),
            "off": ((-1, -2), (-2, -1), (-1, -1), (1, -1), (-1, 1), (1, 1),
                    (2, 1), (1, 2))
        }
    }

    if num_pts not in pts_dict:
        raise ValidationError("FINDIF: Invalid number of points!")

    # Convention: x_pi means x_per_irrep. The ith element is x for irrep i, with Cotton ordering.
    salc_indices_pi = [[] for h in range(n_irrep)]

    # Validate that we have an irrep matching the user-specified irrep, if any.
    try:
        salc_indices_pi[freq_irrep_only]
    except (TypeError, IndexError):
        if freq_irrep_only != -1:
            raise ValidationError("FINDIF: Irrep value not in valid range.")

    # Populate salc_indices_pi for all irreps.
    for i, salc in enumerate(salc_list):
        salc_indices_pi[salc.irrep_index()].append(i)

    # If the method allows more than one irrep, print how the irreps partition the SALCS.
    if print_lvl and method_allowed_irreps != 0x1 and verbose:
        core.print_out("    Index of SALCs per irrep:\n")
        for h in range(n_irrep):
            if print_lvl > 1 or freq_irrep_only in {h, -1}:
                tmp = (" {:d} " *
                       len(salc_indices_pi[h])).format(*salc_indices_pi[h])
                core.print_out("     {:d} : ".format(h + 1) + tmp + "\n")
        core.print_out("    Number of SALCs per irrep:\n")
        for h in range(n_irrep):
            if print_lvl > 1 or freq_irrep_only in {h, -1}:
                core.print_out("     Irrep {:d}: {:d}\n".format(
                    h + 1, len(salc_indices_pi[h])))

    # Now that we've printed the SALCs, clear any that are not of user-specified symmetry.
    if freq_irrep_only != -1:
        for h in range(n_irrep):
            if h != freq_irrep_only:
                salc_indices_pi[h].clear()

    n_disp_pi = []
    disps = pts_dict[num_pts]  # We previously validated num_pts in pts_dict.

    for irrep, indices in enumerate(salc_indices_pi):
        n_disp = len(indices) * len(
            disps["asym_irr" if irrep != 0 else "sym_irr"])
        if mode == "2_0":
            # Either len(indices) or len(indices)-1 is even, so dividing by two is safe.
            n_disp += len(indices) * (len(indices) - 1) // 2 * len(
                disps["off"])
        n_disp_pi.append(n_disp)

    # Let's print out the number of geometries, the displacement multiplicity, and the CdSALCs!
    if print_lvl and verbose:
        core.print_out(
            "    Number of geometries (including reference) is {:d}.\n".format(
                sum(n_disp_pi) + 1))
        if method_allowed_irreps != 0x1:
            core.print_out("    Number of displacements per irrep:\n")
            for i, ndisp in enumerate(n_disp_pi, start=1):
                core.print_out("      Irrep {:d}: {:d}\n".format(i, ndisp))

    if print_lvl > 1 and verbose:
        for salc in salc_list:
            salc.print_out()

    data.update({
        "n_disp_pi": n_disp_pi,
        "n_irrep": n_irrep,
        "n_salc": n_salc,
        "n_atom": n_atom,
        "salc_list": salc_list,
        "salc_indices_pi": salc_indices_pi,
        "disps": disps
    })

    return data
Ejemplo n.º 16
0
def compute_hessian_from_energy(mol, E, freq_irrep_only):
    """Compute the Hessian by finite difference of energies.

    Parameters
    ----------
    mol : qcdb.molecule or psi4.core.Molecule
        The molecule to compute the Hessian of.
    G : list of psi4.core.Matrix
        A list of energies of the molecule at displaced energies
    freq_irrep_only : int
        The Cotton ordered irrep to get frequencies for. Choose -1 for all
        irreps.

    Returns
    -------
    hessian : np.array
        (3*nat, 3* nat) Cartesian hessian [Eh/a0^2]
    """
    def init_string(data):
        out_str = ""
        for i, energy in enumerate(E[:-1], start=1):
            out_str += "    {:5d} : {:20.10f}\n".format(i, energy)
        return (
            "  Computing second-derivative from energies using projected, \n"
            "  symmetry-adapted, cartesian coordinates.\n\n"
            "  {:d} energies passed in, including the reference geometry.\n"
            "    Using {:d}-point formula.\n"
            "    Energy without displacement: {:15.10f}\n"
            "    Check energies below for precision!\n{}".format(
                len(E), data["num_pts"], E[-1], out_str))

    data = _initialize_findif(mol, freq_irrep_only, "2_0", init_string)

    n_disp = sum(data["n_disp_pi"]) + 1
    if len(E) != n_disp:
        raise ValidationError(
            "FINDIF: Received {} energies for {} geometries.".format(
                len(E), n_disp))

    ref_energy = E[-1]
    unused_energies = E[:-1]
    massweighter = np.repeat([mol.mass(a) for a in range(data["n_atom"])],
                             3)**(-0.5)
    B_pi = []
    H_pi = []
    irrep_lbls = mol.irrep_labels()

    # Unlike in the gradient case, we have no symmetry transformations to worry about.
    # We get to the task directly: assembling the force constants in each irrep block.
    for h in range(data["n_irrep"]):
        salcs = data["salc_indices_pi"][h]
        if not salcs: continue

        n_salcs = len(salcs)
        n_disps = data["n_disp_pi"][h]
        irrep_energies = unused_energies[:n_disps]
        unused_energies = unused_energies[n_disps:]

        # Step One: Diagonals
        # For asymmetric irreps, the energy at a + disp is the same as at a - disp
        # We exploited this, so we only need half the displacements for asymmetrics
        disps_per_diag = (data["num_pts"] - 1) // (2 if h else 1)
        diag_disps = disps_per_diag * n_salcs
        energies = np.asarray(irrep_energies[:diag_disps]).reshape(
            (n_salcs, -1))
        # For simplicity, convert the case of an asymmetric to the symmetric case.
        if h:
            energies = np.hstack((energies, np.fliplr(energies)))
        # Now determine all diagonal force constants for this irrep.
        if data["num_pts"] == 3:
            diag_fcs = energies[:, 0] + energies[:, 1]
            diag_fcs -= 2 * ref_energy
            diag_fcs /= (data["disp_size"]**2)
        elif data["num_pts"] == 5:
            diag_fcs = -energies[:,
                                 0] + 16 * energies[:,
                                                    1] + 16 * energies[:,
                                                                       2] - energies[:,
                                                                                     3]
            diag_fcs -= 30 * ref_energy
            diag_fcs /= (12 * data["disp_size"]**2)
        H_irr = np.diag(diag_fcs)

        # Step Two: Off-diagonals
        num_unique_off_elts = n_salcs * (n_salcs - 1) // 2
        offdiag_energies = irrep_energies[diag_disps:]
        # If offdiagonal elements exist, put them into groups per salc pairs
        # ...if offdiagonal elements DON'T exist, reshaping would raise an error.
        if offdiag_energies:
            offdiag_energies = np.reshape(offdiag_energies,
                                          (num_unique_off_elts, -1))
        offdiag_row_index = 0

        for i, salc in enumerate(salcs):
            for j, salc2 in enumerate(salcs[:i]):
                offdiag_row = offdiag_energies[offdiag_row_index]
                if data["num_pts"] == 3:
                    fc = (+offdiag_row[0] + offdiag_row[1] + 2 * ref_energy -
                          energies[i][0] - energies[i][1] - energies[j][0] -
                          energies[j][1]) / (2 * data["disp_size"]**2)
                elif data["num_pts"] == 5:
                    fc = (
                        -offdiag_row[0] - offdiag_row[1] + 9 * offdiag_row[2] -
                        offdiag_row[3] - offdiag_row[4] + 9 * offdiag_row[5] -
                        offdiag_row[6] - offdiag_row[7] + energies[i][0] -
                        7 * energies[i][1] - 7 * energies[i][2] +
                        energies[i][3] + energies[j][0] - 7 * energies[j][1] -
                        7 * energies[j][2] + energies[j][3] +
                        12 * ref_energy) / (12 * data["disp_size"]**2)
                H_irr[i, j] = fc
                H_irr[j, i] = fc
                offdiag_row_index += 1

        B_pi.append(data["salc_list"].matrix_irrep(h))
        H_pi.append(
            _process_hessian_symmetry_block(H_irr, B_pi[-1], massweighter,
                                            irrep_lbls[h], data["print_lvl"]))

    # All blocks of the Hessian are now constructed!
    return _process_hessian(H_pi, B_pi, massweighter, data["print_lvl"])
Ejemplo n.º 17
0
def compute_hessian_from_gradient(mol, G, freq_irrep_only):
    """Compute the Hessian by finite difference of gradients.

    Parameters
    ----------
    mol : qcdb.molecule or psi4.core.Molecule
        The molecule to compute the Hessian of.
    G : list of psi4.core.Matrix
        A list of gradients of the molecule at displaced geometries
    freq_irrep_only : int
        The Cotton ordered irrep to get frequencies for. Choose -1 for all
        irreps.

    Returns
    -------
    hessian : np.array
        (3*nat, 3* nat) Cartesian hessian [Eh/a0^2]
    """
    def init_string(data):
        return (
            "  Computing second-derivative from gradients using projected, \n"
            "  symmetry-adapted, cartesian coordinates.\n\n"
            "  {:d} gradients passed in, including the reference geometry.\n".
            format(len(G)))

    data = _initialize_findif(mol, freq_irrep_only, "2_1", init_string)

    n_disp = sum(data["n_disp_pi"]) + 1  # +1 for the reference geometry
    if len(G) != n_disp:
        raise ValidationError(
            "FINDIF: Received {} gradients for {} geometries.".format(
                len(G), n_disp))

    # For non-totally symmetric CdSALCs, a symmetry operation can convert + and - displacements.
    # Good News: By taking advantage of that, we (potentially) ran less computations.
    # Bad News: We need to find the - displacements from the + computations now.
    # The next ~80 lines of code are dedicated to that task.
    if data["print_lvl"]:
        core.print_out(
            "  Generating complete list of displacements from unique ones.\n\n"
        )

    pg = mol.point_group()
    ct = pg.char_table()
    order = pg.order()

    # Determine what atoms map to what other atoms under the point group operations.
    # The py-side compute_atom_map will work whether mol is a Py-side or C-side object.
    atom_map = molecule.compute_atom_map(mol)
    if data["print_lvl"] >= 3:
        core.print_out("    The atom map:\n")
        for atom, sym_image_list in enumerate(atom_map):
            core.print_out("     {:d} : ".format(atom + 1))
            for image_atom in sym_image_list:
                core.print_out("{:4d}".format(image_atom + 1))
            core.print_out("\n")
        core.print_out("\n")

    # A list of lists of gradients, per irrep
    gradients_pi = []
    # Extract and print the symmetric gradients. This need no additional processing.
    gradients_pi.append([np.array(grad) for grad in G[0:data["n_disp_pi"][0]]])
    # Asymmetric gradients need additional processing. For future convenience, we discard the symmetric ones.
    G = G[data["n_disp_pi"][0]:]

    if data["print_lvl"] >= 3:
        core.print_out("    Symmetric gradients\n")
        for gradient in gradients_pi[0]:
            core.Matrix.from_array(gradient).print_out()
            #gradient.print_out()

    # Asymmetric gradient. There's always SOME operation that transforms a positive
    # into a negative displacement.By doing extra things here, we can find the
    # gradients at the positive displacements.
    for h in range(1, data["n_irrep"]):

        # If there are no CdSALCs in this irrep, let's skip it.
        if not data["n_disp_pi"][h]:
            gradients_pi.append([])
            continue

        gamma = ct.gamma(h)
        if data["print_lvl"] >= 3:
            core.print_out("Characters for irrep {}\n".format(h))
            for group_op in range(order):
                core.print_out(" {:5.1f}".format(gamma.character(group_op)))
            core.print_out("\n")

        # Find the group operation that converts + to - displacements.
        for group_op in range(order):
            if gamma.character(group_op) == -1:
                break
        else:
            raise ValidationError(
                "A symmetric gradient passed for a non-symmetric one.")
        if data["print_lvl"]:
            core.print_out(
                "    Operation {} takes plus displacements of irrep {} to minus ones.\n"
                .format(group_op + 1, gamma.symbol()))

        sym_op = np.array(ct.symm_operation(group_op).matrix())
        gradients = []

        def recursive_gradients(n):
            """Populate gradients, with step -n, -n+1, ... -1, 1, ... n.
               Positive displacements are computed."""
            gradients.append(np.array(G.pop(0)))
            new_grad = np.zeros((data["n_atom"], 3))
            for atom, image in enumerate(atom_map):
                atom2 = image[group_op]
                new_grad[atom2] = np.einsum("xy,y->x", sym_op,
                                            gradients[-1][atom])
            if n > 1:
                recursive_gradients(n - 1)
            gradients.append(new_grad)

        max_disp = (data["num_pts"] -
                    1) // 2  # The numerator had better be divisible by two.
        # i is just for counting the number of times to run recursive_gradients.
        # recursive_gradients "knows" due to the structure of G the number of computed gradients per CdSALC.
        for i in data["salc_indices_pi"][h]:
            recursive_gradients(max_disp)
        gradients_pi.append(gradients)

    # Massweight all gradients.
    # Remember, the atom currently corresponds to our 0 axis, hence these transpose tricks.
    massweighter = np.asarray([mol.mass(a)
                               for a in range(data["n_atom"])])**(-0.5)
    gradients_pi = [[(grad.T * massweighter).T for grad in gradients]
                    for gradients in gradients_pi]

    if data["print_lvl"] >= 3:
        core.print_out("    All mass-weighted gradients\n")
        for gradients in gradients_pi:
            for grad in gradients:
                core.Matrix.from_array(grad).print_out()

    # We have all our gradients generated now!
    # Next, time to get our Hessian.

    H_pi = []
    B_pi = []
    irrep_lbls = mol.irrep_labels()
    massweighter = np.repeat(massweighter, 3)

    for h in range(data["n_irrep"]):
        n_disp = data["n_disp_pi"][h]
        Nindices = len(data["salc_indices_pi"][h])
        gradients = gradients_pi[h]

        if not Nindices:
            continue

        # Flatten each gradient, and turn it into a COLUMN of the matrix.
        gradient_matrix = np.array([grad.flatten() for grad in gradients]).T
        # Transform disps from Cartesian to CdSalc coordinates.
        # For future convenience, we transpose.
        # Rows are gradients and columns are coordinates with respect to a particular CdSALC.
        B_pi.append(data["salc_list"].matrix_irrep(h))
        grads_adapted = np.dot(B_pi[-1], gradient_matrix).T

        if data["print_lvl"] >= 3:
            core.print_out("Gradients in B-matrix coordinates\n")
            for disp in range(n_disp):
                core.print_out(" disp {:d}: ".format(disp))
                for salc in grads_adapted[disp]:
                    core.print_out("{:15.10f}".format(salc))
                core.print_out("\n")

        H_pi.append(np.empty([Nindices, Nindices]))

        if data["num_pts"] == 3:
            H_pi[-1] = (grads_adapted[1::2] -
                        grads_adapted[::2]) / (2.0 * data["disp_size"])
        elif data["num_pts"] == 5:
            H_pi[-1] = (grads_adapted[::4] - 8 * grads_adapted[1::4] +
                        8 * grads_adapted[2::4] -
                        grads_adapted[3::4]) / (12.0 * data["disp_size"])

        H_pi[-1] = _process_hessian_symmetry_block(H_pi[-1], B_pi[-1],
                                                   massweighter, irrep_lbls[h],
                                                   data["print_lvl"])

    # All blocks of the Hessian are now constructed!
    return _process_hessian(H_pi, B_pi, massweighter, data["print_lvl"])
Ejemplo n.º 18
0
def compute_gradient_from_energy(mol, E):
    """Compute the gradient by finite difference of energies.

    Parameters
    ----------
    mol : qcdb.molecule or psi4.core.Molecule
        The molecule to compute the gradient of.
    E : list of floats
        A list of energies of the molecule at displaced geometries.

    Returns
    -------
    gradient : np.array
        (nat, 3) Cartesian gradient [Eh/a0].
    """
    def init_string(data):
        return (
            "  Computing gradient from energies.\n"
            "    Using {:d}-point formula.\n"
            "    Energy without displacement: {:15.10f}\n"
            "    Check energies below for precision!\n"
            "    Forces are for mass-weighted, symmetry-adapted cartesians (in au).\n"
            .format(data["num_pts"], E[-1]))

    data = _initialize_findif(mol, -1, "1_0", init_string)

    n_disp = sum(data["n_disp_pi"]) + 1  # +1 for the reference
    if len(E) != n_disp:
        raise ValidationError(
            "FINDIF: Received {} energies for {} geometries.".format(
                len(E), n_disp))

    # Discard the reference energy.
    E = np.asarray(E[:-1])
    if data["num_pts"] == 3:
        g_q = (E[1::2] - E[::2]) / (2.0 * data["disp_size"])
    elif data["num_pts"] == 5:
        g_q = (E[0::4] - 8.0 * E[1::4] + 8.0 * E[2::4] -
               E[3::4]) / (12.0 * data["disp_size"])
    else:  # This error SHOULD have already been caught, but just in case...
        raise ValidationError(
            "FINDIF: {} is an invalid number of points.".format(
                data["num_pts"]))
    g_q = np.asarray(g_q)

    if data["print_lvl"]:
        max_disp = (data["num_pts"] -
                    1) // 2  # The numerator had better be divisible by two.
        e_per_salc = 2 * max_disp
        energy_string = ""
        for i in range(1, max_disp + 1):
            energy_string = "Energy(-{})        ".format(
                i) + energy_string + "Energy(+{})        ".format(i)
        core.print_out("\n     Coord      " + energy_string + "    Force\n")
        for salc in range(data["n_salc"]):
            print_str = "    {:5d}" + " {:17.10f}" * (
                e_per_salc) + " {force:17.10f}" + "\n"
            energies = E[e_per_salc * salc:e_per_salc * (salc + 1)]
            print_str = print_str.format(salc, force=g_q[salc], *energies)
            core.print_out(print_str)
        core.print_out("\n")

    B = data["salc_list"].matrix()
    g_cart = np.dot(g_q, B)
    g_cart = g_cart.reshape(data["n_atom"], 3)
    massweighter = np.array([mol.mass(a)
                             for a in range(data["n_atom"])])**(0.5)
    g_cart = (g_cart.T * massweighter).T

    if data["print_lvl"]:
        core.print_out(
            "\n-------------------------------------------------------------\n"
        )

    return g_cart
Ejemplo n.º 19
0
def _geom_generator(mol, freq_irrep_only, mode):
    """
    Generate geometries for the specified molecule and derivative levels.
    You probably want to use the gradient_from_energy_geometries,
    hessian_from_energy_geometries, or hessian_form_gradient_geometries
    convenience functions.

    Parameters
    ----------
    mol : qcdb.molecule or psi4.core.Molecule
        The molecule to perform finite difference calculations on.
    freq_irrep_only : int
        The Cotton ordered irrep to get frequencies for. Choose -1 for all
        irreps.
    mode : {"1_0", "2_0", "2_1"}
         The first number specifies the derivative level determined from
         displacements, and the second number is the level determined at.

    Returns
    -------
    disp_geoms : list of psi4.core.Matrix
        A list of displaced geometries to compute quantities at.
    """

    # NOTE: While it would be nice if disp_geoms could be a list of ndarray,
    # psi4.core.molecule needs geometries to be psi4.core.Matrix objects.

    msg_dict = {
        "1_0":
        "energies to determine gradients",
        "2_1":
        "gradients to determine vibrational frequencies and \n"
        "  normal modes.  Resulting frequencies are only valid at stationary points",
        "2_0":
        "gradients to determine vibrational frequencies and \n"
        "  normal modes.  Resulting frequencies are only valid at stationary points"
    }

    try:
        print_msg = msg_dict[mode]
    except KeyError:
        raise ValidationError("FINDIF: Mode {} not recognized.".format(mode))

    def init_string(data):
        return ("  Using finite-differences of {:s}.\n"
                "    Generating geometries for use with {:d}-point formula.\n"
                "    Displacement size will be {:6.2e}.\n".format(
                    print_msg, data["num_pts"], data["disp_size"]))

    data = _initialize_findif(mol, freq_irrep_only, mode, init_string, 1)

    # We can finally start generating displacements.
    ref_geom_temp = mol.geometry()
    ref_geom = ref_geom_temp.clone()
    ref_geom.name = "Reference geometry"

    disp_geoms = []

    def append_geoms(indices, steps):
        """Given a list of indices and a list of steps to displace each, append the corresponding geometry to the list."""
        new_geom = ref_geom.clone()
        # Next, to make this salc/magnitude composite.
        index_steps = zip(indices, steps)
        _displace_cart(mol, new_geom, data["salc_list"], index_steps,
                       data["disp_size"])
        disp_geoms.append(new_geom)

    for h in range(data["n_irrep"]):
        active_indices = data["salc_indices_pi"][h]

        for index in active_indices:
            # Displace along the diagonal.
            # Remember that the totally symmetric irrep has special displacements.
            for val in data["disps"]["sym_irr" if h == 0 else "asym_irr"]:
                append_geoms((index, ), val)

        # Hessian from energies? We have off-diagonal displacements to worry about.
        if mode == "2_0":
            # i indexes SALC indices of the current irrep.
            for i, index in enumerate(active_indices):
                for index2 in active_indices[:i]:
                    for val in data["disps"]["off"]:
                        append_geoms((index, index2), val)

    disp_geoms.append(ref_geom)

    if data["print_lvl"] > 2:
        for disp in disp_geoms:
            disp.print_out()

    if data["print_lvl"] > 1:
        core.print_out(
            "\n-------------------------------------------------------------\n"
        )

    return disp_geoms
Ejemplo n.º 20
0
def compare_moldenfiles(expected, computed, digits=7, label='Compare Molden'):
    ref = moldenfile_to_string(expected).splitlines()
    calc = moldenfile_to_string(computed).splitlines()
    if len(ref) != len(calc):
        raise ValidationError(
            f"These two molden files have different lengths...\n")

    high_accuracy = digits
    index = 0
    max_len = len(calc)
    tests = []
    section = 0

    geom_re = re.compile(
        r'^\s*(\w*)\s+(\d+)\s+(\d+)\s+(-?\d+.\d+)\s+(-?\d+.\d+)\s+(-?\d+.\d+)\s*$'
    )
    basis_header_re = re.compile(r'^\s*([s,p,d,f,g])\s*(\d*)\s*(\d*.\d*)\s*$')
    s1_re = re.compile(r'^\s*(\d+.?\d*)\s+(\d+.?\d*)$')
    s2_re = re.compile(r'^\s*(\d+)\s+(-?\d+.\d+[e,E][\+,-]\d+)\s*$')
    sym_re = re.compile(r'^\s*Sym\s*=\s*(\w*)\s*$')
    energy_re = re.compile(r'^\s*Ene\s*=\s*(-?\d*.?\d*[e,E]?\+?-?\d*)\s*$')
    spin_re = re.compile(r'^\s*Spin\s*=\s*(\w*)\s*$')
    occ_re = re.compile(r'^\s*Occup\s*=\s*(-?\d*.\d*[e,E]?-?\+?\d*)\s*$')

    for i in range(max_len):
        line = calc[i]

        if geom_re.match(line):
            c1, c2, c3, c4, c5, c6 = geom_re.match(line).groups()
            r1, r2, r3, r4, r5, r6 = geom_re.match(line).groups()
            test = compare_strings(r1, c1) and compare_integers(
                r2, c2) and compare_integers(r3, c3) and compare_values(
                    r4, c4, high_accuracy) and compare_values(
                        r5, c5, high_accuracy) and compare_values(
                            r6, c6, high_accuracy)

        elif basis_header_re.match(line):
            c1, c2, c3 = basis_header_re.match(line).groups()
            r1, r2, r3 = basis_header_re.match(ref[i]).groups()
            test = compare_strings(r1, c1) and compare_integers(
                r2, c2) and compare_values(r3, c3, 3)

        elif s1_re.match(line):
            c1, c2 = s1_re.match(line).groups()
            r1, r2 = s1_re.match(ref[i]).groups()
            test = compare_values(r1, c1, high_accuracy) and compare_values(
                r2, c2, high_accuracy)

        elif sym_re.match(line):
            c = sym_re.match(line).group(1)
            r = sym_re.match(ref[i]).group(1)
            test = compare_strings(r, c, f'text line: {line}')

        elif energy_re.match(line):
            c = energy_re.match(line).group(1)
            r = energy_re.match(ref[i]).group(1)
            test = compare_values(r, c, high_accuracy, f'float value: {line}')

        elif spin_re.match(line):
            c = spin_re.match(line).group(1)
            r = spin_re.match(ref[i]).group(1)
            test = compare_strings(r, c, f'text line: {line}')

        elif occ_re.match(line):
            c = occ_re.match(line).group(1)
            r = occ_re.match(ref[i]).group(1)
            test = compare_values(r, c, high_accuracy, f'float value: {line}')

        elif s2_re.match(line):
            c1, c2 = s2_re.match(line).groups()
            r1, r2 = s2_re.match(line).groups()
            test = compare_integers(
                r1, c1, f'int value: {line}') and compare_values(
                    r2, c2, high_accuracy, f'float value: {line}')

        else:
            test = compare_strings(line, ref[i])

        tests.append(test)

    return compare_integers(True, all(tests), label)
Ejemplo n.º 21
0
def corl_xtpl_helgaker_2(functionname: str, zLO: int, valueLO: Extrapolatable, zHI: int, valueHI: Extrapolatable, verbose: int = 1, alpha: Optional[float] = None) -> Extrapolatable:
    r"""Extrapolation scheme for correlation energies with two adjacent zeta-level bases.
    Used by :py:func:`~psi4.cbs`.

    Parameters
    ----------
    functionname
        Name of the CBS component (e.g., 'MP2') used in summary printing.
    zLO
        Zeta number of the smaller basis set in 2-point extrapolation.
    valueLO
        Energy, gradient, or Hessian value at the smaller basis set in 2-point
        extrapolation.
    zHI
        Zeta number of the larger basis set in 2-point extrapolation.
        Must be `zLO + 1`.
    valueHI
        Energy, gradient, or Hessian value at the larger basis set in 2-point
        extrapolation.
    verbose
        Controls volume of printing.
    alpha
        Overrides the default :math:`\alpha = 3.0`

    Returns
    -------
    float or numpy.ndarray
        Eponymous function applied to input zetas and values; type from `valueLO`.

    Notes
    -----
    The extrapolation is calculated according to [5]_:
    :math:`E_{corl}^X = E_{corl}^{\infty} + \beta X^{-alpha}`

    References
    ----------

    .. [5] Halkier, Helgaker, Jorgensen, Klopper, Koch, Olsen, & Wilson,
       Chem. Phys. Lett. 286 (1998) 243-252,
       DOI: 10.1016/S0009-2614(99)00179-7

    Examples
    --------
    >>> # [1] CISD extrapolation
    >>> energy('cbs', corl_wfn='cisd', corl_basis='cc-pV[DT]Z', corl_scheme='corl_xtpl_helgaker_2')

    """
    if type(valueLO) != type(valueHI):
        raise ValidationError(
            f"corl_xtpl_helgaker_2: Inputs must be of the same datatype! ({type(valueLO)}, {type(valueHI)})")

    if alpha is None:
        alpha = 3.0

    if isinstance(valueLO, float):
        value = (valueHI * zHI**alpha - valueLO * zLO**alpha) / (zHI**alpha - zLO**alpha)
        beta = (valueHI - valueLO) / (zHI**(-alpha) - zLO**(-alpha))

        final = value
        if verbose:
            # Output string with extrapolation parameters
            cbsscheme = f"""\n\n   ==> Helgaker 2-point correlated extrapolation for method: {functionname.upper()} <==\n\n"""
            cbsscheme += """   LO-zeta (%s) Energy:               % 16.12f\n""" % (str(zLO), valueLO)
            cbsscheme += """   HI-zeta (%s) Energy:               % 16.12f\n""" % (str(zHI), valueHI)
            cbsscheme += """   Alpha (exponent) Value:           % 16.12f\n""" % alpha
            cbsscheme += f"""   Beta (coefficient) Value:         {beta: 16.12f}\n\n"""
            cbsscheme += """   Extrapolated Energy:              % 16.12f\n\n""" % value
            # Note that in energy-only days, this used to print SCF and Correlation, not Total, Energy

            name_str = "%s/(%s,%s)" % (functionname.upper(), _zeta_val2sym[zLO].upper(), _zeta_val2sym[zHI].upper())
            cbsscheme += """   @Extrapolated """
            cbsscheme += name_str + ':'
            cbsscheme += " " * (19 - len(name_str))
            cbsscheme += """% 16.12f\n\n""" % final
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        return final

    elif isinstance(valueLO, (core.Matrix, core.Vector)):
        valueLO = np.array(valueLO)
        valueHI = np.array(valueHI)

        value = (valueHI * zHI**alpha - valueLO * zLO**alpha) / (zHI**alpha - zLO**alpha)
        beta = (valueHI - valueLO) / (zHI**(-alpha) - zLO**(-alpha))

        if verbose > 2:
            cbsscheme = f"""\n   ==> Helgaker 2-point correlated extrapolation for method: {functionname.upper()} <==\n"""
            cbsscheme += f"""\n   LO-zeta ({zLO}) Data\n"""
            cbsscheme += nppp(valueLO)
            cbsscheme += f"""\n   HI-zeta ({zHI}) Data\n"""
            cbsscheme += nppp(valueHI)

            cbsscheme += f"""\n   Alpha (exponent) Value:          {alpha:16.8f}"""
            cbsscheme += f"""\n   Beta Data\n"""
            cbsscheme += nppp(beta)
            cbsscheme += f"""\n   Extrapolated Data\n"""
            cbsscheme += nppp(value)
            cbsscheme += "\n"
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        value = core.Matrix.from_array(value)
        return value

    else:
        raise ValidationError(f"corl_xtpl_helgaker_2: datatype is not recognized '{type(valueLO)}'.")
Ejemplo n.º 22
0
def scf_xtpl_truhlar_2(functionname: str, zLO: int, valueLO: Extrapolatable, zHI: int, valueHI: Extrapolatable, verbose: int = 1, alpha: Optional[float] = None) -> Extrapolatable:
    r"""Extrapolation scheme using power form for reference energies with two adjacent
    zeta-level bases. Used by :py:func:`~psi4.cbs`.

    Parameters
    ----------
    functionname
        Name of the CBS component (e.g., 'HF') used in summary printing.
    zLO
        Zeta number of the smaller basis set in 2-point extrapolation.
    valueLO
        Energy, gradient, or Hessian value at the smaller basis set in 2-point
        extrapolation.
    zHI
        Zeta number of the larger basis set in 2-point extrapolation
        Must be `zLO + 1`.
    valueHI
        Energy, gradient, or Hessian value at the larger basis set in 2-point
        extrapolation.
    verbose
        Controls volume of printing.
    alpha
        Overrides the default :math:`\alpha = 3.4`

    Returns
    -------
    float or ndarray
        Eponymous function applied to input zetas and values; type from `valueLO`.

    Notes
    -----
    The extrapolation is calculated according to [2]_:
    :math:`E_{total}^X = E_{total}^{\infty} + \beta X^{-\alpha}, \alpha = 3.4`

    References
    ----------

    .. [2] Truhlar, Chem. Phys. Lett. 294 (1998) 45-48,
       DOI: 10.1016/S0009-2614(98)00866-5

    """

    if type(valueLO) != type(valueHI):
        raise ValidationError(
            f"scf_xtpl_truhlar_2: Inputs must be of the same datatype! ({type(valueLO)}, {type(valueHI)})")

    if alpha is None:
        alpha = 3.40

    beta_division = 1 / (zHI**(-1 * alpha) - zLO**(-1 * alpha))
    beta_mult = zHI**(-1 * alpha)

    if isinstance(valueLO, float):
        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose:
            # Output string with extrapolation parameters
            cbsscheme = ''
            cbsscheme += """\n   ==> Truhlar 2-point power form SCF extrapolation for method: %s <==\n\n""" % (
                functionname.upper())
            cbsscheme += """   LO-zeta (%s) Energy:               % 16.12f\n""" % (str(zLO), valueLO)
            cbsscheme += """   HI-zeta (%s) Energy:               % 16.12f\n""" % (str(zHI), valueHI)
            cbsscheme += """   Alpha (exponent) Value:           % 16.12f\n""" % (alpha)
            cbsscheme += """   Beta (coefficient) Value:         % 16.12f\n\n""" % (beta)

            name_str = "%s/(%s,%s)" % (functionname.upper(), _zeta_val2sym[zLO].upper(), _zeta_val2sym[zHI].upper())
            cbsscheme += """   @Extrapolated """
            cbsscheme += name_str + ':'
            cbsscheme += " " * (18 - len(name_str))
            cbsscheme += """% 16.12f\n\n""" % value
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        return value

    elif isinstance(valueLO, (core.Matrix, core.Vector)):
        valueLO = valueLO.to_array()
        valueHI = valueHI.to_array()

        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose > 2:
            cbsscheme = f"""\n   ==> Truhlar 2-point power SCF extrapolation for method: {functionname.upper()} <==\n"""
            cbsscheme += f"""\n   LO-zeta ({zLO}) Data\n"""
            cbsscheme += nppp(valueLO)
            cbsscheme += f"""\n   HI-zeta ({zHI}) Data\n"""
            cbsscheme += nppp(valueHI)

            cbsscheme += f"""\n   Alpha (exponent) Value:          {alpha:16.8f}"""
            cbsscheme += f"""\n   Beta Data\n"""
            cbsscheme += nppp(beta)
            cbsscheme += f"""\n   Extrapolated Data\n"""
            cbsscheme += nppp(value)
            cbsscheme += "\n"
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        value = core.Matrix.from_array(value)
        return value

    else:
        raise ValidationError(f"scf_xtpl_truhlar_2: datatype is not recognized '{type(valueLO)}'.")
Ejemplo n.º 23
0
def scf_finalize_energy(self):
    """Performs stability analysis and calls back SCF with new guess
    if needed, Returns the SCF energy. This function should be called
    once orbitals are ready for energy/property computations, usually
    after iterations() is called.

    """

    # post-scf vv10 correlation
    if core.get_option(
            'SCF', "DFT_VV10_POSTSCF") and self.functional().vv10_b() > 0.0:
        self.functional().set_lock(False)
        self.functional().set_do_vv10(True)
        self.functional().set_lock(True)
        core.print_out(
            "  ==> Computing Non-Self-Consistent VV10 Energy Correction <==\n\n"
        )
        SCFE = 0.0
        self.form_V()
        SCFE += self.compute_E()
        self.set_energies("Total Energy", SCFE)

    # Perform wavefunction stability analysis before doing
    # anything on a wavefunction that may not be truly converged.
    if core.get_option('SCF', 'STABILITY_ANALYSIS') != "NONE":

        # Don't bother computing needed integrals if we can't do anything with them.
        if self.functional().needs_xc():
            raise ValidationError(
                "Stability analysis not yet supported for XC functionals.")

        # We need the integral file, make sure it is written and
        # compute it if needed
        if core.get_option('SCF', 'REFERENCE') != "UHF":
            psio = core.IO.shared_object()
            #psio.open(constants.PSIF_SO_TEI, 1)  # PSIO_OPEN_OLD
            #try:
            #    psio.tocscan(constants.PSIF_SO_TEI, "IWL Buffers")
            #except TypeError:
            #    # "IWL Buffers" actually found but psio_tocentry can't be returned to Py
            #    psio.close(constants.PSIF_SO_TEI, 1)
            #else:
            #    # tocscan returned None
            #    psio.close(constants.PSIF_SO_TEI, 1)

            # logic above foiled by psio_tocentry not returning None<--nullptr in pb11 2.2.1
            #   so forcibly recomputing for now until stability revamp
            core.print_out("    SO Integrals not on disk. Computing...")
            mints = core.MintsHelper(self.basisset())
            #next 2 lines fix a bug that prohibits relativistic stability analysis
            if core.get_global_option('RELATIVISTIC') in ['X2C', 'DKH']:
                mints.set_rel_basisset(self.get_basisset('BASIS_RELATIVISTIC'))
            mints.integrals()
            core.print_out("done.\n")

            # Q: Not worth exporting all the layers of psio, right?

        follow = self.stability_analysis()

        while follow and self.attempt_number_ <= core.get_option(
                'SCF', 'MAX_ATTEMPTS'):
            self.attempt_number_ += 1
            core.print_out(
                "    Running SCF again with the rotated orbitals.\n")

            if self.initialized_diis_manager_:
                self.diis_manager().reset_subspace()
            # reading the rotated orbitals in before starting iterations
            self.form_D()
            self.set_energies("Total Energy", self.compute_initial_E())
            self.iterations()
            follow = self.stability_analysis()

        if follow and self.attempt_number_ > core.get_option(
                'SCF', 'MAX_ATTEMPTS'):
            core.print_out(
                "    There's still a negative eigenvalue. Try modifying FOLLOW_STEP_SCALE\n"
            )
            core.print_out(
                "    or increasing MAX_ATTEMPTS (not available for PK integrals).\n"
            )

    # At this point, we are not doing any more SCF cycles
    #   and we can compute and print final quantities.

    if hasattr(self.molecule(), 'EFP'):
        efpobj = self.molecule().EFP

        efpobj.compute()  # do_gradient=do_gradient)
        efpene = efpobj.get_energy(label='psi')
        efp_wfn_independent_energy = efpene['total'] - efpene['ind']
        self.set_energies("EFP", efpene['total'])

        SCFE = self.get_energies("Total Energy")
        SCFE += efp_wfn_independent_energy
        self.set_energies("Total Energy", SCFE)
        core.print_out(efpobj.energy_summary(scfefp=SCFE, label='psi'))

        core.set_variable(
            'EFP ELST ENERGY',
            efpene['electrostatic'] + efpene['charge_penetration'] +
            efpene['electrostatic_point_charges'])
        core.set_variable('EFP IND ENERGY', efpene['polarization'])
        core.set_variable('EFP DISP ENERGY', efpene['dispersion'])
        core.set_variable('EFP EXCH ENERGY', efpene['exchange_repulsion'])
        core.set_variable('EFP TOTAL ENERGY', efpene['total'])
        core.set_variable('CURRENT ENERGY', efpene['total'])

    core.print_out("\n  ==> Post-Iterations <==\n\n")

    self.check_phases()
    self.compute_spin_contamination()
    self.frac_renormalize()
    reference = core.get_option("SCF", "REFERENCE")

    energy = self.get_energies("Total Energy")

    #    fail_on_maxiter = core.get_option("SCF", "FAIL_ON_MAXITER")
    #    if converged or not fail_on_maxiter:
    #
    #        if print_lvl > 0:
    #            self.print_orbitals()
    #
    #        if converged:
    #            core.print_out("  Energy converged.\n\n")
    #        else:
    #            core.print_out("  Energy did not converge, but proceeding anyway.\n\n")

    if core.get_option('SCF', 'PRINT') > 0:
        self.print_orbitals()

    is_dfjk = core.get_global_option('SCF_TYPE').endswith('DF')
    core.print_out("  @%s%s Final Energy: %20.14f" %
                   ('DF-' if is_dfjk else '', reference, energy))
    # if (perturb_h_) {
    #     core.print_out(" with %f %f %f perturbation" %
    #                    (dipole_field_strength_[0], dipole_field_strength_[1], dipole_field_strength_[2]))
    # }
    core.print_out("\n\n")
    self.print_energies()

    self.clear_external_potentials()
    if core.get_option('SCF', 'PCM'):
        calc_type = core.PCM.CalcType.Total
        if core.get_option("PCM", "PCM_SCF_TYPE") == "SEPARATE":
            calc_type = core.PCM.CalcType.NucAndEle
        Dt = self.Da().clone()
        Dt.add(self.Db())
        _, Vpcm = self.get_PCM().compute_PCM_terms(Dt, calc_type)
        self.push_back_external_potential(Vpcm)

    # Properties
    #  Comments so that autodoc utility will find these PSI variables
    #  Process::environment.globals["SCF DIPOLE X"] =
    #  Process::environment.globals["SCF DIPOLE Y"] =
    #  Process::environment.globals["SCF DIPOLE Z"] =
    #  Process::environment.globals["SCF QUADRUPOLE XX"] =
    #  Process::environment.globals["SCF QUADRUPOLE XY"] =
    #  Process::environment.globals["SCF QUADRUPOLE XZ"] =
    #  Process::environment.globals["SCF QUADRUPOLE YY"] =
    #  Process::environment.globals["SCF QUADRUPOLE YZ"] =
    #  Process::environment.globals["SCF QUADRUPOLE ZZ"] =

    # Orbitals are always saved, in case an MO guess is requested later
    # save_orbitals()

    # Shove variables into global space
    for k, v in self.variables().items():
        core.set_variable(k, v)

    self.finalize()
    if self.V_potential():
        self.V_potential().clear_collocation_cache()

    core.print_out("\nComputation Completed\n")

    return energy
Ejemplo n.º 24
0
def parse_arbitrary_order(name: str) -> Tuple[str, Union[str, int, None]]:
    r"""Function to parse name string into a method family like CI or MRCC and specific
    level information like 4 for CISDTQ or MRCCSDTQ.

    """

    name = name.lower()
    mtdlvl_mobj = re.match(r"""\A(?P<method>[a-z]+)(?P<level>\d+)\Z""", name)

    # matches 'mrccsdt(q)'
    if name.startswith('mrcc'):

        # avoid undoing fn's good work when called twice
        if name == 'mrcc':
            return name, None

        # grabs 'sdt(q)'
        ccfullname = name[4:]

        # A negative order indicates perturbative method
        methods = {
            'sd'          : { 'method': 1, 'order':  2, 'fullname': 'CCSD'         },
            'sdt'         : { 'method': 1, 'order':  3, 'fullname': 'CCSDT'        },
            'sdtq'        : { 'method': 1, 'order':  4, 'fullname': 'CCSDTQ'       },
            'sdtqp'       : { 'method': 1, 'order':  5, 'fullname': 'CCSDTQP'      },
            'sdtqph'      : { 'method': 1, 'order':  6, 'fullname': 'CCSDTQPH'     },
            'sd(t)'       : { 'method': 3, 'order': -3, 'fullname': 'CCSD(T)'      },
            'sdt(q)'      : { 'method': 3, 'order': -4, 'fullname': 'CCSDT(Q)'     },
            'sdtq(p)'     : { 'method': 3, 'order': -5, 'fullname': 'CCSDTQ(P)'    },
            'sdtqp(h)'    : { 'method': 3, 'order': -6, 'fullname': 'CCSDTQP(H)'   },
            'sd(t)_l'     : { 'method': 4, 'order': -3, 'fullname': 'CCSD(T)_L'    },
            'sdt(q)_l'    : { 'method': 4, 'order': -4, 'fullname': 'CCSDT(Q)_L'   },
            'sdtq(p)_l'   : { 'method': 4, 'order': -5, 'fullname': 'CCSDTQ(P)_L'  },
            'sdtqp(h)_l'  : { 'method': 4, 'order': -6, 'fullname': 'CCSDTQP(H)_L' },
            'sdt-1a'      : { 'method': 5, 'order':  3, 'fullname': 'CCSDT-1a'     },
            'sdtq-1a'     : { 'method': 5, 'order':  4, 'fullname': 'CCSDTQ-1a'    },
            'sdtqp-1a'    : { 'method': 5, 'order':  5, 'fullname': 'CCSDTQP-1a'   },
            'sdtqph-1a'   : { 'method': 5, 'order':  6, 'fullname': 'CCSDTQPH-1a'  },
            'sdt-1b'      : { 'method': 6, 'order':  3, 'fullname': 'CCSDT-1b'     },
            'sdtq-1b'     : { 'method': 6, 'order':  4, 'fullname': 'CCSDTQ-1b'    },
            'sdtqp-1b'    : { 'method': 6, 'order':  5, 'fullname': 'CCSDTQP-1b'   },
            'sdtqph-1b'   : { 'method': 6, 'order':  6, 'fullname': 'CCSDTQPH-1b'  },
            '2'           : { 'method': 7, 'order':  2, 'fullname': 'CC2'          },
            '3'           : { 'method': 7, 'order':  3, 'fullname': 'CC3'          },
            '4'           : { 'method': 7, 'order':  4, 'fullname': 'CC4'          },
            '5'           : { 'method': 7, 'order':  5, 'fullname': 'CC5'          },
            '6'           : { 'method': 7, 'order':  6, 'fullname': 'CC6'          },
            'sdt-3'       : { 'method': 8, 'order':  3, 'fullname': 'CCSDT-3'      },
            'sdtq-3'      : { 'method': 8, 'order':  4, 'fullname': 'CCSDTQ-3'     },
            'sdtqp-3'     : { 'method': 8, 'order':  5, 'fullname': 'CCSDTQP-3'    },
            'sdtqph-3'    : { 'method': 8, 'order':  6, 'fullname': 'CCSDTQPH-3'   }
        }  # yapf: disable

        # looks for 'sdt(q)' in dictionary
        if ccfullname in methods:
            return 'mrcc', methods[ccfullname]
        else:
            raise ValidationError(f"""MRCC method '{name}' invalid.""")

    elif mtdlvl_mobj:
        namestump = mtdlvl_mobj.group('method')
        namelevel = int(mtdlvl_mobj.group('level'))

        if namestump in ['mp', 'zapt', 'ci']:
            # Let mp2, mp3, mp4 pass through to select functions
            if namestump == 'mp' and namelevel in [2, 3, 4]:
                return name, None
            # Otherwise return method and order
            else:
                return namestump, namelevel
        else:
            return name, None
    else:
        return name, None
Ejemplo n.º 25
0
def scf_xtpl_karton_2(functionname: str, zLO: int, valueLO: Extrapolatable, zHI: int, valueHI: Extrapolatable, verbose: int = 1, alpha: Optional[float] = None) -> Extrapolatable:
    r"""Extrapolation scheme using root-power form for reference energies with two adjacent
    zeta-level bases. Used by :py:func:`~psi4.cbs`.

    Parameters
    ----------
    functionname
        Name of the CBS component (e.g., 'HF') used in summary printing.
    zLO
        Zeta number of the smaller basis set in 2-point extrapolation.
    valueLO
        Energy, gradient, or Hessian value at the smaller basis set in 2-point
        extrapolation.
    zHI
        Zeta number of the larger basis set in 2-point extrapolation
        Must be `zLO + 1`.
    valueHI
        Energy, gradient, or Hessian value at the larger basis set in 2-point
        extrapolation.
    verbose
        Controls volume of printing.
    alpha
        Overrides the default :math:`\alpha = 6.3`

    Returns
    -------
    float or ndarray
        Eponymous function applied to input zetas and values; type from `valueLO`.

    Notes
    -----
    The extrapolation is calculated according to [3]_:
    :math:`E_{total}^X = E_{total}^{\infty} + \beta e^{-\alpha\sqrt{X}}, \alpha = 6.3`

    References
    ----------

    .. [3] Karton, Martin, Theor. Chem. Acc. 115 (2006) 330-333,
       DOI: 10.1007/s00214-005-0028-6

    """

    if type(valueLO) != type(valueHI):
        raise ValidationError(
            f"scf_xtpl_karton_2: Inputs must be of the same datatype! ({type(valueLO)}, {type(valueHI)})")

    if alpha is None:
        alpha = 6.30

    # prior to April 2022, this wrong expression was used
    # beta_division = 1 / (math.exp(-1 * alpha) * (math.exp(math.sqrt(zHI)) - math.exp(math.sqrt(zLO))))
    beta_division = 1 / (math.exp(-1 * alpha * math.sqrt(zHI)) - math.exp(-1 * alpha * math.sqrt(zLO)))
    beta_mult = math.exp(-1 * alpha * math.sqrt(zHI))

    if isinstance(valueLO, float):
        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose:
            # Output string with extrapolation parameters
            cbsscheme = ''
            cbsscheme += """\n   ==> Karton 2-point power form SCF extrapolation for method: %s <==\n\n""" % (
                functionname.upper())
            cbsscheme += """   LO-zeta (%s) Energy:               % 16.12f\n""" % (str(zLO), valueLO)
            cbsscheme += """   HI-zeta (%s) Energy:               % 16.12f\n""" % (str(zHI), valueHI)
            cbsscheme += """   Alpha (exponent) Value:           % 16.12f\n""" % (alpha)
            cbsscheme += """   Beta (coefficient) Value:         % 16.12f\n\n""" % (beta)

            name_str = "%s/(%s,%s)" % (functionname.upper(), _zeta_val2sym[zLO].upper(), _zeta_val2sym[zHI].upper())
            cbsscheme += """   @Extrapolated """
            cbsscheme += name_str + ':'
            cbsscheme += " " * (18 - len(name_str))
            cbsscheme += """% 16.12f\n\n""" % value
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        return value

    elif isinstance(valueLO, (core.Matrix, core.Vector)):
        valueLO = valueLO.to_array()
        valueHI = valueHI.to_array()

        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose > 2:
            cbsscheme = f"""\n   ==> Karton 2-point power SCF extrapolation for method: {functionname.upper()} <==\n"""
            cbsscheme += f"""\n   LO-zeta ({zLO}) Data\n"""
            cbsscheme += nppp(valueLO)
            cbsscheme += f"""\n   HI-zeta ({zHI}) Data\n"""
            cbsscheme += nppp(valueHI)

            cbsscheme += f"""\n   Alpha (exponent) Value:          {alpha:16.8f}"""
            cbsscheme += f"""\n   Beta Data\n"""
            cbsscheme += nppp(beta)
            cbsscheme += f"""\n   Extrapolated Data\n"""
            cbsscheme += nppp(value)
            cbsscheme += "\n"
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        value = core.Matrix.from_array(value)
        return value

    else:
        raise ValidationError(f"scf_xtpl_Karton_2: datatype is not recognized '{type(valueLO)}'.")
Ejemplo n.º 26
0
def sort_derivative_type(
    target_dertype,
    highest_analytic_dertype: int,
    user_dertype: Union[int, None],
    proc_messages: Dict[int, str],
    verbose: int = 1,
) -> Tuple[int, int]:
    r"""Find the best derivative level (0, 1, 2) and strategy (analytic, finite difference)
    to achieve `target_dertype` within constraints of `user_dertype` and `highest_analytic_dertype`.

    Parameters
    ----------
    target_dertype: {'energy', 'gradient', 'hessian'}
        Type of calculation targeted by driver.
    highest_analytic_dertype
        Highest derivative level program can provide analytically.
    user_dertype
        User input on which derivative level should be employed to achieve `target_dertype`.
    proc_messages
        Dertype-indexed detailed message to be appended to user error.
    verbose
        Control amount of output printing.

    Returns
    -------
    tuple : (int, int)
        "second" is highest accessible derivative level `highest_analytic_dertype` to achieve
        `target_dertype` "first" within constraints of `user_dertype`. When
        "first" == "second", analytic is the best strategy, otherwise finite
        difference of target "first" by means of "second".

    Raises
    ------
    ValidationError
        When input validation fails. When `user_dertype` exceeds `target_dertype`.
    MissingMethodError
        When `user_dertype` exceeds what available for `method`.

    """
    egh = ['energy', 'gradient', 'hessian']

    # Validate input dertypes
    if target_dertype not in egh:
        raise ValidationError(
            f"target_dertype ({target_dertype}) must be in {egh}.")

    if not (user_dertype is None or isinstance(user_dertype, int)):
        raise ValidationError(
            f"user_dertype ({user_dertype}) should only be None or int!")

    dertype = highest_analytic_dertype

    # Negotiations. In particular:
    # * don't return higher derivative than targeted by driver
    # * don't return higher derivative than spec'd by user. that is, user can downgrade derivative
    # * alert user to conflict between driver and user_dertype

    if user_dertype is not None and user_dertype > egh.index(target_dertype):
        raise ValidationError(
            f'User dertype ({user_dertype}) excessive for target calculation ({target_dertype})'
        )

    if egh.index(target_dertype) < dertype:
        dertype = egh.index(target_dertype)

    if user_dertype is not None:
        if user_dertype <= dertype:
            dertype = user_dertype
        else:
            msg = f"""Derivative level requested ({user_dertype}) exceeds that available ({highest_analytic_dertype})."""
            if proc_messages.get(user_dertype, False):
                msg += f""" Details: {proc_messages[user_dertype]}."""
            raise MissingMethodError(msg)

    # hack section
    if core.get_global_option('PCM') and dertype != 0:
        core.print_out(
            '\nPCM analytic gradients are not implemented yet, re-routing to finite differences.\n'
        )
        dertype = 0

    if core.get_global_option("RELATIVISTIC") in ["X2C", "DKH"]:
        core.print_out(
            "\nRelativistic analytic gradients are not implemented yet, re-routing to finite differences.\n"
        )
        dertype = 0

    if verbose > 1:
        print(
            f'Derivative negotiations: target/driver={egh.index(target_dertype)}, best_available={highest_analytic_dertype}, user_dertype={user_dertype} -> FINAL={dertype}'
        )

    return (egh.index(target_dertype), dertype)
Ejemplo n.º 27
0
def check_consistency(func_dictionary):
    """
    This checks the consistency of the definitions of exchange and correlation components
    of the functional, including detecting duplicate requests for LibXC params, inconsistent
    requests for HF exchange and missing correlation. It also makes sure that names of methods
    passed in using dft_functional={} syntax have a non-implemented name.
    """
    # 0a) make sure method name is set:
    if "name" not in func_dictionary:
        raise ValidationError(
            "SCF: No method name was specified in functional dictionary.")
    else:
        name = func_dictionary["name"]
        # 0b) make sure provided name is unique:
        if (name.lower()
                in functionals.keys()) and (func_dictionary
                                            not in functionals.values()):
            raise ValidationError(
                "SCF: Provided name for a custom dft_functional matches an already defined one: %s."
                % (name))

    # 1a) sanity checks definition of xc_functionals
    if "xc_functionals" in func_dictionary:
        if "x_functionals" in func_dictionary or "x_hf" in func_dictionary:
            raise ValidationError(
                "SCF: Duplicate specification of exchange (XC + X) in functional %s."
                % (name))
        elif "c_functionals" in func_dictionary or "c_mp2" in func_dictionary:
            raise ValidationError(
                "SCF: Duplicate specification of correlation (XC + C) in functional %s."
                % (name))

    # 1b) require at least an empty exchange functional entry or X_HF
    elif "x_functionals" not in func_dictionary and "x_hf" not in func_dictionary:
        raise ValidationError("SCF: No exchange specified in functional %s." %
                              (name))

    # 1c) require at least an empty correlation functional entry or C_MP2
    elif "c_functionals" not in func_dictionary and "c_mp2" not in func_dictionary:
        raise ValidationError(
            "SCF: No correlation specified in functional %s." % (name))

    # 2) use_libxc handling:
    use_libxc = 0
    if "x_functionals" in func_dictionary:
        for item in func_dictionary["x_functionals"]:
            if "use_libxc" in func_dictionary["x_functionals"][item] and \
            func_dictionary["x_functionals"][item]["use_libxc"]:
                use_libxc += 1

    # 2a) only 1 component in x_functionals can have "use_libxc": True to prevent libxc conflicts
    if use_libxc > 1:
        raise ValidationError(
            "SCF: Duplicate request for libxc exchange parameters in functional %s."
            % (name))

    # 2b) if "use_libxc" is defined in x_functionals, there shouldn't be an "x_hf" key
    elif use_libxc == 1 and "x_hf" in func_dictionary:
        raise ValidationError(
            "SCF: Inconsistent definition of exchange in functional %s." %
            (name))

    # 2c) ensure libxc params requested in "x_hf" are for a functional that is included in "x_functionals"
    elif "x_hf" in func_dictionary and "use_libxc" in func_dictionary["x_hf"] \
    and func_dictionary["x_hf"]["use_libxc"] not in func_dictionary["x_functionals"]:
        raise ValidationError(
            "SCF: Libxc parameters requested for an exchange functional not defined as a component of %s."
            % (name))

    # 3) checks would be caught at runtime or involve only formatting.
    #    included here to preempt driver definition problems, if specific fctl not in tests.
    # 3a) check formatting for citation
    if "citation" in func_dictionary:
        cit = func_dictionary["citation"]
        if cit and not (cit.startswith('    ') and cit.endswith('\n')):
            raise ValidationError(
                f"SCF: All citations should have the form '    A. Student, B. Prof, J. Goodstuff Vol, Page, Year\n', not : {cit}"
            )
    if "dispersion" in func_dictionary:
        disp = func_dictionary["dispersion"]
        # 3b) check dispersion type present and known
        if "type" not in disp or disp["type"] not in _dispersion_aliases:
            raise ValidationError(
                f"SCF: Dispersion type ({disp['type']}) should be among ({_dispersion_aliases.keys()})"
            )
    # 3c) check dispersion params complete
        allowed_params = sorted(
            dashcoeff[_dispersion_aliases[disp["type"]]]["default"].keys())
        if "params" not in disp or sorted(
                disp["params"].keys()) != allowed_params:
            raise ValidationError(
                f"SCF: Dispersion params ({list(disp['params'].keys())}) must include all ({allowed_params})"
            )
    # 3d) check formatting for dispersion citation
        if "citation" in disp:
            cit = disp["citation"]
            if cit and not ((cit.startswith('    ') and cit.endswith('\n')) or
                            re.match(r"^10.\d{4,9}/[-._;()/:A-Z0-9]+$", cit)):
                raise ValidationError(
                    f"SCF: All citations should have the form '    A. Student, B. Prof, J. Goodstuff Vol, Page, Year\n', not : {cit}"
                )
Ejemplo n.º 28
0
def build_superfunctional(name, restricted, npoints=None, deriv=1):
    if npoints is None:
        npoints = core.get_option("SCF", "DFT_BLOCK_MAX_POINTS")

    # We are a XC generating function

    if hasattr(name, '__call__'):
        custom_error = "SCF: Custom functional type must either be a SuperFunctional or a tuple of (SuperFunctional, (base_name, dashparam))."
        sfunc = name("name", npoints, deriv, restricted)

        # Without Dispersion
        if isinstance(sfunc, core.SuperFunctional):
            sup = (sfunc, False)
        # With Dispersion
        elif isinstance(sfunc[0], core.SuperFunctional):
            sup = sfunc
            # Can we validate dispersion?
        else:
            raise ValidationError(custom_error)

        # Double check that the SuperFunctional is correctly sized (why dont we always do this?)
        sup[0].set_max_points(npoints)
        sup[0].set_deriv(deriv)
        sup[0].allocate()

    # Check for supplied dict_func functionals
    elif isinstance(name, dict):
        sup = dft_builder.build_superfunctional_from_dictionary(
            name, npoints, deriv, restricted)
    # Check for pre-defined dict-based functionals
    elif name.lower() in dft_builder.functionals:
        sup = dft_builder.build_superfunctional_from_dictionary(
            dft_builder.functionals[name.lower()], npoints, deriv, restricted)
    else:
        raise ValidationError("SCF: Functional (%s) not found!" % name)

    if (core.get_global_option('INTEGRAL_PACKAGE')
            == 'ERD') and (sup[0].is_x_lrc() or sup[0].is_c_lrc()):
        raise ValidationError(
            "INTEGRAL_PACKAGE ERD does not play nicely with omega ERI's, so stopping."
        )

    # Lock and unlock the functional
    sup[0].set_lock(False)

    # Set options
    if core.has_option_changed("SCF", "DFT_OMEGA") and sup[0].is_x_lrc():
        omega = core.get_option("SCF", "DFT_OMEGA")
        sup[0].set_x_omega(omega)

        # We also need to loop through all of the exchange functionals
        if sup[0].is_libxc_func():
            # Full libxc funcs are dropped in c_functionals (smooth move!)
            sup[0].c_functionals()[0].set_omega(omega)
        else:
            for x_func in sup[0].x_functionals():
                x_func.set_omega(omega)
    if core.has_option_changed("SCF", "DFT_OMEGA_C") and sup[0].is_c_lrc():
        sup[0].set_c_omega(core.get_option("SCF", "DFT_OMEGA_C"))

    if core.has_option_changed("SCF", "DFT_ALPHA"):
        sup[0].set_x_alpha(core.get_option("SCF", "DFT_ALPHA"))
    if core.has_option_changed("SCF", "DFT_ALPHA_C"):
        sup[0].set_c_alpha(core.get_option("SCF", "DFT_ALPHA_C"))

    # add VV10 correlation to any functional or modify existing
    # custom procedures using name 'scf' without any quadrature grid like HF will fail and are not detected
    if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS")
            and sup[0].vv10_b() > 0.0):
        if not isinstance(name, dict):
            if (name.lower() == 'hf'):
                raise ValidationError("SCF: HF with -NL not implemented")
        nl_tuple = core.get_option("SCF", "NL_DISPERSION_PARAMETERS")
        sup[0].set_vv10_b(nl_tuple[0])
        if len(nl_tuple) > 1:
            sup[0].set_vv10_c(nl_tuple[1])
        if len(nl_tuple) > 2:
            raise ValidationError(
                "too many entries in NL_DISPERSION_PARAMETERS for DFT-NL")
    elif core.has_option_changed("SCF", "DFT_VV10_B"):
        if not isinstance(name, dict):
            if (name.lower() == 'hf'):
                raise ValidationError("SCF: HF with -NL not implemented")
        vv10_b = core.get_option("SCF", "DFT_VV10_B")
        sup[0].set_vv10_b(vv10_b)
        if core.has_option_changed("SCF", "DFT_VV10_C"):
            vv10_c = core.get_option("SCF", "DFT_VV10_C")
            sup[0].set_vv10_c(vv10_c)
        if (abs(sup[0].vv10_c() - 0.0) <= 1e-8):
            core.print_out(
                "SCF: VV10_C not specified. Using default (C=0.0093)!")
            sup[0].set_vv10_c(0.0093)

    if (core.has_option_changed("SCF", "NL_DISPERSION_PARAMETERS")
            and core.has_option_changed("SCF", "DFT_VV10_B")):
        raise ValidationError(
            "SCF: Decide between NL_DISPERSION_PARAMETERS and DFT_VV10_B !!")

    # Check SCF_TYPE
    if sup[0].is_x_lrc() and (core.get_global_option("SCF_TYPE") not in [
            "DISK_DF", "MEM_DF", "DIRECT", "DF", "OUT_OF_CORE", "PK"
    ]):
        raise ValidationError(
            "SCF: SCF_TYPE (%s) not supported for range-separated functionals, plese use SCF_TYPE = 'DF' to automatically select the correct JK build."
            % core.get_global_option("SCF_TYPE"))

    if (core.get_global_option('INTEGRAL_PACKAGE')
            == 'ERD') and (sup[0].is_x_lrc()):
        raise ValidationError(
            'INTEGRAL_PACKAGE ERD does not play nicely with LRC DFT functionals, so stopping.'
        )

    sup[0].set_lock(True)

    return sup
Ejemplo n.º 29
0
def scf_xtpl_helgaker_2(functionname: str, zLO: int, valueLO: Extrapolatable, zHI: int, valueHI: Extrapolatable, verbose: int = 1, alpha: Optional[float] = None) -> Extrapolatable:
    r"""Extrapolation scheme using exponential form for reference energies with two adjacent
    zeta-level bases. Used by :py:func:`~psi4.cbs`.

    Parameters
    ----------
    functionname
        Name of the CBS component (e.g., 'HF') used in summary printing.
    zLO
        Zeta number of the smaller basis set in 2-point extrapolation.
    valueLO
        Energy, gradient, or Hessian value at the smaller basis set in 2-point
        extrapolation.
    zHI
        Zeta number of the larger basis set in 2-point extrapolation.
        Must be `zLO + 1`.
    valueHI
        Energy, gradient, or Hessian value at the larger basis set in 2-point
        extrapolation.
    verbose
        Controls volume of printing.
    alpha
        Fitted 2-point parameter. Overrides the default :math:`\alpha = 1.63`

    Returns
    -------
    float or ndarray
        Eponymous function applied to input zetas and values; type from `valueLO`.

    Notes
    -----
    The extrapolation is calculated according to [1]_:
    :math:`E_{total}^X = E_{total}^{\infty} + \beta e^{-\alpha X}, \alpha = 1.63`

    References
    ----------

    .. [1] Halkier, Helgaker, Jorgensen, Klopper, & Olsen, Chem. Phys. Lett. 302 (1999) 437-446,
       DOI: 10.1016/S0009-2614(99)00179-7

    Examples
    --------
    >>> # [1] Hartree-Fock extrapolation
    >>> psi4.energy('cbs', scf_wfn='hf', scf_basis='cc-pV[DT]Z', scf_scheme='scf_xtpl_helgaker_2')

    """

    if type(valueLO) != type(valueHI):
        raise ValidationError(
            f"scf_xtpl_helgaker_2: Inputs must be of the same datatype! ({type(valueLO)}, {type(valueHI)})")

    if alpha is None:
        alpha = 1.63

    beta_division = 1 / (math.exp(-1 * alpha * zLO) * (math.exp(-1 * alpha) - 1))
    beta_mult = math.exp(-1 * alpha * zHI)

    if isinstance(valueLO, float):
        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose:
            # Output string with extrapolation parameters
            cbsscheme = ''
            cbsscheme += """\n   ==> Helgaker 2-point exponential SCF extrapolation for method: %s <==\n\n""" % (
                functionname.upper())
            cbsscheme += """   LO-zeta (%s) Energy:               % 16.12f\n""" % (str(zLO), valueLO)
            cbsscheme += """   HI-zeta (%s) Energy:               % 16.12f\n""" % (str(zHI), valueHI)
            cbsscheme += """   Alpha (exponent) Value:           % 16.12f\n""" % (alpha)
            cbsscheme += """   Beta (coefficient) Value:         % 16.12f\n\n""" % (beta)

            name_str = "%s/(%s,%s)" % (functionname.upper(), _zeta_val2sym[zLO].upper(), _zeta_val2sym[zHI].upper())
            cbsscheme += """   @Extrapolated """
            cbsscheme += name_str + ':'
            cbsscheme += " " * (18 - len(name_str))
            cbsscheme += """% 16.12f\n\n""" % value
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        return value

    elif isinstance(valueLO, (core.Matrix, core.Vector)):
        valueLO = valueLO.to_array()
        valueHI = valueHI.to_array()

        beta = (valueHI - valueLO) * beta_division
        value = valueHI - beta * beta_mult

        if verbose > 2:
            cbsscheme = f"""\n   ==> Helgaker 2-point exponential SCF extrapolation for method: {functionname.upper()} <==\n"""
            cbsscheme += f"""\n   LO-zeta ({zLO}) Data\n"""
            cbsscheme += nppp(valueLO)
            cbsscheme += f"""\n   HI-zeta ({zHI}) Data\n"""
            cbsscheme += nppp(valueHI)

            cbsscheme += f"""\n   Alpha (exponent) Value:          {alpha:16.8f}"""
            cbsscheme += f"""\n   Beta Data\n"""
            cbsscheme += nppp(beta)
            cbsscheme += f"""\n   Extrapolated Data\n"""
            cbsscheme += nppp(value)
            cbsscheme += "\n"
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        value = core.Matrix.from_array(value)
        return value

    else:
        raise ValidationError(f"scf_xtpl_helgaker_2: datatype is not recognized '{type(valueLO)}'.")
Ejemplo n.º 30
0
def scf_xtpl_helgaker_3(functionname: str, zLO: int, valueLO: Extrapolatable, zMD: int, valueMD: Extrapolatable, zHI: int, valueHI: Extrapolatable, verbose: int = 1, alpha: Optional[float] = None) -> Extrapolatable:
    r"""Extrapolation scheme for reference energies with three adjacent zeta-level bases.
    Used by :py:func:`~psi4.cbs`.

    Parameters
    ----------
    functionname
        Name of the CBS component (e.g., 'HF') used in summary printing.
    zLO
        Zeta number of the smaller basis set in 3-point extrapolation.
    valueLO
        Energy, gradient, or Hessian value at the smaller basis set in 3-point
        extrapolation.
    zMD
        Zeta number of the medium basis set in 3-point extrapolation.
        Must be `zLO + 1`.
    valueMD
        Energy, gradient, or Hessian value at the medium basis set in 3-point
        extrapolation.
    zHI
        Zeta number of the larger basis set in 3-point extrapolation.
        Must be `zLO + 2`.
    valueHI
        Energy, gradient, or Hessian value at the larger basis set in 3-point
        extrapolation.
    verbose
        Controls volume of printing.
    alpha
        Not used.

    Returns
    -------
    float or ndarray
        Eponymous function applied to input zetas and values; type from `valueLO`.

    Notes
    -----
    The extrapolation is calculated according to [4]_:
    :math:`E_{total}^X = E_{total}^{\infty} + \beta e^{-\alpha X}, \alpha = 3.0`

    References
    ----------

    .. [4] Halkier, Helgaker, Jorgensen, Klopper, & Olsen, Chem. Phys. Lett. 302 (1999) 437-446,
       DOI: 10.1016/S0009-2614(99)00179-7

    Examples
    --------
    >>> # [1] Hartree-Fock extrapolation
    >>> psi4.energy('cbs', scf_wfn='hf', scf_basis='cc-pV[DTQ]Z', scf_scheme='scf_xtpl_helgaker_3')

    """

    if (type(valueLO) != type(valueMD)) or (type(valueMD) != type(valueHI)):
        raise ValidationError(
            f"scf_xtpl_helgaker_3: Inputs must be of the same datatype! ({type(valueLO)}, {type(valueMD)}, {type(valueHI)})"
        )

    if isinstance(valueLO, float):

        ratio = (valueHI - valueMD) / (valueMD - valueLO)
        alpha = -1 * math.log(ratio)
        beta = (valueHI - valueMD) / (math.exp(-1 * alpha * zMD) * (ratio - 1))
        value = valueHI - beta * math.exp(-1 * alpha * zHI)

        if verbose:
            # Output string with extrapolation parameters
            cbsscheme = ''
            cbsscheme += """\n   ==> Helgaker 3-point SCF extrapolation for method: %s <==\n\n""" % (
                functionname.upper())
            cbsscheme += """   LO-zeta (%s) Energy:               % 16.12f\n""" % (str(zLO), valueLO)
            cbsscheme += """   MD-zeta (%s) Energy:               % 16.12f\n""" % (str(zMD), valueMD)
            cbsscheme += """   HI-zeta (%s) Energy:               % 16.12f\n""" % (str(zHI), valueHI)
            cbsscheme += """   Alpha (exponent) Value:           % 16.12f\n""" % (alpha)
            cbsscheme += """   Beta (coefficient) Value:         % 16.12f\n\n""" % (beta)

            name_str = "%s/(%s,%s,%s)" % (functionname.upper(), _zeta_val2sym[zLO].upper(), _zeta_val2sym[zMD].upper(),
                                          _zeta_val2sym[zHI].upper())
            cbsscheme += """   @Extrapolated """
            cbsscheme += name_str + ':'
            cbsscheme += " " * (18 - len(name_str))
            cbsscheme += """% 16.12f\n\n""" % value
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        return value

    elif isinstance(valueLO, (core.Matrix, core.Vector)):
        valueLO = np.array(valueLO)
        valueMD = np.array(valueMD)
        valueHI = np.array(valueHI)

        nonzero_mask = np.abs(valueHI) > 1.e-14
        top = (valueHI - valueMD)[nonzero_mask]
        bot = (valueMD - valueLO)[nonzero_mask]

        ratio = top / bot
        alpha = -1 * np.log(np.abs(ratio))
        beta = top / (np.exp(-1 * alpha * zMD) * (ratio - 1))
        np_value = valueHI.copy()
        np_value[nonzero_mask] -= beta * np.exp(-1 * alpha * zHI)
        np_value[~nonzero_mask] = 0.0

        if verbose > 2:
            cbsscheme = f"""\n   ==> Helgaker 3-point power SCF extrapolation for method: {functionname.upper()} <==\n"""
            cbsscheme += f"""\n   LO-zeta ({zLO}) Data\n"""
            cbsscheme += nppp(valueLO)
            cbsscheme += f"""\n   MD-zeta ({zMD}) Data\n"""
            cbsscheme += nppp(valueMD)
            cbsscheme += f"""\n   HI-zeta ({zHI}) Data\n"""
            cbsscheme += nppp(valueHI)

            cbsscheme += f"""\n   Alpha Data\n"""
            cbsscheme += nppp(alpha)
            cbsscheme += f"""\n   Beta Data\n"""
            cbsscheme += nppp(beta)
            cbsscheme += f"""\n   Extrapolated Data\n"""
            cbsscheme += nppp(np_value)
            cbsscheme += "\n"
            core.print_out(cbsscheme)
            logger.debug(cbsscheme)

        ## Build and set from numpy routines
        #value = core.Matrix(*valueHI.shape)
        #value_view = np.asarray(value)
        #value_view[:] = np_value
        #return value

        np_value = core.Matrix.from_array(np_value)
        return np_value

    else:
        raise ValidationError(f"scf_xtpl_helgaker_3: datatype is not recognized '{type(valueLO)}'.")