199 auto t_start = std::chrono::steady_clock::now();
200 auto elapsed_s = [&](std::chrono::steady_clock::time_point since) {
201 return std::chrono::duration<double>(std::chrono::steady_clock::now() -
207 <<
" Starting Ewald background calculation"
214 std::vector<Index> ids;
217 std::vector<Index> offsets;
219 offsets.reserve(top.
Segments().size() + 1);
220 offsets.push_back(0);
224 ids.push_back(seg.getId());
225 offsets.push_back(offsets.back() + 3 * mol.
size());
227 const Index total_size = offsets.back();
230 <<
" segments, " << (total_size / 3)
231 <<
" polarizable sites total (" << elapsed_s(t_start)
232 <<
"s)" << std::flush;
234 const Eigen::Matrix3d& box = top.
getBox();
235 const double volume = box.col(0).dot(box.col(1).cross(box.col(2)));
274 const double eps = std::max(std::erfc(s_r), 1
e-300);
275 const double s_k = 2.0 * std::sqrt(-std::log(eps));
278 const double n_sites = double(total_size / 3);
280 std::cbrt(volume) * std::sqrt(s_r / s_k);
299 <<
" Real/reciprocal-space sums constructed ("
300 << elapsed_s(t_start) <<
"s)" << std::flush;
303 <<
" nm^-1), k_max=" <<
k_max_ <<
" bohr^-1 ("
306 <<
" k-vectors), real-space cutoff "
309 <<
")" << std::flush;
313 const double n_sites = double(total_size / 3);
314 const double n_real_est = (n_sites / volume) *
319 <<
" Ewald balance (terms per target, estimated): " <<
"real "
320 << n_real_est <<
" vs reciprocal " << recip_sum.
NumKVectors()
329 <<
" bohr) lies beyond the real-space cutoff (" << cutoff
330 <<
" bohr). The shell search will walk shells that contribute "
331 "nothing before it is allowed to stop. Accuracy is unaffected "
332 "-- it is erfc(screening_factor) either way -- but lowering "
333 "r_min to at most the cutoff would save that work."
340 <<
", r_min=" <<
r_min_ <<
" bohr ("
343 <<
", thole_a=" <<
thole_a_ << std::flush;
345 << (
induce_ ?
"true" :
"false")
348 <<
", solver=" << (
use_jor_ ?
"JOR" :
"PCG")
360 <<
TimeStamp() <<
" debug_match_legacy_first_step is ON" << std::flush;
366 auto t_field = std::chrono::steady_clock::now();
367 std::vector<std::pair<Index, PolarSite*>> targets;
368 targets.reserve(std::size_t(total_size / 3));
369 for (std::size_t n = 0; n < ids.size(); ++n) {
371 for (
Index s = 0; s < segment.
size(); ++s) {
375 targets.push_back({ids[n], &site});
382 << elapsed_s(t_field) <<
"s)" << std::flush;
384 auto t_recip = std::chrono::steady_clock::now();
386 std::vector<PolarSite*> recip_targets;
387 recip_targets.reserve(targets.size());
388 for (
const auto& entry : targets) {
389 recip_targets.push_back(entry.second);
393 [&](std::size_t done, std::size_t total) {
396 <<
TimeStamp() <<
" k-space progress: " << done <<
"/" << total
397 <<
" k-vectors (" << elapsed_s(t_recip) <<
"s)" << std::flush;
400 <<
" Reciprocal-space permanent field done ("
401 << elapsed_s(t_recip) <<
"s)" << std::flush;
407 auto t_shape = std::chrono::steady_clock::now();
409 PolarSite shape_probe(-1,
"X", Eigen::Vector3d::Zero());
411 const Eigen::Vector3d shape_field = shape_probe.
V();
412 for (
const auto& entry : targets) {
413 entry.second->V() += shape_field;
417 << elapsed_s(t_shape) <<
"s)" << std::flush;
420 << elapsed_s(t_field) <<
"s)" << std::flush;
421 auto t_intra = std::chrono::steady_clock::now();
438 for (
Index n = 0; n <
Index(ids.size()); ++n) {
441 for (
Index i = 0; i < n_sites; ++i) {
442 for (
Index j = 0; j < n_sites; ++j) {
444 segment[j], segment[i]);
449 <<
" Intramolecular static compensation applied ("
450 << elapsed_s(t_intra) <<
"s)" << std::flush;
452 Eigen::VectorXd b(total_size);
455 for (std::size_t n = 0; n < ids.size(); ++n) {
458 Index base = offsets[n];
459 for (
Index s = 0; s < segment.
size(); ++s) {
460 b.segment<3>(base + 3 * s) = targets[t].second->V();
461 targets[t].second->Reset();
470 <<
" solve (max " <<
max_iter_ <<
" iterations, tolerance "
472 auto t_pcg = std::chrono::steady_clock::now();
480 Index indefinite_at_iteration = -1;
481 double indefinite_curvature = 0.0;
482 double lanczos_min_eigenvalue = std::numeric_limits<double>::quiet_NaN();
491 iterations = result.iterations;
492 residual = result.residual;
493 converged = result.converged;
496 <<
TimeStamp() <<
" JOR finished after " << iterations
497 <<
" iterations, max_dU=" << result.max_dU
498 <<
" avg_dU=" << result.avg_dU <<
" (residual=" << residual
499 <<
", informational) (" << elapsed_s(t_pcg) <<
"s)" << std::flush;
501 throw std::runtime_error(
502 "EwaldBackground: JOR did not converge within max_iter");
515 iterations = result.iterations;
516 residual = result.residual;
517 converged = result.converged;
518 indefinite_at_iteration = result.indefinite_at_iteration;
519 indefinite_curvature = result.indefinite_curvature;
520 lanczos_min_eigenvalue = result.lanczos_min_eigenvalue;
522 Eigen::DiagonalPreconditioner<double> precond;
527 iterations = result.iterations;
528 residual = result.residual;
529 converged = result.converged;
530 indefinite_at_iteration = result.indefinite_at_iteration;
531 indefinite_curvature = result.indefinite_curvature;
532 lanczos_min_eigenvalue = result.lanczos_min_eigenvalue;
536 <<
TimeStamp() <<
" PCG finished after " << iterations
537 <<
" iterations, residual " << residual <<
" (" << elapsed_s(t_pcg)
538 <<
"s)" << std::flush;
543 const double n = double(std::max<Index>(tm.n_calls, 1));
545 <<
TimeStamp() <<
" RawMultiply phase breakdown over " << tm.n_calls
546 <<
" calls (total " << tm.total() <<
"s):" << std::flush;
547 auto line = [&](
const char* nm,
double t) {
550 << (boost::format(
" %1$-12s %2$8.3fs total "
551 "%3$7.3fs/call %4$5.1f%%") %
553 (tm.total() > 0 ? 100.0 * t / tm.total() : 0.0))
557 line(
"setup", tm.setup);
558 line(
"real_space", tm.real_space);
559 line(
"reciprocal", tm.reciprocal);
560 line(
"shape", tm.shape);
561 line(
"assemble", tm.assemble);
562 line(
"intra", tm.intra);
570 " neighbours: %1$.1f entries/target over %2$d targets "
571 "(%3$d kept, %4$d culled beyond %5$.1f bohr = %6$.1f%%)") %
572 ns.entries_per_target() % ns.targets % ns.entries % ns.culled %
577 if (!std::isnan(lanczos_min_eigenvalue)) {
579 << lanczos_min_eigenvalue << std::flush;
580 if (lanczos_min_eigenvalue < 0.0) {
582 <<
" NEGATIVE -- the operator is likely not "
588 if (indefinite_at_iteration >= 0) {
589 throw std::runtime_error(
590 "EwaldBackground: PCG operator is NOT positive-definite "
591 "(found at iteration " +
592 std::to_string(indefinite_at_iteration) +
593 ", p.A.p = " + std::to_string(indefinite_curvature) +
" <= 0).");
597 throw std::runtime_error(
598 "EwaldBackground: PCG did not converge (max_iter reached, "
599 "operator was never found indefinite along the way)");
603 for (std::size_t n = 0; n < ids.size(); ++n) {
605 Index base = offsets[n];
606 for (
Index s = 0; s < segment.
size(); ++s) {
607 segment[s].setInduced_Dipole(x.segment<3>(base + 3 * s));
615 <<
" polarmethod.induce is false: skipping the PCG solve, "
616 "induced dipoles left at zero"
620 auto t_cpt = std::chrono::steady_clock::now();
625 for (std::size_t n = 0; n < ids.size(); ++n) {
627 Index base = offsets[n];
628 for (
Index s = 0; s < segment.
size(); ++s) {
629 segment[s].V() = b.segment<3>(base + 3 * s);
658 <<
"s)" << std::flush;
660 <<
" Ewald background calculation done, total "
661 << elapsed_s(t_start) <<
"s" << std::flush;