votca 2026-dev
Loading...
Searching...
No Matches
ewaldblockjacobipreconditioner.h
Go to the documentation of this file.
1#ifndef VOTCA_XTP_EWALDBLOCKJACOBIPRECONDITIONER_H
2#define VOTCA_XTP_EWALDBLOCKJACOBIPRECONDITIONER_H
3
4// Standard includes
5#include <optional>
6#include <stdexcept>
7
8// Third party includes
9#include <Eigen/Cholesky>
10#include <Eigen/IterativeLinearSolvers>
11
12// Local VOTCA includes
14#include "ewaldregistry.h"
15
16namespace votca {
17namespace xtp {
18
51 using Scalar = double;
52 using Vector = Eigen::VectorXd;
53
54 public:
56 enum {
57 ColsAtCompileTime = Eigen::Dynamic,
58 MaxColsAtCompileTime = Eigen::Dynamic
59 };
60
62
63 // registry/ids/alpha_ewald/thole_a must be the same as those passed to
64 // EwaldPeriodicDipoleOperator (same segments, layout and interactor).
66 std::vector<Index> ids, double alpha_ewald,
67 double thole_a)
68 : registry_(&registry), ids_(std::move(ids)) {
69 intra_interactor_.emplace(alpha_ewald, thole_a);
72 is_initialized_ = true;
73 }
74
75 Index rows() const { return size_; }
76 Index cols() const { return size_; }
77
78 // Required by the Eigen preconditioner interface. No-ops: the blocks
79 // depend only on registry_/ids_/intra_interactor_ and are factorized
80 // in the constructor.
81 template <typename MatType>
83 return *this;
84 }
85 template <typename MatType>
87 return *this;
88 }
89 template <typename MatType>
91 return *this;
92 }
93
94 template <typename Rhs, typename Dest>
95 void _solve_impl(const Rhs& b, Dest& x) const {
96 // Segments own disjoint slices of x, so they are independent.
97#pragma omp parallel for schedule(dynamic, 4)
98 for (Index nn = 0; nn < Index(ids_.size()); ++nn) {
99 const std::size_t n = std::size_t(nn);
100 const Index base = offsets_[n];
101 const Index width = offsets_[n + 1] - base;
102 // Single-site segments hold a 3x3 LDLT of getPInv(), so no special
103 // case is needed.
104 x.segment(base, width) = factorizations_[n].solve(b.segment(base, width));
105 }
106 }
107
108 template <typename Rhs>
109 inline const Eigen::Solve<EwaldBlockJacobiPreconditioner, Rhs> solve(
110 const Eigen::MatrixBase<Rhs>& b) const {
111 eigen_assert(is_initialized_ &&
112 "EwaldBlockJacobiPreconditioner is not initialized.");
113 eigen_assert(size_ == b.rows() &&
114 "EwaldBlockJacobiPreconditioner::solve(): invalid number "
115 "of rows of the right hand side matrix b");
116 return Eigen::Solve<EwaldBlockJacobiPreconditioner, Rhs>(*this,
117 b.derived());
118 }
119
120 Eigen::ComputationInfo info() const { return Eigen::Success; }
121
122 private:
124 offsets_.reserve(ids_.size() + 1);
125 offsets_.push_back(0);
126 for (Index id : ids_) {
127 if (!registry_->Has(id, EwaldChargeState::Neutral)) {
128 throw std::runtime_error(
129 "EwaldBlockJacobiPreconditioner: segment id not registered at "
130 "EwaldChargeState::Neutral");
131 }
132 Index n_sites = registry_->Get(id, EwaldChargeState::Neutral).size();
133 offsets_.push_back(offsets_.back() + 3 * n_sites);
134 }
135 size_ = offsets_.back();
136 }
137
138 // Builds and factorizes the local block of every segment. The
139 // intramolecular coupling enters with the sign it has in the operator
140 // A = P^-1 - C (subtracted), i.e. opposite to the field accumulated by
141 // AddIntraSegmentCoupling.
143 // Blocks are independent and factorized in parallel.
144 factorizations_.resize(ids_.size());
145#pragma omp parallel for schedule(dynamic, 4)
146 for (Index nn = 0; nn < Index(ids_.size()); ++nn) {
147 const std::size_t n = std::size_t(nn);
148 const PolarSegment& segment =
150 const Index n_sites = segment.size();
151 const Index width = 3 * n_sites;
152 Eigen::MatrixXd block = Eigen::MatrixXd::Zero(width, width);
153
154 for (Index s = 0; s < n_sites; ++s) {
155 block.block<3, 3>(3 * s, 3 * s) = segment[s].getPInv();
156 }
157
158 if (n_sites >= 2) {
159 for (Index i = 0; i < n_sites; ++i) {
160 const PolarSite& site_i = segment[i];
161 for (Index j = i + 1; j < n_sites; ++j) {
162 const PolarSite& site_j = segment[j];
163 const Eigen::Vector3d r_vec = site_i.getPos() - site_j.getPos();
164 const double r = r_vec.norm();
165 // The operator couples an intramolecular pair through its
166 // explicit term l*B_erfc + (l-1)*B_erf plus the B_erf part of
167 // the reciprocal sum, in total the Thole-damped bare coupling
168 // l*(B_erfc + B_erf), which is used here.
170 intra_interactor_->ComputeB(r);
172 intra_interactor_->ComputeErfB(r);
174 intra_interactor_->ComputeThole(r, site_j, site_i);
175 const Eigen::Matrix3d coupling =
176 t.l5 * (b.B2 + berf.B2) * (r_vec * r_vec.transpose()) -
177 t.l3 * (b.B1 + berf.B1) * Eigen::Matrix3d::Identity();
178 // Subtracted, as in A = P^-1 - C. With the wrong sign
179 // (P^-1 + C_intra) a 5000-site solve needed 29 instead of 16
180 // PCG iterations.
181 block.block<3, 3>(3 * i, 3 * j) -= coupling;
182 block.block<3, 3>(3 * j, 3 * i) -= coupling.transpose();
183 }
184 }
185 }
186
187 factorizations_[n].compute(block);
188 }
189 }
190
192 std::vector<Index> ids_;
193 std::optional<EwaldRealSpaceInteractor> intra_interactor_;
194 std::vector<Index> offsets_;
196 std::vector<Eigen::LDLT<Eigen::MatrixXd>> factorizations_;
198};
199
200} // namespace xtp
201} // namespace votca
202
203#endif // VOTCA_XTP_EWALDBLOCKJACOBIPRECONDITIONER_H
const Eigen::Solve< EwaldBlockJacobiPreconditioner, Rhs > solve(const Eigen::MatrixBase< Rhs > &b) const
EwaldBlockJacobiPreconditioner & compute(const MatType &)
std::vector< Eigen::LDLT< Eigen::MatrixXd > > factorizations_
EwaldBlockJacobiPreconditioner(EwaldRegistry &registry, std::vector< Index > ids, double alpha_ewald, double thole_a)
std::optional< EwaldRealSpaceInteractor > intra_interactor_
EwaldBlockJacobiPreconditioner & factorize(const MatType &)
EwaldBlockJacobiPreconditioner & analyzePattern(const MatType &)
Class to represent Atom/Site in electrostatic+polarization.
Definition polarsite.h:36
const Eigen::Vector3d & getPos() const
Definition staticsite.h:80
STL namespace.
Charge transport classes.
Definition ERIs.h:28
ClassicalSegment< PolarSite > PolarSegment
Provides a means for comparing floating point numbers.
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26