34template <
class Matrix>
35void SqrtAndInvSqrt(
const Matrix& A, Matrix& sqrt, Matrix& inv_sqrt,
36 double rel_cutoff = 1
e-12) {
37 Eigen::SelfAdjointEigenSolver<Matrix> es(0.5 * (A + A.transpose()));
38 const auto& w = es.eigenvalues();
39 const double cutoff = rel_cutoff * std::max(w.maxCoeff(), 0.0);
40 auto s = w.array().max(0.0).sqrt().matrix().eval();
43 si(i) = (w(i) > cutoff && w(i) > 0.0) ? 1.0 /
s(i) : 0.0;
45 sqrt = es.eigenvectors() *
s.asDiagonal() * es.eigenvectors().transpose();
47 es.eigenvectors() * si.asDiagonal() * es.eigenvectors().transpose();
61 blocks_.push_back(std::move(block));
65 throw std::runtime_error(
66 "ChargeResponseSolver: the sites of segment " + seg.
getType() +
67 " are not set up for its charge response (PolarRegion)");
70 sites_.push_back({s, i, q});
71 Eigen::Matrix3d sq, isq;
72 SqrtAndInvSqrt(Eigen::Matrix3d(seg[i].getpolarization()), sq, isq);
97 if (ra.
q >= 0 || rb.
q >= 0) {
109 Eigen::VectorXd result = Eigen::VectorXd::Zero(
dim_);
110#pragma omp parallel for schedule(dynamic) reduction(+ : result)
111 for (
Index a = 0; a < ns; ++a) {
114 for (
Index b = a + 1; b < ns; ++b) {
122 result.segment<3>(mb) += c.
T * x.segment<3>(ma);
123 result.segment<3>(ma) += c.
T.transpose() * x.segment<3>(mb);
128 const double qa = x(qa_idx);
129 result.segment<3>(mb) -= qa * c.
B1 * c.
r;
130 result(qa_idx) -= c.
B1 * x.segment<3>(mb).dot(c.
r);
132 result(qb_idx) += qa * c.
B0;
136 const double qb = x(qb_idx);
137 result.segment<3>(ma) += qb * c.
B1 * c.
r;
138 result(qb_idx) += c.
B1 * x.segment<3>(ma).dot(c.
r);
140 result(qa_idx) += qb * c.
B0;
151 Eigen::MatrixXd T = Eigen::MatrixXd::Zero(
dim_,
dim_);
152#pragma omp parallel for schedule(dynamic)
153 for (
Index a = 0; a < ns; ++a) {
156 for (
Index b = a + 1; b < ns; ++b) {
165 T.block<3, 3>(mb, ma) = c.
T;
166 T.block<3, 3>(ma, mb) = c.
T.transpose();
171 T.block<3, 1>(mb, qa) = -c.
B1 * c.
r;
172 T.block<1, 3>(qa, mb) = -c.
B1 * c.
r.transpose();
175 T.block<3, 1>(ma, qb) = c.
B1 * c.
r;
176 T.block<1, 3>(qb, ma) = c.
B1 * c.
r.transpose();
178 if (qa >= 0 && qb >= 0) {
185 A = 0.5 * (A + A.transpose()).eval();
186 A.diagonal().array() += 1.0;
191 const Eigen::MatrixXd& X)
const {
192 Eigen::MatrixXd result(X.rows(), X.cols());
195 result.middleRows(block.q0, n) = block.sqrt * X.middleRows(block.q0, n);
199 result.middleRows<3>(m) =
alpha_sqrt_[std::size_t(a)] * X.middleRows<3>(m);
205 bool inverse)
const {
206 Eigen::VectorXd result(
dim_);
209 result.segment(block.q0, n) =
210 (inverse ? block.inv_sqrt : block.sqrt) * x.segment(block.q0, n);
214 result.segment<3>(m) =
225 Eigen::VectorXd c(
dim_);
231 c.segment<3>(
nq_ + 3 * a) = -(site.
V() + site.
V_noE());
237 Eigen::VectorXd x(
dim_);
263 const double rhs_norm = rhs.norm();
264 if (rhs_norm == 0.0) {
272 Eigen::VectorXd r = rhs -
Apply(y);
273 Eigen::VectorXd p = r;
274 double rr = r.squaredNorm();
276 info.
error = std::sqrt(rr) / rhs_norm;
277 if (info.
error < tolerance) {
281 const Eigen::VectorXd Ap =
Apply(p);
282 const double pAp = p.dot(Ap);
284 throw std::runtime_error(
285 "ChargeResponseSolver: the operator 1 + M^1/2 T M^1/2 is not "
286 "positive definite (polarization catastrophe); check the widths "
287 "and polarizabilities");
289 const double alpha = rr / pAp;
292 const double rr_new = r.squaredNorm();
293 p = r + (rr_new / rr) * p;
297 info.
error = std::sqrt(rr) / rhs_norm;
308 Eigen::VectorXd dq(seg.
size());
310 dq(i) = seg[i].Induced_Charge();
312 e += 0.5 * (block.inv_sqrt * dq).squaredNorm();
const std::string & getType() const
std::vector< SiteRef > sites_
Eigen::MatrixXd DenseOperator() const
double InternalChargeEnergy() const
Coupling PairCoupling(Index a, Index b) const
std::vector< PolarSegment > & segments_
Eigen::MatrixXd ApplyMhalf(const Eigen::MatrixXd &X) const
M^1/2 X for a matrix of unknown-space columns.
std::vector< SegmentBlock > blocks_
const eeInteractor & interactor_
Eigen::VectorXd Sources() const
std::vector< Eigen::Matrix3d > alpha_sqrt_
void WriteToSites(const Eigen::VectorXd &x)
std::vector< Eigen::Matrix3d > alpha_inv_sqrt_
Eigen::VectorXd Apply(const Eigen::VectorXd &y) const
ChargeResponseSolver(std::vector< PolarSegment > &segments, const eeInteractor &interactor)
Eigen::VectorXd ApplyT(const Eigen::VectorXd &x) const
Eigen::VectorXd ReadFromSites() const
Info Solve(double tolerance, Index max_iter, bool use_previous)
const Eigen::MatrixXd & Pi() const
bool hasChargeResponse() const
const ChargeResponse & getChargeResponse() const
Class to represent Atom/Site in electrostatic+polarization.
void setInduced_Dipole(const Eigen::Vector3d &induced_dipole)
const Eigen::Vector3d & Induced_Dipole() const
const Eigen::Vector3d & V_noE() const
const Eigen::Vector3d & V() const
double Induced_Charge() const
void setInduced_Charge(double dq)
const Eigen::Vector3d & getPos() const
Mediates interaction between polar and static sites.
static double PairWidth(const PolarSite &site1, const PolarSite &site2)
Charge transport classes.
ClassicalSegment< PolarSite > PolarSegment
Provides a means for comparing floating point numbers.
Interaction of two normalized Gaussian charge distributions exp(-r^2/s_i^2) and exp(-r^2/s_j^2): erf(...