votca 2026-dev
Loading...
Searching...
No Matches
sigma_base.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
23// Third party includes
24#include <boost/math/constants/constants.hpp>
25
26#include <algorithm>
27#include <stdexcept>
28#include <string>
29#include <vector>
30
31// VOTCA includes
33
34// Local VOTCA includes
37
38namespace votca {
39namespace xtp {
40
42 Index nrows,
43 const std::function<void(Index, Eigen::MatrixXd&)>& makeX) const {
44 const Index qpmin = opt_.qpmin - opt_.rpamin;
45 const Index auxsize = Mmn_.auxsize();
46 // X of a block of levels kept at once, and the chunk of all M_n gathered
47 // for one product; the block size sets how often the integrals are read.
48 const double kBlockBytes = block_bytes_;
49 const double kChunkBytes = chunk_bytes_;
50 const Index block = std::max<Index>(
51 1, std::min<Index>(qptotal_, Index(kBlockBytes / (8.0 * double(nrows) *
52 double(auxsize)))));
53 const Index chunk = std::max<Index>(
54 1, std::min<Index>(auxsize, Index(kChunkBytes / (8.0 * double(nrows) *
55 double(qptotal_)))));
56 Eigen::MatrixXd A = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
57 std::vector<Eigen::MatrixXd> X(static_cast<std::size_t>(block));
58 // chunk buffers kept across chunks (no reallocation per chunk)
59 Eigen::MatrixXd Mc;
60 Eigen::MatrixXd Xc;
61 for (Index m0 = 0; m0 < qptotal_; m0 += block) {
62 const Index nb = std::min(block, qptotal_ - m0);
63#pragma omp parallel for schedule(dynamic)
64 for (Index j = 0; j < nb; ++j) {
65 makeX(m0 + j, X[std::size_t(j)]);
66 }
67 for (Index s0 = 0; s0 < auxsize; s0 += chunk) {
68 const Index ns = std::min(chunk, auxsize - s0);
69 Mc.resize(nrows * ns, qptotal_);
70 Xc.resize(nrows * ns, nb);
71#pragma omp parallel for schedule(static)
72 for (Index n = 0; n < qptotal_; ++n) {
73 Eigen::Map<Eigen::MatrixXd>(Mc.col(n).data(), nrows, ns) =
74 Mmn_[n + qpmin].block(0, s0, nrows, ns);
75 }
76 for (Index j = 0; j < nb; ++j) {
77 Eigen::Map<Eigen::MatrixXd>(Xc.col(j).data(), nrows, ns) =
78 X[std::size_t(j)].middleCols(s0, ns);
79 }
80 A.middleRows(m0, nb).noalias() += Xc.transpose() * Mc;
81 }
82 }
83 return A;
84}
85
87 throw std::runtime_error(
88 "The pole form of Sigma_c is only available for sigma_integrator ppm "
89 "and exact");
90}
91
93 const Eigen::VectorXd&) const {
94 throw std::runtime_error(
95 "Screening data are only available for sigma_integrator ppm and exact");
96}
97
98void Sigma_base::FrameSums(const Eigen::MatrixXd& a,
99 const Eigen::VectorXd& omegas,
100 const Eigen::VectorXd& nu, ScreeningData& data) {
101 const Index S = omegas.size();
102 // columns: Omega^-1, Omega^0, Omega^1, then Omega/(nu^2+Omega^2)
103 Eigen::MatrixXd g(S, 3 + nu.size());
104 for (Index s = 0; s < S; ++s) {
105 const double w = omegas(s);
106 g(s, 0) = 1.0 / w;
107 g(s, 1) = 1.0;
108 g(s, 2) = w;
109 for (Index j = 0; j < nu.size(); ++j) {
110 g(s, 3 + j) = -w / (nu(j) * nu(j) + w * w);
111 }
112 }
113 const Eigen::MatrixXd sums = a * g;
114 data.frame_moments = sums.leftCols(3);
115 data.frame_eps_inv_imag = sums.rightCols(nu.size());
116}
117
118Eigen::MatrixXd Sigma_base::CalcExchangeMatrix() const {
119 const Index occlevel = opt_.homo - opt_.rpamin + 1;
120 const Index qpmin = opt_.qpmin - opt_.rpamin;
121 // Exchange is with the bare v, which is M M^T only as long as the
122 // auxiliary frame is orthogonal. Once the index is dressed for an
123 // environment (TCMatrix_gwbse::DressAuxIndex) it takes the bare
124 // interaction in the current frame as an explicit kernel.
125 const bool bare = Mmn_.AuxFrameIsOrthogonal();
126 Eigen::MatrixXd v;
127 if (!bare) {
128 v = Mmn_.ToCurrentAuxFrame(
129 Eigen::MatrixXd::Identity(Mmn_.auxsize(), Mmn_.auxsize()));
130 }
131 // Sigma_x(m,n) = - sum_{i occ} sum_P M_m(i,P) [v] M_n(i,P)
132 const Eigen::MatrixXd A =
133 PairContraction(occlevel, [&](Index m, Eigen::MatrixXd& X) {
134 if (bare) {
135 X = Mmn_[m + qpmin].topRows(occlevel);
136 } else {
137 X = Mmn_[m + qpmin].topRows(occlevel) * v;
138 }
139 });
140 return -0.5 * (A + A.transpose());
141}
142
143Eigen::MatrixXd Sigma_base::CalcHartreeMatrix(const Eigen::MatrixXd& R) const {
144 const Index occlevel = opt_.homo - opt_.rpamin + 1;
145 const Index qpmin = opt_.qpmin - opt_.rpamin;
146 // d_P = sum_{i occ} M_ii,P (the density in the auxiliary basis)
147 Eigen::VectorXd d = Eigen::VectorXd::Zero(Mmn_.auxsize());
148 for (Index i = 0; i < occlevel; ++i) {
149 d += Mmn_[i].row(i).transpose();
150 }
151 // Kernel 1 (bare v) or 1 + R (u = v + v_reac), given in the fill-time
152 // frame and brought into the current one (see CalcExchangeMatrix). On
153 // dressed integrals F F^T = 1 + R, so the screened kernel is close to the
154 // identity there; it is still taken explicitly, which keeps this right
155 // in any frame.
156 if (R.size() > 0) {
157 if (R.rows() != Mmn_.auxsize() || R.cols() != Mmn_.auxsize()) {
158 throw std::runtime_error(
159 "Sigma_base::CalcHartreeMatrix: reaction field is " +
160 std::to_string(R.rows()) + "x" + std::to_string(R.cols()) +
161 ", auxiliary basis has " + std::to_string(Mmn_.auxsize()) +
162 " functions.");
163 }
164 d = Mmn_.ToCurrentAuxFrame(
165 Eigen::MatrixXd::Identity(Mmn_.auxsize(), Mmn_.auxsize()) + R) *
166 d;
167 } else if (!Mmn_.AuxFrameIsOrthogonal()) {
168 d = Mmn_.ToCurrentAuxFrame(
169 Eigen::MatrixXd::Identity(Mmn_.auxsize(), Mmn_.auxsize())) *
170 d;
171 }
172 Eigen::MatrixXd J(qptotal_, qptotal_);
173#pragma omp parallel for schedule(dynamic)
174 for (Index m = 0; m < qptotal_; ++m) {
175 J.col(m) = 2.0 * Mmn_[m + qpmin].middleRows(qpmin, qptotal_) * d;
176 }
177 return 0.5 * (J + J.transpose());
178}
179
181 const Eigen::MatrixXd& R) const {
182 const Eigen::MatrixXd Rc = Mmn_.ToCurrentAuxFrame(R);
183 const Index occlevel = opt_.homo - opt_.rpamin + 1;
184 const Index qpmin = opt_.qpmin - opt_.rpamin;
185 // Row l of X_m is s_l M_m(l,:) R: the occupation sign folded in once.
186 const Eigen::MatrixXd A =
187 PairContraction(Mmn_.nsize(), [&](Index m, Eigen::MatrixXd& X) {
188 X = Mmn_[m + qpmin] * Rc;
189 X.topRows(occlevel) *= -1.0;
190 });
191 return 0.25 * (A + A.transpose());
192}
193
195 const Eigen::VectorXd& frequencies) const {
196 Eigen::VectorXd result = Eigen::VectorXd::Zero(qptotal_);
197#pragma omp parallel for schedule(dynamic)
198 for (Index gw_level = 0; gw_level < qptotal_; gw_level++) {
199 result(gw_level) =
200 CalcCorrelationDiagElement(gw_level, frequencies[gw_level]);
201 }
202 return result;
203}
204
206 const Eigen::VectorXd& frequencies, double s) const {
207 if (!HasPoleForm()) {
208 throw std::runtime_error(
209 "SRG-regularized QSGW needs a self-energy with a pole form (exact or "
210 "ppm)");
211 }
212 if (!(s > 0.0)) {
213 throw std::runtime_error("SRG flow parameter s must be positive");
214 }
215 if (frequencies.size() != qptotal_) {
216 throw std::runtime_error("CalcCorrelationSRG: one frequency per level");
217 }
218 std::vector<DiagPoleForm> forms;
219 forms.reserve(std::size_t(qptotal_));
220 for (Index n = 0; n < qptotal_; ++n) {
221 forms.push_back(PoleForm(n));
222 }
223 const DiagPoleForm& first = forms.front();
224 const ExactPoles& poles = first.poles;
225 const Index B = poles.nlevels();
226 const Index S = poles.nmodes();
227 const Index P = poles.npoles();
228 // pole positions and factors, p = m + B s
229 Eigen::ArrayXd z(P);
230 Eigen::ArrayXd c = Eigen::ArrayXd::Ones(P);
231 for (Index mode = 0; mode < S; ++mode) {
232 for (Index m = 0; m < B; ++m) {
233 z(m + B * mode) = poles.energies(m) +
234 ((m < poles.n_occ) ? -1.0 : 1.0) * poles.omegas(mode);
235 if (first.mode_factors != nullptr) {
236 c(m + B * mode) = first.mode_factors[mode];
237 }
238 }
239 }
240 Eigen::MatrixXd result(qptotal_, qptotal_);
241#pragma omp parallel for schedule(dynamic)
242 for (Index n1 = 0; n1 < qptotal_; ++n1) {
243 const Eigen::Map<const Eigen::ArrayXd> r1(forms[std::size_t(n1)].residues,
244 P);
245 const Eigen::ArrayXd d1 = frequencies(n1) - z;
246 const Eigen::ArrayXd cr1 = c * r1;
247 for (Index n2 = 0; n2 <= n1; ++n2) {
248 const Eigen::Map<const Eigen::ArrayXd> r2(forms[std::size_t(n2)].residues,
249 P);
250 const Eigen::ArrayXd d2 = frequencies(n2) - z;
251 const Eigen::ArrayXd D = d1.square() + d2.square();
252 // (1 - exp(-s D)) / D, with its limit s at D -> 0
253 const Eigen::ArrayXd damp = (s * D < 1e-12)
254 .select(Eigen::ArrayXd::Constant(P, s),
255 -(-s * D).unaryExpr([](double x) {
256 return std::expm1(x);
257 }) / D.max(1e-300));
258 const double value =
259 first.prefactor * (cr1 * r2 * (d1 + d2) * damp).sum();
260 result(n1, n2) = value;
261 result(n2, n1) = value;
262 }
263 }
264 return result;
265}
266
268 const Eigen::VectorXd& frequencies) const {
269 Eigen::MatrixXd result = Eigen::MatrixXd::Zero(qptotal_, qptotal_);
270#pragma omp parallel for schedule(dynamic)
271 for (Index gw_level1 = 0; gw_level1 < qptotal_; gw_level1++) {
272 for (Index gw_level2 = gw_level1 + 1; gw_level2 < qptotal_; gw_level2++) {
273 double sigma_c = CalcCorrelationOffDiagElement(
274 gw_level1, gw_level2, frequencies[gw_level1], frequencies[gw_level2]);
275 result(gw_level2, gw_level1) = sigma_c;
276 }
277 }
278 result = result.selfadjointView<Eigen::Lower>();
279 return result;
280}
281
282} // namespace xtp
283} // namespace votca
virtual bool HasPoleForm() const
Definition sigma_base.h:148
Eigen::MatrixXd CalcHartreeMatrix(const Eigen::MatrixXd &R=Eigen::MatrixXd()) const
static void FrameSums(const Eigen::MatrixXd &a, const Eigen::VectorXd &omegas, const Eigen::VectorXd &nu, ScreeningData &data)
Definition sigma_base.cc:98
virtual Eigen::MatrixXd CalcCorrelationOffDiag(const Eigen::VectorXd &frequencies) const
Eigen::MatrixXd CalcCorrelationSRG(const Eigen::VectorXd &frequencies, double s) const
virtual ScreeningData ScreeningDump(const Eigen::VectorXd &nu) const
Definition sigma_base.cc:92
virtual double CalcCorrelationOffDiagElement(Index gw_level1, Index gw_level2, double frequency1, double frequency2) const =0
virtual DiagPoleForm PoleForm(Index gw_level) const
Definition sigma_base.cc:86
virtual double CalcCorrelationDiagElement(Index gw_level, double frequency) const =0
Eigen::MatrixXd PairContraction(Index nrows, const std::function< void(Index, Eigen::MatrixXd &)> &makeX) const
Definition sigma_base.cc:41
Eigen::VectorXd CalcCorrelationDiag(const Eigen::VectorXd &frequencies) const
TCMatrix_gwbse & Mmn_
Definition sigma_base.h:207
Eigen::MatrixXd CalcReactionFieldMatrix(const Eigen::MatrixXd &R) const
Static self-energy of an environment reaction field: the COH+SEX pair for v_reac, in the QP window.
Eigen::MatrixXd CalcExchangeMatrix() const
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
Pole sums of the exact (RPA spectral) correlation self-energy.
const Eigen::VectorXd & energies
const Eigen::VectorXd & omegas