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}
48
49std::string Sigma_PPM::ScreeningSummary() const {
50 // Sigma_c sees only modes with non-zero weight (weights below 1e-5 are
51 // set to zero in PPM_construct_parameters).
52 const Eigen::ArrayXd w = ppm_.getPpm_weight().array();
53 return "PPM modes with weight > 0: " + std::to_string((w > 0.0).count()) +
54 " of " + std::to_string(w.size()) +
55 " (> 1e-3: " + std::to_string((w > 1e-3).count()) +
56 ", > 1e-2: " + std::to_string((w > 1e-2).count()) + ")" +
57 ((ppm_.InvalidModes() > 0)
58 ? "; WARNING: " + std::to_string(ppm_.InvalidModes()) +
59 " with negative squared frequency (|.| used)"
60 : std::string());
61}
62
63// sum over modes P and levels n of
64// fac_P |M(n,P)|^2 term(t), t = w_k - e_n +- Omega_P
65// (+ for occupied, - for virtual n), for every frequency w_k. This runs
66// inside the parallel QP solver: the array expressions are evaluated lazily
67// inside the sums, so there are no temporaries (allocations per mode and
68// frequency, millions per iteration, were slow on macOS), but the sums are
69// still vectorised.
70template <class Term>
71void Sigma_PPM::AccumulateDiag(Index gw_level, const double* freqs, Index nfreq,
72 double* out, Term term) const {
73 // first virtual level, counted from rpamin like the energies and the
74 // rows of Mmn_
75 const Index lumo = opt_.homo + 1 - opt_.rpamin;
76 const Index levelsum = Mmn_.nsize(); // total number of bands
77 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
78 const Eigen::MatrixXd& M = Mmn_[gw_level + qpmin_offset];
79 const auto e = rpa_.getRPAInputEnergies().array();
80 const Eigen::VectorXd& weight = ppm_.getPpm_weight();
81 const Eigen::VectorXd& omega = ppm_.getPpm_freq();
82 for (Index i_aux = 0; i_aux < Mmn_.auxsize(); i_aux++) {
83 // the ppm_weights smaller 1.e-5 are set to zero in
84 // PPM_construct_parameters
85 if (weight(i_aux) < 1.e-9) {
86 continue;
87 }
88 const double ppm_freq = omega(i_aux);
89 const double fac = 0.5 * weight(i_aux) * ppm_freq;
90 // lazily evaluated, vectorised array expressions: no temporaries
91 const auto m2 = M.col(i_aux).array().square();
92 for (Index k = 0; k < nfreq; ++k) {
93 const auto t_occ = (freqs[k] + ppm_freq) - e.head(lumo);
94 const auto t_virt = (freqs[k] - ppm_freq) - e.tail(levelsum - lumo);
95 out[k] += fac * ((m2.head(lumo) * term(t_occ)).sum() +
96 (m2.tail(levelsum - lumo) * term(t_virt)).sum());
97 }
98 }
99}
100
102 double frequency) const {
104 const double eta2 = opt_.eta * opt_.eta;
105 double sigma = 0.0;
106 AccumulateDiag(gw_level, &frequency, 1, &sigma,
107 [eta2](const auto& t) { return t / (t.square() + eta2); });
108 return sigma;
109}
110
112 double frequency) const {
113 const double eta2 = opt_.eta * opt_.eta;
114 double dsigma_domega = 0.0;
115 AccumulateDiag(gw_level, &frequency, 1, &dsigma_domega,
116 [eta2](const auto& t) {
117 return (eta2 - t.square()) / (t.square() + eta2).square();
118 });
119 return dsigma_domega;
120}
121
123 Index gw_level, const Eigen::VectorXd& frequencies) const {
124 const Index nfreq = frequencies.size();
125 for (Index i = 0; i < nfreq; ++i) {
127 }
128 // the poles e_n -+ Omega_P with weights fac_P |M(n,P)|^2 have the form
129 // of the exact self-energy: far poles via Chebyshev interpolation (see
130 // exact_pole_sums), the integrals of the level read once
131 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
132 const ExactPoles poles{rpa_.getRPAInputEnergies(),
133 opt_.homo + 1 - opt_.rpamin, ppm_.getPpm_freq(),
134 opt_.eta};
135 return exact_pole_sums::Values(poles, Mmn_[gw_level + qpmin_offset].data(),
136 frequencies, mode_factors_.data());
137}
138
140 Index gw_level2,
141 double frequency1,
142 double frequency2) const {
143 // first virtual level, counted from rpamin like the energies and the
144 // rows of Mmn_
145 const Index lumo = opt_.homo + 1 - opt_.rpamin;
146 const double eta2 = opt_.eta * opt_.eta;
147 const Index levelsum = Mmn_.nsize(); // total number of bands
148 const Index auxsize = Mmn_.auxsize(); // size of the GW basis
149 const Eigen::VectorXd ppm_weight = ppm_.getPpm_weight();
150 const Eigen::VectorXd ppm_freqs = ppm_.getPpm_freq();
151 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
152 const Eigen::VectorXd RPAEnergies = rpa_.getRPAInputEnergies();
153 double sigma_c = 0;
154 for (Index i_aux = 0; i_aux < auxsize; i_aux++) {
155 // the ppm_weights smaller 1.e-5 are set to zero in rpa.cc
156 // PPM_construct_parameters
157 if (ppm_weight(i_aux) < 1.e-9) {
158 continue;
159 }
160 const double ppm_freq = ppm_freqs(i_aux);
161 const double fac = 0.25 * ppm_weight(i_aux) * ppm_freq;
162 const Eigen::MatrixXd& Mmn1 = Mmn_[gw_level1 + qpmin_offset];
163 const Eigen::MatrixXd& Mmn2 = Mmn_[gw_level2 + qpmin_offset];
164 const Eigen::ArrayXd Mmn1xMmn2 =
165 Mmn1.col(i_aux).cwiseProduct(Mmn2.col(i_aux));
166 Eigen::ArrayXd temp1 = RPAEnergies;
167 temp1.segment(0, lumo) -= ppm_freq;
168 temp1.segment(lumo, levelsum - lumo) += ppm_freq;
169 Eigen::ArrayXd temp2 = (frequency2 - temp1);
170 temp1 = (frequency1 - temp1);
171 sigma_c +=
172 fac * ((temp1 / (temp1.abs2() + eta2) + temp2 / (temp2.abs2() + eta2)) *
173 Mmn1xMmn2)
174 .sum();
175 }
176 return sigma_c;
177}
178
180 const Eigen::VectorXd& frequencies) const {
181 // first virtual level, counted from rpamin like the energies and the
182 // rows of Mmn_
183 const Index lumo = opt_.homo + 1 - opt_.rpamin;
184 const double eta2 = opt_.eta * opt_.eta;
185 const Index levelsum = Mmn_.nsize();
186 const Index auxsize = Mmn_.auxsize();
187 const Index qpmin_offset = opt_.qpmin - opt_.rpamin;
188 const Eigen::VectorXd& ppm_weight = ppm_.getPpm_weight();
189 const Eigen::VectorXd& ppm_freqs = ppm_.getPpm_freq();
190 const Eigen::ArrayXd energies = rpa_.getRPAInputEnergies().array();
191 // Sigma_mn = A_mn + A_nm with A_mn = sum_{l,P} X_m(l,P) M_n(l,P),
192 // X_m(l,P) = fac_P M_m(l,P) t/(t^2+eta^2), t = w_m - e_l -+ Omega_P,
193 // the same terms as CalcCorrelationOffDiagElement, summed differently.
194 const Eigen::MatrixXd A =
195 PairContraction(levelsum, [&](Index m, Eigen::MatrixXd& X) {
196 const Eigen::MatrixXd& M = Mmn_[m + qpmin_offset];
197 X.resize(levelsum, auxsize);
198 const double w = frequencies(m);
199 Eigen::ArrayXd t(levelsum);
200 for (Index i_aux = 0; i_aux < auxsize; ++i_aux) {
201 if (ppm_weight(i_aux) < 1.e-9) {
202 X.col(i_aux).setZero();
203 continue;
204 }
205 const double ppm_freq = ppm_freqs(i_aux);
206 const double fac = 0.25 * ppm_weight(i_aux) * ppm_freq;
207 t = w - energies;
208 t.head(lumo) += ppm_freq;
209 t.tail(levelsum - lumo) -= ppm_freq;
210 X.col(i_aux) =
211 (fac * M.col(i_aux).array() * t / (t.abs2() + eta2)).matrix();
212 }
213 });
214 Eigen::MatrixXd result = A + A.transpose();
215 result.diagonal().setZero();
216 return result;
217}
218
219} // namespace xtp
220} // namespace votca
double CalcCorrelationOffDiagElement(Index gw_level1, Index gw_level2, double frequency1, double frequency2) const final
Definition sigma_ppm.cc:139
void AccumulateDiag(Index gw_level, const double *freqs, Index nfreq, double *out, Term term) const
Definition sigma_ppm.cc:71
Eigen::VectorXd CalcCorrelationDiagElements(Index gw_level, const Eigen::VectorXd &frequencies) const final
Definition sigma_ppm.cc:122
double CalcCorrelationDiagElement(Index gw_level, double frequency) const final
Definition sigma_ppm.cc:101
Eigen::VectorXd mode_factors_
Definition sigma_ppm.h:66
void PrepareScreening() final
Definition sigma_ppm.cc:33
double CalcCorrelationDiagElementDerivative(Index gw_level, double frequency) const final
Definition sigma_ppm.cc:111
Eigen::MatrixXd CalcCorrelationOffDiag(const Eigen::VectorXd &frequencies) const final
Definition sigma_ppm.cc:179
std::string ScreeningSummary() const final
One line about the screening just prepared (empty if nothing to say).
Definition sigma_ppm.cc:49
DFTTimings * timings_
Definition sigma_base.h:144
Eigen::MatrixXd PairContraction(Index nrows, const std::function< void(Index, Eigen::MatrixXd &)> &makeX) const
Definition sigma_base.cc:39
TCMatrix_gwbse & Mmn_
Definition sigma_base.h:142
void CountDiagEval() const
Definition sigma_base.h:149
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.