Skip to content

Commit 758c871

Browse files
aboys-cbdyzheng
andcommitted
fix(xc): close gga_grad 2 builtin and LibXC stress derivatives
Based-on: #7758 Co-authored-by: dyzheng <zhengdy@aisi.ac.cn>
1 parent eeb18c5 commit 758c871

4 files changed

Lines changed: 428 additions & 0 deletions

File tree

‎source/source_hamilt/module_xc/libxc_pot.cpp‎

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,170 @@
1616
#include <vector>
1717
#include <complex>
1818

19+
void XC_Functional_Libxc::gradcorr_ncgga_sf_libxc(const std::vector<int>& func_id,
20+
const std::size_t nrxx,
21+
const double tpiba,
22+
const Charge* const chr,
23+
const std::map<int, double>* scaling_factor,
24+
const double hybrid_alpha,
25+
const double hse_omega,
26+
std::vector<double>& stress_gga)
27+
{
28+
constexpr int nspin = 2;
29+
stress_gga.assign(9, 0.0);
30+
31+
std::vector<xc_func_type> funcs = XC_Functional_Libxc::init_func(func_id, XC_POLARIZED, hybrid_alpha, hse_omega);
32+
bool has_gga = false;
33+
for (const xc_func_type& func: funcs)
34+
{
35+
has_gga = has_gga || func.info->family == XC_FAMILY_GGA || func.info->family == XC_FAMILY_HYB_GGA;
36+
}
37+
if (!has_gga)
38+
{
39+
XC_Functional_Libxc::finish_func(funcs);
40+
return;
41+
}
42+
43+
// This is the same forward graph used by v_xc_libxc: the local spin map,
44+
// its projected FFT gradients, and the sigma invariants are constructed
45+
// once and shared by all Libxc components.
46+
const XC_Functional_Libxc::NclSfDiscreteData sf_data
47+
= XC_Functional_Libxc::make_ncl_sf_discrete_data(nrxx, tpiba, chr, true);
48+
const std::vector<double>& rho = sf_data.rho;
49+
const std::vector<double> sigma = XC_Functional_Libxc::convert_sigma(sf_data.spin_gradient);
50+
std::vector<double> aggregate_dsigma(3 * nrxx, 0.0);
51+
52+
for (xc_func_type& func: funcs)
53+
{
54+
if (func.info->family != XC_FAMILY_GGA && func.info->family != XC_FAMILY_HYB_GGA)
55+
{
56+
continue;
57+
}
58+
59+
constexpr double rho_threshold = 1.0e-6;
60+
constexpr double grho_threshold = 1.0e-10;
61+
xc_func_set_dens_threshold(&func, rho_threshold);
62+
const std::vector<double> sgn
63+
= XC_Functional_Libxc::cal_sgn(rho_threshold, grho_threshold, func, nspin, nrxx, rho, sigma);
64+
std::vector<double> exc(nrxx);
65+
std::vector<double> vrho(nspin * nrxx);
66+
std::vector<double> vsigma(3 * nrxx);
67+
constexpr int nr_batch_size = 1024;
68+
#ifdef _OPENMP
69+
#pragma omp parallel for schedule(static, nr_batch_size)
70+
#endif
71+
for (int ir_start = 0; ir_start < static_cast<int>(nrxx); ir_start += nr_batch_size)
72+
{
73+
const int ir_end = std::min(ir_start + nr_batch_size, static_cast<int>(nrxx));
74+
const int nrxx_thread = ir_end - ir_start;
75+
xc_gga_exc_vxc(&func,
76+
nrxx_thread,
77+
rho.data() + ir_start * nspin,
78+
sigma.data() + ir_start * 3,
79+
exc.data() + ir_start,
80+
vrho.data() + ir_start * nspin,
81+
vsigma.data() + ir_start * 3);
82+
}
83+
84+
double factor = 1.0;
85+
if (scaling_factor != nullptr)
86+
{
87+
const std::map<int, double>::const_iterator entry = scaling_factor->find(func.info->number);
88+
if (entry != scaling_factor->end())
89+
{
90+
factor = entry->second;
91+
}
92+
}
93+
const XC_Functional_Libxc::LibxcWeightedDerivatives weighted
94+
= XC_Functional_Libxc::make_libxc_weighted_derivatives(func,
95+
nspin,
96+
nrxx,
97+
sgn,
98+
rho,
99+
sigma,
100+
exc,
101+
vrho,
102+
vsigma);
103+
for (std::size_t index = 0; index < aggregate_dsigma.size(); ++index)
104+
{
105+
aggregate_dsigma[index] += factor * weighted.dsigma[index];
106+
}
107+
}
108+
109+
// For g_s=sum_A J_sA G_h x_A, a reciprocal deformation changes G_h but
110+
// not the pointwise map J. Therefore the metric derivative is exactly
111+
// sum_s h_s,l g_s,m, with h_s=dE/dg_s built from the sanitizer-reversed,
112+
// component-scaled aggregate above.
113+
#ifdef _OPENMP
114+
#pragma omp parallel
115+
{
116+
std::vector<double> local_stress(9, 0.0);
117+
#pragma omp for schedule(static, 512)
118+
for (std::size_t ir = 0; ir < nrxx; ++ir)
119+
{
120+
const std::size_t sigma_index = 3 * ir;
121+
const ModuleBase::Vector3<double>& grad_up = sf_data.spin_gradient[0][ir];
122+
const ModuleBase::Vector3<double>& grad_down = sf_data.spin_gradient[1][ir];
123+
const ModuleBase::Vector3<double> h_up
124+
= ModuleBase::e2
125+
* (2.0 * aggregate_dsigma[sigma_index] * grad_up + aggregate_dsigma[sigma_index + 1] * grad_down);
126+
const ModuleBase::Vector3<double> h_down
127+
= ModuleBase::e2
128+
* (2.0 * aggregate_dsigma[sigma_index + 2] * grad_down + aggregate_dsigma[sigma_index + 1] * grad_up);
129+
const double grad_up_component[3] = {grad_up.x, grad_up.y, grad_up.z};
130+
const double grad_down_component[3] = {grad_down.x, grad_down.y, grad_down.z};
131+
const double h_up_component[3] = {h_up.x, h_up.y, h_up.z};
132+
const double h_down_component[3] = {h_down.x, h_down.y, h_down.z};
133+
for (int l = 0; l < 3; ++l)
134+
{
135+
for (int m = 0; m <= l; ++m)
136+
{
137+
local_stress[l * 3 + m]
138+
+= h_up_component[l] * grad_up_component[m] + h_down_component[l] * grad_down_component[m];
139+
}
140+
}
141+
}
142+
#pragma omp critical(libxc_ncgga_stress_reduce)
143+
{
144+
for (int l = 0; l < 3; ++l)
145+
{
146+
for (int m = 0; m <= l; ++m)
147+
{
148+
stress_gga[l * 3 + m] += local_stress[l * 3 + m];
149+
}
150+
}
151+
}
152+
}
153+
#else
154+
for (std::size_t ir = 0; ir < nrxx; ++ir)
155+
{
156+
const std::size_t sigma_index = 3 * ir;
157+
const ModuleBase::Vector3<double>& grad_up = sf_data.spin_gradient[0][ir];
158+
const ModuleBase::Vector3<double>& grad_down = sf_data.spin_gradient[1][ir];
159+
const ModuleBase::Vector3<double> h_up
160+
= ModuleBase::e2
161+
* (2.0 * aggregate_dsigma[sigma_index] * grad_up + aggregate_dsigma[sigma_index + 1] * grad_down);
162+
const ModuleBase::Vector3<double> h_down
163+
= ModuleBase::e2
164+
* (2.0 * aggregate_dsigma[sigma_index + 2] * grad_down + aggregate_dsigma[sigma_index + 1] * grad_up);
165+
const double grad_up_component[3] = {grad_up.x, grad_up.y, grad_up.z};
166+
const double grad_down_component[3] = {grad_down.x, grad_down.y, grad_down.z};
167+
const double h_up_component[3] = {h_up.x, h_up.y, h_up.z};
168+
const double h_down_component[3] = {h_down.x, h_down.y, h_down.z};
169+
for (int l = 0; l < 3; ++l)
170+
{
171+
for (int m = 0; m <= l; ++m)
172+
{
173+
stress_gga[l * 3 + m]
174+
+= h_up_component[l] * grad_up_component[m] + h_down_component[l] * grad_down_component[m];
175+
}
176+
}
177+
}
178+
#endif
179+
180+
XC_Functional_Libxc::finish_func(funcs);
181+
}
182+
19183
std::tuple<double, double, ModuleBase::matrix> XC_Functional_Libxc::v_xc_libxc( // Peize Lin update for nspin==4 at
20184
// 2023.01.14
21185
const std::vector<int>& func_id,

‎source/source_hamilt/module_xc/test/test_xc_functional_ncgga_sf.cpp‎

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,13 +1491,92 @@ TEST_F(RealPwNcgga, LibxcGgaGrad2DifferentiatesMixedLdaAndGgaComponents)
14911491
expect_vtxc_matches_returned_potential("libxc_gga2_mixed_lda_gga", evaluate);
14921492
}
14931493

