Repository navigation
Fix #6286: avoid redundant libxc init_func calls in per-point evaluation paths - #8082
Merged
Merged
Conversation
…ppers issue deepmodeling#6286 reported that init_func was called thousands of times after SCF convergence. The root cause is that the per-point libxc wrappers (xc_spin_libxc, gcxc_libxc, gcxc_spin_libxc, tau_xc, tau_xc_spin) called init_func -> evaluate(1 point) -> finish_func inside every grid point of the OpenMP loops. Since init_func only depends on the functional id, polarization, and runtime parameters (alpha, omega) -- none of which vary across grid points -- it only needs to run once per thread. Changes: - Add funcs-taking overloads to all five per-point wrappers; the old func_id overloads become thin wrappers that init/evaluate/finish. - In gradcorr_xc_kernel, init the functional once per OpenMP thread (inside #pragma omp parallel, before the for) and reuse it for all points that thread handles. - In v_xc's nspin==4 branch, split #pragma omp parallel for into parallel { init; for } so init runs once per thread. - Add a std::atomic<int> init counter with get_and_reset_init_count() for tests to assert per-point paths do not re-initialize. - Add unit tests: funcs-overload vs func_id-overload bit-equality for LDA/GGA/mGGA, and an init-count assertion (old=1, new=0). init_func call count on the per-point paths drops from O(nrxx * SCF) to O(nthreads * SCF). No change to physical results. Verification: make -j 30 (ENABLE_LIBXC=ON, libxc 7.0.0) exit 0; all 10 XC test executables pass (VXC 18, GRADCORR 3, PBE 23, PZ_SPN 14, SCANL_LAPL 5, HSE 9, SCAN 1, LAPL 4, NCGGA_RADIAL 9, NCGGA_DISCRETE_FD 40); code_quality_score 8/8 files pass (avg 80.6, min 61).
Critsium-xy
reviewed
Oct 8, 2026
…#8082) - tau_xc/tau_xc_spin funcs overloads take hybrid_alpha explicitly instead of reading the XC_Functional global for SCAN0 scaling - revert the unreachable use_libxc branch change in v_xc (nspin==4) and the now-unused xc_spin_libxc funcs overload - drop the init_func call counter from the production path - only init libxc in gradcorr when a libxc branch runs (nspin0 != 1 || is_stress) - gcxc_libxc func_id overload returns before init_func for negligible rho/grho, as before - remove const_cast: Libxc >= 5.1.7 takes const xc_func_type* - replace self-comparison tests with a libxc gradcorr regression test (references from the per-point implementation) and a SCAN0 test checking the hybrid_alpha argument is used Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Critsium-xy
approved these changes
Oct 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix #6286
Description:
Issue #6286 reported that after SCF convergence, XC_Functional_Libxc::init_func was called over 3000 times while running the 08_EXX/01_GO_S1_HSE test.
Root cause: the per-point libxc wrappers (xc_spin_libxc, gcxc_libxc, gcxc_spin_libxc, tau_xc, tau_xc_spin) called init_func -> evaluate on a single grid point -> finish_func inside the grid-point OpenMP loop. init_func only depends on the functional id, polarization, and runtime parameters (hybrid_alpha, hse_omega), none of which vary across grid points, so it needs to run only once per thread rather than once per point.
Changes:
Result: init_func calls on the per-point paths drop from O(nrxx * SCF steps) to O(nthreads * SCF steps), eliminating the 3000+ calls observed in the issue. Physical results are unchanged because the functional evaluation itself is identical.
Verification:
Note: this PR addresses only the per-point (A-class) redundant init_func calls. The remaining per-SCF-step init_func calls in v_xc_libxc and related whole-grid functions (B-class) are left for a follow-up, where a cached RAII functional handle can reduce them to once per calculation.