votca 2026-dev
Loading...
Searching...
No Matches
ewald_potential.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2020 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20// Standard includes
21#include <cmath>
22#include <stdexcept>
23#include <string>
24
25// Third party includes
26#include <boost/format.hpp>
27
28// VOTCA includes
30
31// Local VOTCA includes
33#include "votca/xtp/vxc_grid.h"
34
35namespace votca {
36namespace xtp {
37template <class Grid>
39
40template <class Grid>
42
43 Mat_p_Energy vewald = Mat_p_Energy(basissize, basissize);
44
45 // Checked HERE, serially, before the parallel region -- not inside it,
46 // where this check used to live.
47 //
48 // OpenMP requires that an exception thrown inside a structured block be
49 // caught inside that same block, by the same thread (OpenMP 5.2,
50 // "Exception Handling"). Throwing out of a `parallel for` body is
51 // undefined behaviour, whatever gcc and clang happen to do with it in
52 // practice. Intel's icpx does something else: it crashes outright, with
53 // a segfault inside its own OpenMP region outliner --
54 //
55 // Running pass "vpo-paropt" on module ".../ewald_potential.cc"
56 // llvm::vpo::VPOParoptUtils::genOutlineFunction(...)
57 // llvm::vpo::VPOParoptTransform::genMultiThreadedCode(...)
58 // icpx: error: clang frontend command failed with exit code 139
59 //
60 // -- observed directly, on Intel oneAPI DPC++/C++ 2026.1.1 with
61 // -fiopenmp, in the ubuntu:intel CI job. That crash is a compiler bug
62 // and should be reported as one; the throw it chokes on is still ours
63 // to fix, and hoisting it is the fix rather than the workaround. This
64 // loop's body is otherwise structurally identical to
65 // Vxc_Potential::IntegrateVXC's (same template, same schedule(guided),
66 // same user-defined Mat_p_Energy reduction from eigen.h), which icpx
67 // compiles -- and the throw is the one thing that differs.
68 //
69 // Validating up front is better regardless: it is O(number of boxes),
70 // it costs nothing next to the integration, and it fails before any
71 // work is done instead of midway through a partly-accumulated matrix.
72 // The `!box.Matrixsize()` skip is kept identical to the one below so
73 // that exactly the same set of boxes is checked as before.
74 for (Index i = 0; i < grid_.getBoxesSize(); ++i) {
75 const GridBox& box = grid_[i];
76 if (!box.Matrixsize()) {
77 continue;
78 }
79 // The potential values are copied in from another grid, so a
80 // mismatch in box structure would otherwise read past the end of a
81 // vector rather than fail.
82 const std::vector<double>& pot = box.getPotentialValues();
83 if (Index(pot.size()) != box.size()) {
84 throw std::runtime_error(
85 "Ewald_Potential::IntegrateEwald: box " + std::to_string(i) +
86 " has " + std::to_string(box.size()) + " grid points but " +
87 std::to_string(pot.size()) +
88 " potential values. The grid the potential was evaluated on is "
89 "not the grid being integrated over.");
90 }
91 }
92
93#pragma omp parallel for schedule(guided) reduction(+ : vewald)
94 for (Index i = 0; i < grid_.getBoxesSize(); ++i) {
95 const GridBox& box = grid_[i];
96 if (!box.Matrixsize()) {
97 continue;
98 }
99 double Eewald_box = 0.0;
100
101 Eigen::MatrixXd Vewald_here =
102 Eigen::MatrixXd::Zero(box.Matrixsize(), box.Matrixsize());
103 const std::vector<Eigen::Vector3d>& points = box.getGridPoints();
104 const std::vector<double>& weights = box.getGridWeights();
105 const std::vector<double>& pot = box.getPotentialValues();
106 // Size agreement between pot and the box was established serially
107 // above, before this region was entered.
108
109 // iterate over gridpoints
110 for (Index p = 0; p < box.size(); p++) {
111 AOShell::AOValues ao = box.CalcAOValues(points[p]);
112 const double weight = weights[p] * pot[p];
113
114 // ABSOLUTE VALUE, and not for tidiness. The equivalent guard in
115 // Vxc_Potential reads rho*weight < 1e-20, where rho is a DENSITY
116 // and the product cannot be negative, so it means "negligibly
117 // small". Here the integrand carries a POTENTIAL, which is
118 // negative over roughly half of space around a neutral periodic
119 // background -- the unsigned test discarded every one of those
120 // points, keeping only the half that does not cancel.
121 if (std::abs(weight) < 1.e-20) {
122 continue; // skip the rest, if integrand is very small
123 }
124
125 Eewald_box += weight;
126 Vewald_here.noalias() += weight * ao.values * ao.values.transpose();
127 }
128 box.AddtoBigMatrix(vewald.matrix(), Vewald_here);
129 vewald.energy() += Eewald_box;
130 }
131
132 // The accumulated Eewald_box is sum_p w_p phi_p, i.e. the integral of
133 // the potential over the grid -- not an energy of anything. The energy
134 // of the electron density in this potential is the contraction of the
135 // matrix below with the density matrix, which is the caller's to form.
136 // Returned as zero rather than as a plausible-looking number nobody
137 // should use; the nuclear share has no route through here at all and
138 // is supplied separately.
139 //
140 // ELECTRON CHARGE -- the minus sign. What the loop accumulated is the
141 // plain potential integral, +<chi_mu|phi|chi_nu>. Electrons carry
142 // charge -1, so their energy in this potential is -\int rho phi, and
143 // the caller adds this matrix straight into H0, where Tr(D H0) must
144 // already BE that energy. AOMultipole::FillPotential does exactly this
145 // for the nuclei and for the external multipoles -- `aopotential_ -=
146 // Fill(aobasis)` in both overloads -- and this is the same conversion
147 // for the same reason.
148 //
149 // Nearly invisible if wrong. For a NEUTRAL QM region the nuclear term
150 // +sum_A Z_A phi(R_A) and the electronic term -\int rho phi differ only
151 // through the shape of the density, not its total charge, so they very
152 // nearly cancel. Dropping the sign turns that cancellation into a
153 // doubling. Measured on a neutral methane in a rank-0 methane
154 // background, against an otherwise identical job with no ewaldregion:
155 // nuclear -6.689 meV, electronic -6.817 meV, total -13.506 meV, where
156 // the correct total is +0.128 meV -- the right order for a
157 // near-spherical neutral molecule in a slowly varying periodic
158 // potential, and consistent with the classical channel's own
159 // -0.46 meV per segment.
160 //
161 // No existing test could catch it. A zeroed background has phi = 0, so
162 // both halves vanish whatever the sign. EwaldRegion::PotentialAt is
163 // pinned against lattice sums and unit probes, none of which build an
164 // AO matrix. And ApplyFieldTo, which carries the entire validation
165 // against legacy, never reaches this file.
166 return Mat_p_Energy(0.0, -vewald.matrix());
167}
168
169template class Ewald_Potential<Vxc_Grid>;
170
171} // namespace xtp
172} // namespace votca
Mat_p_Energy IntegrateEwald(Index basissize) const
const std::vector< Eigen::Vector3d > & getGridPoints() const
Definition gridbox.h:48
void AddtoBigMatrix(Eigen::MatrixXd &bigmatrix, const Eigen::MatrixXd &smallmatrix) const
Definition gridbox.cc:71
const std::vector< double > & getGridWeights() const
Definition gridbox.h:50
AOShell::AOValues CalcAOValues(const Eigen::Vector3d &point) const
Definition gridbox.cc:44
Index size() const
Definition gridbox.h:64
Index Matrixsize() const
Definition gridbox.h:68
std::vector< double > & getPotentialValues()
Definition gridbox.h:51
Eigen::MatrixXd & matrix()
Definition eigen.h:80
double & energy()
Definition eigen.h:81
Charge transport classes.
Definition ERIs.h:28
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26