1494+
TEST_F(RealPwNcgga, LibxcGgaGrad2StressProductionDispatchClosesSmoothMetricDerivative)
1495+
{
1496+
const BranchMargins margins = report_branch_margins("libxc_stress_smooth");
1497+
EXPECT_GT(margins.min_abs_total_density, 1.0);
1498+
EXPECT_GT(margins.min_signed_saturation_gap, 0.8);
1499+
EXPECT_GT(margins.min_eta_distance, 0.3);
14941500

1501+
// The analytic tensor must come through the existing public production
1502+
// dispatch. Keeping the new helper out of the test-only patch lets the
1503+
// matched parent build and fail on the numerical identity, not at link
1504+
// time because the child-only helper does not exist yet.
1505+
expect_gradient_stress_metric_derivative(
1506+
"libxc_smooth_dispatch",
1507+
[this]() { return evaluate_libxc_gga(); },
1508+
[this]() { return evaluate_libxc_pbe_gradient_stress_dispatch(); },
1509+
3.0e-4,
1510+
1.0e-11);
1511+
}
14951512

1513+
TEST_F(RealPwNcgga, LibxcGgaGrad2StressProductionDispatchClosesLocalMapBranches)
1514+
{
1515+
const Evaluator energy = [this]() { return evaluate_libxc_gga(); };
1516+
const StressEvaluator stress = [this]() { return evaluate_libxc_pbe_gradient_stress_dispatch(); };
14961517

1518+
set_negative_gga_state();
1519+
const BranchMargins negative = report_branch_margins("libxc_stress_negative_abs");
1520+
EXPECT_GT(negative.min_abs_total_density, 1.3);
1521+
EXPECT_GT(negative.min_signed_saturation_gap, 0.8);
1522+
expect_gradient_stress_metric_derivative("libxc_negative_abs_dispatch", energy, stress, 3.0e-4, 1.0e-11);
14971523

1524+
set_saturated_gga_state();
1525+
const BranchMargins saturated = report_branch_margins("libxc_stress_saturated");
1526+
EXPECT_LT(saturated.max_signed_saturation_gap, -0.1);
1527+
expect_gradient_stress_metric_derivative("libxc_saturated_dispatch", energy, stress, 3.0e-4, 1.0e-11);
14981528

1529+
set_inside_eta_state();
1530+
const BranchMargins radial = report_branch_margins("libxc_stress_inside_eta");
1531+
EXPECT_LT(radial.max_magnitude, 6.0e-4);
1532+
EXPECT_GT(radial.min_eta_distance, 4.0e-4);
1533+
expect_gradient_stress_metric_derivative("libxc_inside_eta_dispatch", energy, stress, 3.0e-4, 1.0e-11);
1534+
}
14991535

