Skip to content

Document periodic real-space cutoffs, ATM behavior, and unit conventions across CLI, Python, and ASE#168

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-energy-correction-inconsistency
Draft

Document periodic real-space cutoffs, ATM behavior, and unit conventions across CLI, Python, and ASE#168
Copilot wants to merge 3 commits intomainfrom
copilot/fix-energy-correction-inconsistency

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 11, 2026

Periodic D3 energies were easy to compare incorrectly because the executable, Python API, ASE calculator, and VASP can differ in ATM usage, cutoff control, and units. This adds a focused guide that makes those differences explicit and shows how to reproduce pairwise-only vs ATM-enabled results with consistent cutoffs.

  • New guide: periodic cutoff behavior

    • Added doc/guide/periodic-cutoffs.rst
    • Explains the default periodic cutoffs used by the library:
      • disp2 = 60 Bohr
      • disp3 = 40 Bohr
      • cn = 40 Bohr
    • Calls out the main comparison traps:
      • executable is pairwise-only unless --atm is passed
      • Python/ASE method-based setups default to ATM-enabled parameters
      • CLI, Python API, and ASE report energies in different units
  • Cross-interface examples

    • Added concise examples for:
      • s-dftd3
      • low-level Python API via DispersionModel.set_realspace_cutoff(...)
      • ASE via DFTD3(realspace_cutoff=...)
    • Shows both:
      • pairwise D3(BJ)
      • D3(BJ)-ATM
    • Uses multiple cutoff sets so users can inspect cutoff dependence directly
  • Unit conventions clarified

    • Documented cutoff and result units in one place:
      • CLI: Hartree output
      • Python API: Bohr inputs, Hartree output
      • ASE: Angstrom cutoff inputs, eV outputs
    • Tightened Python/ASE API docstrings so generated reference docs state the same unit conventions
  • Docs navigation

    • Added the new page to the How-to index
    • Linked it from the Python API docs as the reference for periodic cutoff / ATM comparisons

Example from the new guide:

from ase.io import read
from ase.units import Bohr, Hartree
from dftd3.interface import DispersionModel, RationalDampingParam

atoms = read("CONTCAR")
model = DispersionModel(
    atoms.numbers,
    atoms.positions / Bohr,
    lattice=atoms.cell.array / Bohr,
    periodic=atoms.pbc,
)

params = {
    "D3(BJ)": RationalDampingParam(method="PBE", atm=False),
    "D3(BJ)-ATM": RationalDampingParam(method="PBE", atm=True),
}

for disp2, disp3, cn in [(60.0, 40.0, 40.0), (95.0, 70.0, 70.0)]:
    model.set_realspace_cutoff(disp2=disp2, disp3=disp3, cn=cn)
    for label, param in params.items():
        energy = model.get_dispersion(param=param, grad=False)["energy"]
        print(label, f"{energy:.12f} Eh", f"{energy * Hartree:.8f} eV")

Rendered guide:
Periodic cutoff guide

Original prompt

This section details on the original issue you should resolve

<issue_title>Inconsistent energy correction between executable, Python API, and VASP</issue_title>
<issue_description>I am getting some inconsistent energy corrections depending on how I fetch them. @tgmaxson, this looks far less notable than in #68 but tagging in case you have some wisdom to share.

With s-dftd3 1.2.1:

s-dftd3 CONTCAR --bj pbe

returns -2.0077614201867E-01 Eh or -5.463397149451446 eV (-5.463397104953209 eV with CODATA 2014, the ASE default).

from ase.io import read
from dftd3.ase import DFTD3

atoms = read("CONTCAR")
atoms.calc = DFTD3(method="PBE", damping="d3bj")
e = atoms.get_potential_energy()
print(e)

returns -5.456782359427048. This difference of ~0.0066 eV is not resolved by setting alp=14.0 in light of #150.

In VASP 6.5.1, it is using DFTD3 V3.0 Rev 1 and returning -5.46486 eV, which is closer to s-dtd3 but still different by 0.0015 eV.

CONTCAR.txt

OUTCAR.txt</issue_description>

<agent_instructions>Extend the documentation of the library by a guide on the real space cutoff for periodic calculations. Ensure that the guide can answer the key questions from the discussion in this issue. Provide examples to compute the D3 energy with and without ATM contribution for different cutoffs with the standalone executable, the Python API and the ASE calculator. Document the units used for all APIs carefully. Keep the guide concise and informative.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@awvwgk Can you verify that the cutoff values used in VASP are matching the ones used in here? I am adding a feature in #154 to expose the cutoff values in the ASE calculator. In the Python API those can already be changed using `set_realspace_cutoff` method. @awvwgk The ASE unit conversion can be somewhat confusing. `60 * Bohr` converts 60 Bohr to ASE's default unit which is Angstrom. Since I am expecting the user input in the ASE calculator in Angstrom the value gets converted to Bohr before passing it to dftd3 (via `/ Bohr`). @awvwgk The default cutoff is 60 Bohr both in Python and Fortran

!> Two-body interaction cutoff
real(wp), parameter :: disp2_default = 60.0_wp
</comment_new>
<comment_new>@awvwgk
The ATM term can be disabled by setting s9=0, you can explicitly set this one in the input to the ASE calculator.

The params_tweaks dict contains the damping parameters, at least s8, a1 and a2
must be provided
======================== =========== ============================================
Tweakable parameter Default Description
======================== =========== ============================================
s6 1.0 Scaling of the dipole-dipole dispersion
s8 None Scaling of the dipole-quadrupole dispersion
s9 1.0 Scaling of the three-body dispersion energy
a1 None Scaling of the critical radii
a2 None Offset of the critical radii
alp 14.0 Exponent of the zero damping (ATM only)
======================== =========== ============================================

Something along the lines of

from dftd3.ase import DFTD3
from dftd3.parameters import get_damping_param

param = get_damping_param("pbe", defaults=["bj"])
param["s9"] = 0.0

calc = DFTD3(damping="d3bj")
calc.set(params_tweaks=param)

Setting the disp3 threshold to zero would work, but is a quite expensive way to remove the ATM since the iterations over all triples will be done at least for L=0 in the current implementation to discard them.</comment_new>
<comment_new>@awvwgk

In other D3 package, triple interaction is not available. When one want to compare results from such packages, is it safety to set something like this?

I am keeping a comparison between different D3 packages in https://dftd3.readthedocs.io/en/latest/comparison.html, if there is one I missed or the comparison could be made more complete, let me know please.</comment_new>


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits March 11, 2026 13:21
Co-authored-by: awvwgk <28669218+awvwgk@users.noreply.github.com>
Co-authored-by: awvwgk <28669218+awvwgk@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix inconsistent energy correction between tools Document periodic real-space cutoffs, ATM behavior, and unit conventions across CLI, Python, and ASE Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Inconsistent energy correction between executable, Python API, and VASP

2 participants