Source code for tad_dftd4.disp

# This file is part of tad-dftd4.
#
# SPDX-Identifier: Apache-2.0
# Copyright (C) 2024 Grimme Group
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Dispersion energy
=================

This module provides the dispersion energy evaluation for the pairwise
interactions. It contains the main entrypoint for the dispersion energy
(:func:`.dftd4`).
"""

from __future__ import annotations

import torch
from tad_mctc.convert import any_to_tensor
from tad_mctc.ncoord import cn_d4, erf_count
from tad_mctc.typing import DD, CNFunc, CountingFunction, Tensor
from tad_multicharge import get_eeq_charges

from .cutoff import Cutoff
from .damping import Damping, Param, RationalDamping, ZeroDamping
from .dispersion import Disp
from .dispersion.d4 import D4ATMApprox
from .dispersion.twobody import TwoBodyTerm
from .model import D4Model, ModelInst, ModelKey

__all__ = ["dftd4", "get_properties"]


[docs] def dftd4( numbers: Tensor, positions: Tensor, charge: Tensor | float | int, param: Param, *, model: ModelKey | ModelInst = "d4", rcov: Tensor | None = None, r4r2: Tensor | None = None, rvdw: Tensor | None = None, q: Tensor | None = None, cutoff: Cutoff | None = None, cn_function: CNFunc = cn_d4, counting_function: CountingFunction = erf_count, damping_function: Damping = RationalDamping(), ) -> Tensor: """ Evaluate DFT-D4 dispersion energy for a (batch of) molecule(s). Parameters ---------- numbers : Tensor Atomic numbers for all atoms in the system of shape ``(..., nat)``. positions : Tensor Cartesian coordinates of all atoms (shape: ``(..., nat, 3)``). charge : Tensor Total charge of the system. param : Param DFT-D4 damping parameters. model : D4Model | D4SModel | None, optional The DFT-D4 dispersion model for the evaluation of the C6 coefficients. Defaults to ``None``, which creates :class:`tad_dftd4.model.d4.D4Model`. rcov : Tensor | None, optional Covalent radii of the atoms in the system. Defaults to ``None``, i.e., default values are used. r4r2 : Tensor | None, optional r⁴ over r² expectation values of the atoms in the system. Defaults to ``None``, i.e., default values are used. rvdw : Tensor | None, optional Pairwise van der Waals radii of the atoms in the system. Defaults to ``None``, i.e., default values are used. q : Tensor | None, optional Atomic partial charges. Defaults to ``None``, i.e., EEQ charges are calculated using the total ``charge``. cutoff : Cutoff | None, optional Collection of real-space cutoffs. Defaults to ``None``, i.e., :class:`tad_dftd4.cutoff.Cutoff` is initialized with its defaults. cn_function : CNFunction, optional Function to calculate the coordination number. Defaults to :func:`tad_mctc.ncoord.d4.cn_d4`, which uses the :func:`tad_mctc.ncoord.count.erf_count` counting function. counting_function : CountingFunction, optional Counting function used for the DFT-D4 coordination number. Defaults to the error function counting function :func:`tad_mctc.ncoord.count.erf_count`. damping_function : DampingFunction, optional Damping function to evaluate distance dependent contributions. Defaults to the Becke-Johnson rational damping function :class:`tad_dftd4.damping.functions.RationalDamping`. Returns ------- Tensor Atom-resolved DFT-D4 dispersion energy. Raises ------ ValueError Shape inconsistencies between ``numbers``, ``positions``, ``r4r2``, or, ``rcov``. """ dd: DD = {"device": positions.device, "dtype": positions.dtype} disp = Disp( cn_fn=cn_function, cn_fn_kwargs={"counting_function": counting_function}, model=model, **dd, ) twobody_term = TwoBodyTerm( damping_fn=damping_function, charge_dependent=True, ) disp.register(twobody_term) threebody_term = D4ATMApprox( damping_fn=ZeroDamping(), charge_dependent=False, ) disp.register(threebody_term) return disp.calculate( numbers, positions, any_to_tensor(charge), param, cutoff=cutoff, q=q, rcov=rcov, r4r2=r4r2, rvdw=rvdw, )
[docs] def get_properties( numbers: Tensor, positions: Tensor, charge: Tensor | float | int | None = None, cutoff: Cutoff | None = None, ) -> tuple[Tensor, Tensor, Tensor, Tensor]: """ Wrapper to evaluate properties related to this dispersion model. Parameters ---------- numbers : Tensor Atomic numbers for all atoms in the system of shape ``(..., nat)``. positions : Tensor Cartesian coordinates of all atoms (shape: ``(..., nat, 3)``). charge : Tensor | None Total charge of the system. Defaults to ``None``, i.e., ``0``. cutoff : Cutoff | None Real-space cutoff. Defaults to ``None``, i.e., the defaults for all cutoffs are used. Returns ------- (Tensor, Tensor, Tensor, Tensor) Properties related to the dispersion model: - DFT-D4 coordination number - Atomic partial charges - Atom-resolved C6 dispersion coefficients - Static polarizabilities """ dd: DD = {"device": positions.device, "dtype": positions.dtype} if cutoff is None: cutoff = Cutoff(**dd) if charge is None: charge = torch.tensor(0.0, **dd) else: charge = any_to_tensor(charge, **dd) cn = cn_d4(numbers, positions) q = get_eeq_charges(numbers, positions, charge, cutoff=cutoff.cn_eeq) model = D4Model(numbers, **dd) weights = model.weight_references(cn, q) c6 = model.get_atomic_c6(weights) alpha = model.get_polarizabilities(weights) return cn, q, c6, alpha