1536+
TEST_F(RealPwNcgga, LibxcGgaGrad2StressAggregatesPublicFunctionalScaling)
1537+
{
1538+
const std::vector<int> gga = {XC_GGA_X_ITYH, XC_GGA_C_LYPR, XC_GGA_X_B88, XC_GGA_C_LYP};
1539+
const std::map<int, double> scaling
1540+
= {{XC_GGA_X_ITYH, -1.0}, {XC_GGA_C_LYPR, -1.0}, {XC_GGA_X_B88, 1.0}, {XC_GGA_C_LYP, 1.0}};
1541+
const std::vector<double> exchange_short = evaluate_gradient_stress_dispatch("GGA_X_ITYH");
1542+
const std::vector<double> correlation_short = evaluate_gradient_stress_dispatch("GGA_C_LYPR");
1543+
const std::vector<double> exchange_full = evaluate_gradient_stress_dispatch("GGA_X_B88");
1544+
const std::vector<double> correlation_full = evaluate_gradient_stress_dispatch("GGA_C_LYP");
1545+
const std::vector<double> scaled = evaluate_gradient_stress_dispatch("BLYP_LR");
1546+
ASSERT_EQ(exchange_short.size(), 9U);
1547+
ASSERT_EQ(correlation_short.size(), 9U);
1548+
ASSERT_EQ(exchange_full.size(), 9U);
1549+
ASSERT_EQ(correlation_full.size(), 9U);
1550+
ASSERT_EQ(scaled.size(), 9U);
1551+
for (int row = 0; row < 3; ++row)
1552+
{
1553+
for (int column = 0; column <= row; ++column)
1554+
{
1555+
const int index = row * 3 + column;
1556+
const double expected
1557+
= -exchange_short[index] - correlation_short[index] + exchange_full[index] + correlation_full[index];
1558+
EXPECT_NEAR(scaled[index], expected, 3.0e-12 * std::max(1.0, std::abs(expected)));
1559+
}
1560+
}
1561+
expect_gradient_stress_metric_derivative(
1562+
"libxc_blyp_lr_dispatch",
1563+
[this, &gga, &scaling]() { return evaluate_libxc(gga, &scaling); },
1564+
[this]() { return evaluate_gradient_stress_dispatch("BLYP_LR"); },
1565+
3.0e-4,
1566+
1.0e-11);
1567+
}
15001568

