Skip to content

Honour the range-separation omega in GPU libxc functionals (#815) - #923

Merged
sunqm merged 1 commit into
pyscf:masterfrom
michalkrompiec:fix-rsh-omega
Sep 25, 2026
Merged

sunqm merged 1 commit into
pyscf:masterfrom
michalkrompiec:fix-rsh-omega

Conversation

@michalkrompiec

@michalkrompiec michalkrompiec commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Problem

The omega of a range-separated functional reaches the exact exchange but never the semilocal part, which stays at libxc's built-in default. Both ways of setting omega are affected:

  1. Declared in xc_code, e.g. LR_HF(0.3)+ITYH,LYP. The string is parsed correctly, but omega is never given to libxc, so ITYH runs at its default of 0.2. No override is needed to hit this.
  2. Overridden with mf.omega. The override is applied to the exact exchange only.

In both cases the two halves of the exchange are range-separated at different omegas, and nothing warns. Only omegas that differ from libxc's default are affected.

Measured on an A100 with gpu4pyscf 1.8.1, against pyscf on the CPU:

case GPU − CPU
LR_HF(0.3)+ITYH,LYP, omega declared in the string: max pointwise exc / vxc error (test_libxc density) 0.069 / 0.052
LRC_WPBEH, ni.omega = 0.1: max pointwise exc / vxc error (same density) 0.045 / 0.045
LC_WPBE, mf.omega = 0.175: RKS SCF energy (water/def2-TZVPP) 1.069 Ha

Fix

The same semantics as pyscf.dft.libxc.XCFunctionalCache and LIBXC_xc_func_set_params: an override takes precedence over the omega declared in xc_code, and zero leaves libxc's default in place.

  • libxc.XCfun(xc, spin, omega=0): when omega is nonzero, set _omega on the functional and on each func_aux that exposes it. The name is looked up with xc_func_info_get_ext_params_name, because the bundled libxc does not export xc_func_find_ext_params_name.
  • numint._init_xcfuns(xc_code, spin, omega=None): resolves omega as above. Omega is part of the cache key, so functionals with different omegas are separate cached objects.
  • NumInt._init_xcfuns and NumInt2C._init_xcfuns pass self.omega. The PBC and multigrid classes reuse NumInt._init_xcfuns.
  • eval_xc_eff passes omega to _init_xcfuns and to the CPU fallback. NumInt.eval_xc_eff now forwards its omega= argument, which it previously dropped.
  • to_cpu() on NumInt, NumInt2C and the PBC KNumInt/NumInt keeps omega.

Tests

  • test_libxc.py, GPU vs CPU exc/vxc at the existing 1e-10 bound:
    • an omega declared in xc_code with no override (RKS and UKS);
    • overrides on LRC_WPBEH (RKS and UKS), CAM_B3LYP and an ITYH string;
    • an override not leaking into the next call through the functional cache;
    • to_cpu() keeping omega.
  • test_rks.py: an LC_WPBE SCF with mf.omega = 0.175 against mf.to_cpu(), to 1e-6 Ha.

On an A100, with the patch applied to v1.8.1:

  • all four new tests fail without the fix and pass with it;
  • test_libxc, test_rks, test_numint and test_numint2c show no regressions;
  • test_numint::test_sparse_index fails with and without the patch: its exact-float fingerprint differs in the 14th digit.

NumInt2C and the PBC paths get the same code change but are not exercised by these tests.

Note: LC_WPBE vxc at its default omega

The libxc override test uses LRC_WPBEH rather than LC_WPBE because, independently of this change, LC_WPBE's GPU vxc differs from pyscf by about 2e-9 at its default omega of 0.4. That is above test_libxc's 1e-10 bound. LRC_WPBEH, CAM_B3LYP and ITYH agree to about 1e-15. This may be worth a separate look.

Fixes #815

The omega declared in xc_code, or set as mf.omega, reached the exact
exchange but never the semilocal functional, which stayed at libxc's
default. Set it as pyscf's LIBXC_xc_func_set_params does, key the
functional cache on omega, forward omega through eval_xc_eff and the CPU
fallback, and keep it in to_cpu.
@sunqm
sunqm merged commit c2ea863 into pyscf:master Sep 25, 2026
4 checks passed
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.

mf.omega override on range-separated functionals gives wrong energies on GPU (works on CPU)

2 participants