votca 2026-dev
Loading...
Searching...
No Matches
sigma_ppm.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// VOTCA includes
22#include <votca/tools/globals.h>
23
24// Local VOTCA includes
25#include "sigma_ppm.h"
27#include "votca/xtp/ppm.h"
29
30namespace votca {
31namespace xtp {
32
34 {
35 // the epsilon builds inside are timed separately by the RPA
36 OptionalTiming t(timings_, "screening: PPM eigensolver, inverse");
37 ppm_.PPM_construct_parameters(rpa_);
38 }
39 OptionalTiming t(timings_, "screening: rotate Mmn (PPM frame)");
40 Mmn_.MultiplyRightWithAuxMatrix(ppm_.getPpm_phi());
41 const Eigen::VectorXd& weight = ppm_.getPpm_weight();
42 const Eigen::VectorXd& omega = ppm_.getPpm_freq();
43 mode_factors_.resize(weight.size());
44 for (Index i = 0; i < weight.size(); ++i) {
45 mode_factors_(i) = (weight(i) < 1.e-9) ? 0.0 : 0.5 * weight(i) * omega(i);
46 }
47 screening_energies_ = rpa_.getRPAInputEnergies();
48}
49
51 // residues: the integrals of the level in the PPM frame (rows: RPA levels,
52 // columns: modes), the same arrays CalcCorrelationDiagElements sums
53 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
54 return DiagPoleForm{
55 ExactPoles{rpa_.getRPAInputEnergies(), opt_.homo + 1 - opt_.rpamin,
56 ppm_.getPpm_freq(), opt_.eta},
57 Mmn_[gw_level + qpmin_offset].data(), mode_factors_.data(), 1.0};
58}
59
61 const Eigen::VectorXd& nu) const {
62 // one pole per mode of the frame: eps^-1_ii(i nu) - 1 =
63 // -w_i Omega_i^2 / (nu^2 + Omega_i^2), i.e. a_ii = w_i Omega_i
64 ScreeningData data;
65 const Eigen::VectorXd& w = ppm_.getPpm_weight();
66 data.omegas = ppm_.getPpm_freq();
67 data.strength = w;
68 data.frame_weights = w;
70 const Index n = w.size();
71 data.frame_moments.resize(n, 3);
72 data.frame_eps_inv_imag.resize(n, nu.size());
73 for (Index i = 0; i < n; ++i) {
74 const double W = data.omegas(i);
75 data.frame_moments(i, 0) = w(i);
76 data.frame_moments(i, 1) = w(i) * W;
77 data.frame_moments(i, 2) = w(i) * W * W;
78 for (Index j = 0; j < nu.size(); ++j) {
79 data.frame_eps_inv_imag(i, j) = -w(i) * W * W / (nu(j) * nu(j) + W * W);
80 }
81 }
82 return data;
83}
84
85std::string Sigma_PPM::ScreeningSummary() const {
86 // Sigma_c sees only modes with non-zero weight (weights below 1e-5 are
87 // set to zero in PPM_construct_parameters).
88 const Eigen::ArrayXd w = ppm_.getPpm_weight().array();
89 return "PPM modes with weight > 0: " + std::to_string((w > 0.0).count()) +
90 " of " + std::to_string(w.size()) +
91 " (> 1e-3: " + std::to_string((w > 1e-3).count()) +
92 ", > 1e-2: " + std::to_string((w > 1e-2).count()) + ")" +
93 ((ppm_.InvalidModes() > 0)
94 ? "; WARNING: " + std::to_string(ppm_.InvalidModes()) +
95 " with negative squared frequency (|.| used)"
96 : std::string());
97}
98
99// sum over modes P and levels n of
100// fac_P |M(n,P)|^2 term(t), t = w_k - e_n +- Omega_P
101// (+ for occupied, - for virtual n), for every frequency w_k. This runs
102// inside the parallel QP solver: the array expressions are evaluated lazily
103// inside the sums, so there are no temporaries (allocations per mode and
104// frequency, millions per iteration, were slow on macOS), but the sums are
105// still vectorised.
106template <class Term>
107void Sigma_PPM::AccumulateDiag(Index gw_level, const double* freqs, Index nfreq,
108 double* out, Term term) const {
109 // first virtual level, counted from rpamin like the energies and the
110 // rows of Mmn_
111 const Index lumo = opt_.homo + 1 - opt_.rpamin;
112 const Index levelsum = Mmn_.nsize(); // total number of bands
113 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
114 const Eigen::MatrixXd& M = Mmn_[gw_level + qpmin_offset];
115 const auto e = rpa_.getRPAInputEnergies().array();
116 const Eigen::VectorXd& weight = ppm_.getPpm_weight();
117 const Eigen::VectorXd& omega = ppm_.getPpm_freq();
118 for (Index i_aux = 0; i_aux < Mmn_.auxsize(); i_aux++) {
119 // the ppm_weights smaller 1.e-5 are set to zero in
120 // PPM_construct_parameters
121 if (weight(i_aux) < 1.e-9) {
122 continue;
123 }
124 const double ppm_freq = omega(i_aux);
125 const double fac = 0.5 * weight(i_aux) * ppm_freq;
126 // lazily evaluated, vectorised array expressions: no temporaries
127 const auto m2 = M.col(i_aux).array().square();
128 for (Index k = 0; k < nfreq; ++k) {
129 const auto t_occ = (freqs[k] + ppm_freq) - e.head(lumo);
130 const auto t_virt = (freqs[k] - ppm_freq) - e.tail(levelsum - lumo);
131 out[k] += fac * ((m2.head(lumo) * term(t_occ)).sum() +
132 (m2.tail(levelsum - lumo) * term(t_virt)).sum());
133 }
134 }
135}
136
138 double frequency) const {
140 const double eta2 = opt_.eta * opt_.eta;
141 double sigma = 0.0;
142 AccumulateDiag(gw_level, &frequency, 1, &sigma,
143 [eta2](const auto& t) { return t / (t.square() + eta2); });
144 return sigma;
145}
146
148 double frequency) const {
149 const double eta2 = opt_.eta * opt_.eta;
150 double dsigma_domega = 0.0;
151 AccumulateDiag(gw_level, &frequency, 1, &dsigma_domega,
152 [eta2](const auto& t) {
153 return (eta2 - t.square()) / (t.square() + eta2).square();
154 });
155 return dsigma_domega;
156}
157
159 Index gw_level, const Eigen::VectorXd& frequencies) const {
160 const Index nfreq = frequencies.size();
161 for (Index i = 0; i < nfreq; ++i) {
163 }
164 // the poles e_n -+ Omega_P with weights fac_P |M(n,P)|^2 have the form
165 // of the exact self-energy: far poles via Chebyshev interpolation (see
166 // exact_pole_sums), the integrals of the level read once
167 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
168 const ExactPoles poles{rpa_.getRPAInputEnergies(),
169 opt_.homo + 1 - opt_.rpamin, ppm_.getPpm_freq(),
170 opt_.eta};
171 return exact_pole_sums::Values(poles, Mmn_[gw_level + qpmin_offset].data(),
172 frequencies, mode_factors_.data());
173}
174
176 Index gw_level2,
177 double frequency1,
178 double frequency2) const {
179 // first virtual level, counted from rpamin like the energies and the
180 // rows of Mmn_
181 const Index lumo = opt_.homo + 1 - opt_.rpamin;
182 const double eta2 = opt_.eta * opt_.eta;
183 const Index levelsum = Mmn_.nsize(); // total number of bands
184 const Index auxsize = Mmn_.auxsize(); // size of the GW basis
185 const Eigen::VectorXd ppm_weight = ppm_.getPpm_weight();
186 const Eigen::VectorXd ppm_freqs = ppm_.getPpm_freq();
187 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
188 const Eigen::VectorXd RPAEnergies = rpa_.getRPAInputEnergies();
189 double sigma_c = 0;
190 for (Index i_aux = 0; i_aux < auxsize; i_aux++) {
191 // the ppm_weights smaller 1.e-5 are set to zero in rpa.cc
192 // PPM_construct_parameters
193 if (ppm_weight(i_aux) < 1.e-9) {
194 continue;
195 }
196 const double ppm_freq = ppm_freqs(i_aux);
197 const double fac = 0.25 * ppm_weight(i_aux) * ppm_freq;
198 const Eigen::MatrixXd& Mmn1 = Mmn_[gw_level1 + qpmin_offset];
199 const Eigen::MatrixXd& Mmn2 = Mmn_[gw_level2 + qpmin_offset];
200 const Eigen::ArrayXd Mmn1xMmn2 =
201 Mmn1.col(i_aux).cwiseProduct(Mmn2.col(i_aux));
202 Eigen::ArrayXd temp1 = RPAEnergies;
203 temp1.segment(0, lumo) -= ppm_freq;
204 temp1.segment(lumo, levelsum - lumo) += ppm_freq;
205 Eigen::ArrayXd temp2 = (frequency2 - temp1);
206 temp1 = (frequency1 - temp1);
207 sigma_c +=
208 fac * ((temp1 / (temp1.abs2() + eta2) + temp2 / (temp2.abs2() + eta2)) *
209 Mmn1xMmn2)
210 .sum();
211 }
212 return sigma_c;
213}
214
216 const Eigen::VectorXd& frequencies) const {
217 // first virtual level, counted from rpamin like the energies and the
218 // rows of Mmn_
219 const Index lumo = opt_.homo + 1 - opt_.rpamin;
220 const double eta2 = opt_.eta * opt_.eta;
221 const Index levelsum = Mmn_.nsize();
222 const Index auxsize = Mmn_.auxsize();
223 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
224 const Eigen::VectorXd& ppm_weight = ppm_.getPpm_weight();
225 const Eigen::VectorXd& ppm_freqs = ppm_.getPpm_freq();
226 const Eigen::ArrayXd energies = rpa_.getRPAInputEnergies().array();
227 // Sigma_mn = A_mn + A_nm with A_mn = sum_{l,P} X_m(l,P) M_n(l,P),
228 // X_m(l,P) = fac_P M_m(l,P) t/(t^2+eta^2), t = w_m - e_l -+ Omega_P,
229 // the same terms as CalcCorrelationOffDiagElement, summed differently.
230 const Eigen::MatrixXd A =
231 PairContraction(levelsum, [&](Index m, Eigen::MatrixXd& X) {
232 const Eigen::MatrixXd& M = Mmn_[m + qpmin_offset];
233 X.resize(levelsum, auxsize);
234 const double w = frequencies(m);
235 Eigen::ArrayXd t(levelsum);
236 for (Index i_aux = 0; i_aux < auxsize; ++i_aux) {
237 if (ppm_weight(i_aux) < 1.e-9) {
238 X.col(i_aux).setZero();
239 continue;
240 }
241 const double ppm_freq = ppm_freqs(i_aux);
242 const double fac = 0.25 * ppm_weight(i_aux) * ppm_freq;
243 t = w - energies;
244 t.head(lumo) += ppm_freq;
245 t.tail(levelsum - lumo) -= ppm_freq;
246 X.col(i_aux) =
247 (fac * M.col(i_aux).array() * t / (t.abs2() + eta2)).matrix();
248 }
249 });
250 Eigen::MatrixXd result = A + A.transpose();
251 result.diagonal().setZero();
252 return result;
253}
254
255} // namespace xtp
256} // namespace votca
double CalcCorrelationOffDiagElement(Index gw_level1, Index gw_level2, double frequency1, double frequency2) const final
Definition sigma_ppm.cc:175
Eigen::VectorXd screening_energies_
Definition sigma_ppm.h:72
DiagPoleForm PoleForm(Index gw_level) const final
Definition sigma_ppm.cc:50
void AccumulateDiag(Index gw_level, const double *freqs, Index nfreq, double *out, Term term) const
Definition sigma_ppm.cc:107
Eigen::VectorXd CalcCorrelationDiagElements(Index gw_level, const Eigen::VectorXd &frequencies) const final
Definition sigma_ppm.cc:158
double CalcCorrelationDiagElement(Index gw_level, double frequency) const final
Definition sigma_ppm.cc:137
Eigen::VectorXd mode_factors_
Definition sigma_ppm.h:70
ScreeningData ScreeningDump(const Eigen::VectorXd &nu) const final
Definition sigma_ppm.cc:60
void PrepareScreening() final
Definition sigma_ppm.cc:33
double CalcCorrelationDiagElementDerivative(Index gw_level, double frequency) const final
Definition sigma_ppm.cc:147
Eigen::MatrixXd CalcCorrelationOffDiag(const Eigen::VectorXd &frequencies) const final
Definition sigma_ppm.cc:215
std::string ScreeningSummary() const final
One line about the screening just prepared (empty if nothing to say).
Definition sigma_ppm.cc:85
DFTTimings * timings_
Definition sigma_base.h:209
Eigen::MatrixXd PairContraction(Index nrows, const std::function< void(Index, Eigen::MatrixXd &)> &makeX) const
Definition sigma_base.cc:41
TCMatrix_gwbse & Mmn_
Definition sigma_base.h:207
void CountDiagEval() const
Definition sigma_base.h:221
Eigen::VectorXd Values(const ExactPoles &poles, const double *residues, const Eigen::VectorXd &w)
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.