1569+
TEST_F(RealPwNcgga, LibxcGgaGrad2StressProductionDispatchClosesFullXcDiagonalWithoutCore)
1570+
{
1571+
set_zero_core_density();
1572+
const BranchMargins margins = report_branch_margins("libxc_full_xc_no_core");
1573+
EXPECT_GT(margins.min_abs_total_density, 1.0);
1574+
EXPECT_GT(margins.min_signed_saturation_gap, 0.8);
1575+
expect_full_xc_diagonal_stress(
1576+
"libxc_full_xc_dispatch_no_core",
1577+
[this]() { return evaluate_libxc_gga(); },
1578+
[this]() { return evaluate_libxc_pbe_gradient_stress_dispatch(); });
1579+
}
15011580
#endif
15021581

15031582
TEST_F(RealPwNcgga, BuiltinGgaGrad2VtxcEqualsFinalValencePotentialInnerProduct)
@@ -1653,13 +1732,64 @@ TEST_F(RealPwNcgga, BuiltinGgaGrad2IsCovariantUnderGlobalSpinRotation)
16531732
}
16541733
}
16551734

1735+
TEST_F(RealPwNcgga, BuiltinGgaGrad2StressClosesSmoothSixComponentMetricDerivative)
1736+
{
1737+
const BranchMargins margins = report_branch_margins("stress_smooth");
1738+
EXPECT_GT(margins.min_abs_total_density, 1.0);
1739+
EXPECT_GT(margins.min_signed_saturation_gap, 0.8);
1740+
EXPECT_GT(margins.min_eta_distance, 0.3);
1741+
expect_builtin_gradient_stress_metric_derivative("smooth");
16561742

1743+
set_zero_core_density();
1744+
const BranchMargins zero_core = report_branch_margins("stress_smooth_zero_core");
1745+
EXPECT_GT(zero_core.min_abs_total_density, 1.0);
1746+
EXPECT_GT(zero_core.min_signed_saturation_gap, 0.8);
1747+
expect_builtin_full_xc_diagonal_stress("smooth");
1748+
}
16571749

1750+
TEST_F(RealPwNcgga, BuiltinGgaGrad2StressClosesNegativeAbsSixComponentMetricDerivative)
1751+
{
1752+
set_negative_gga_state();
1753+
const BranchMargins margins = report_branch_margins("stress_negative_abs");
1754+
EXPECT_GT(margins.min_abs_total_density, 1.3);
1755+
EXPECT_GT(margins.min_signed_saturation_gap, 0.8);
1756+
expect_builtin_gradient_stress_metric_derivative("negative_abs");
16581757

1758+
set_zero_core_density();
1759+
const BranchMargins zero_core = report_branch_margins("stress_negative_abs_zero_core");
1760+
EXPECT_GT(zero_core.min_abs_total_density, 1.3);
1761+
EXPECT_GT(zero_core.min_signed_saturation_gap, 0.8);
1762+
expect_builtin_full_xc_diagonal_stress("negative_abs");
1763+
}
16591764

1765+
TEST_F(RealPwNcgga, BuiltinGgaGrad2StressClosesSaturatedSixComponentMetricDerivative)
1766+
{
1767+
set_saturated_gga_state();
1768+
const BranchMargins margins = report_branch_margins("stress_saturated");
1769+
EXPECT_LT(margins.max_signed_saturation_gap, -0.1);
1770+
expect_builtin_gradient_stress_metric_derivative("saturated");
16601771

1772+
set_zero_core_density();
1773+
const BranchMargins zero_core = report_branch_margins("stress_saturated_zero_core");
1774+
EXPECT_LT(zero_core.max_signed_saturation_gap, -0.1);
1775+
expect_builtin_full_xc_diagonal_stress("saturated");
1776+
}
16611777

1778+
TEST_F(RealPwNcgga, BuiltinGgaGrad2StressClosesRadialEtaSixComponentMetricDerivative)
1779+
{
1780+
set_inside_eta_state();
1781+
const BranchMargins margins = report_branch_margins("stress_inside_eta");
1782+
EXPECT_GT(margins.min_abs_total_density, 0.025);
1783+
EXPECT_LT(margins.max_magnitude, 6.0e-4);
1784+
EXPECT_GT(margins.min_eta_distance, 4.0e-4);
1785+
expect_builtin_gradient_stress_metric_derivative("inside_eta");
16621786

1787+
set_zero_core_density();
1788+
const BranchMargins zero_core = report_branch_margins("stress_inside_eta_zero_core");
1789+
EXPECT_GT(zero_core.min_abs_total_density, 0.025);
1790+
EXPECT_LT(zero_core.max_magnitude, 6.0e-4);
1791+
expect_builtin_full_xc_diagonal_stress("inside_eta");
1792+
}
16631793

16641794
} // namespace
16651795

0 commit comments

Comments
 (0)