285 auto t_start = std::chrono::steady_clock::now();
286 auto elapsed_s = [&](std::chrono::steady_clock::time_point since) {
287 return std::chrono::duration<double>(std::chrono::steady_clock::now() -
293 <<
" Starting Ewald background calculation"
300 std::vector<Index> ids;
305 std::vector<Index> offsets;
307 offsets.reserve(top.
Segments().size() + 1);
308 offsets.push_back(0);
312 ids.push_back(seg.getId());
313 offsets.push_back(offsets.back() + 3 * mol.
size());
315 const Index total_size = offsets.back();
318 <<
" segments, " << (total_size / 3)
319 <<
" polarizable sites total (" << elapsed_s(t_start)
320 <<
"s)" << std::flush;
322 const Eigen::Matrix3d& box = top.
getBox();
323 const double volume = box.col(0).dot(box.col(1).cross(box.col(2)));
384 const double eps = std::max(std::erfc(s_r), 1
e-300);
385 const double s_k = 2.0 * std::sqrt(-std::log(eps));
388 const double n_sites = double(total_size / 3);
390 std::cbrt(volume) * std::sqrt(s_r / s_k);
416 <<
" Real/reciprocal-space sums constructed ("
417 << elapsed_s(t_start) <<
"s)" << std::flush;
420 <<
" nm^-1), k_max=" <<
k_max_ <<
" bohr^-1 ("
423 <<
" k-vectors), real-space cutoff "
426 <<
")" << std::flush;
434 const double n_sites = double(total_size / 3);
435 const double n_real_est = (n_sites / volume) *
440 <<
" Ewald balance (terms per target, estimated): " <<
"real "
441 << n_real_est <<
" vs reciprocal " << recip_sum.
NumKVectors()
452 <<
" bohr) lies beyond the real-space cutoff (" << cutoff
453 <<
" bohr). The shell search will walk shells that contribute "
454 "nothing before it is allowed to stop. Accuracy is unaffected "
455 "-- it is erfc(screening_factor) either way -- but lowering "
456 "r_min to at most the cutoff would save that work."
463 <<
", r_min=" <<
r_min_ <<
" bohr ("
466 <<
", thole_a=" <<
thole_a_ << std::flush;
468 << (
induce_ ?
"true" :
"false")
471 <<
", solver=" << (
use_jor_ ?
"JOR" :
"PCG")
475 :
" (unpreconditioned)"))
483 <<
TimeStamp() <<
" debug_match_legacy_first_step is ON" << std::flush;
491 auto t_field = std::chrono::steady_clock::now();
492 std::vector<std::pair<Index, PolarSite*>> targets;
493 targets.reserve(std::size_t(total_size / 3));
494 for (std::size_t n = 0; n < ids.size(); ++n) {
496 for (
Index s = 0; s < segment.
size(); ++s) {
500 targets.push_back({ids[n], &site});
503 for (
const auto& entry : targets) {
508 << elapsed_s(t_field) <<
"s)" << std::flush;
510 auto t_recip = std::chrono::steady_clock::now();
514 std::vector<PolarSite*> recip_targets;
515 recip_targets.reserve(targets.size());
516 for (
const auto& entry : targets) {
517 recip_targets.push_back(entry.second);
521 [&](std::size_t done, std::size_t total) {
525 <<
TimeStamp() <<
" k-space progress: " << done <<
"/" << total
526 <<
" k-vectors (" << elapsed_s(t_recip) <<
"s)" << std::flush;
529 <<
" Reciprocal-space permanent field done ("
530 << elapsed_s(t_recip) <<
"s)" << std::flush;
532 for (
const auto& entry : targets) {
536 << elapsed_s(t_field) <<
"s)" << std::flush;
570 for (
Index n = 0; n <
Index(ids.size()); ++n) {
573 for (
Index i = 0; i < n_sites; ++i) {
574 for (
Index j = 0; j < n_sites; ++j) {
576 segment[j], segment[i]);
581 <<
" Intramolecular static compensation applied ("
582 << elapsed_s(t_field) <<
"s)" << std::flush;
584 Eigen::VectorXd b(total_size);
587 for (std::size_t n = 0; n < ids.size(); ++n) {
590 Index base = offsets[n];
591 for (
Index s = 0; s < segment.
size(); ++s) {
592 b.segment<3>(base + 3 * s) = targets[t].second->V();
593 targets[t].second->Reset();
602 <<
" solve (max " <<
max_iter_ <<
" iterations, tolerance "
604 auto t_pcg = std::chrono::steady_clock::now();
612 Index indefinite_at_iteration = -1;
613 double indefinite_curvature = 0.0;
614 double lanczos_min_eigenvalue = std::numeric_limits<double>::quiet_NaN();
629 iterations = result.iterations;
630 residual = result.residual;
631 converged = result.converged;
634 <<
TimeStamp() <<
" JOR finished after " << iterations
635 <<
" iterations, max_dU=" << result.max_dU
636 <<
" avg_dU=" << result.avg_dU <<
" (residual=" << residual
637 <<
", informational) (" << elapsed_s(t_pcg) <<
"s)" << std::flush;
639 throw std::runtime_error(
640 "EwaldBackground: JOR did not converge within max_iter");
663 iterations = result.iterations;
664 residual = result.residual;
665 converged = result.converged;
666 indefinite_at_iteration = result.indefinite_at_iteration;
667 indefinite_curvature = result.indefinite_curvature;
668 lanczos_min_eigenvalue = result.lanczos_min_eigenvalue;
670 Eigen::DiagonalPreconditioner<double> precond;
675 iterations = result.iterations;
676 residual = result.residual;
677 converged = result.converged;
678 indefinite_at_iteration = result.indefinite_at_iteration;
679 indefinite_curvature = result.indefinite_curvature;
680 lanczos_min_eigenvalue = result.lanczos_min_eigenvalue;
684 <<
TimeStamp() <<
" PCG finished after " << iterations
685 <<
" iterations, residual " << residual <<
" (" << elapsed_s(t_pcg)
686 <<
"s)" << std::flush;
697 const double n = double(std::max<Index>(tm.n_calls, 1));
699 <<
TimeStamp() <<
" RawMultiply phase breakdown over " << tm.n_calls
700 <<
" calls (total " << tm.total() <<
"s):" << std::flush;
701 auto line = [&](
const char* nm,
double t) {
704 << (boost::format(
" %1$-12s %2$8.3fs total "
705 "%3$7.3fs/call %4$5.1f%%") %
707 (tm.total() > 0 ? 100.0 * t / tm.total() : 0.0))
711 line(
"setup", tm.setup);
712 line(
"real_space", tm.real_space);
713 line(
"reciprocal", tm.reciprocal);
714 line(
"shape", tm.shape);
715 line(
"assemble", tm.assemble);
716 line(
"intra", tm.intra);
724 " neighbours: %1$.1f entries/target over %2$d targets "
725 "(%3$d kept, %4$d culled beyond %5$.1f bohr = %6$.1f%%)") %
726 ns.entries_per_target() % ns.targets % ns.entries % ns.culled %
731 if (!std::isnan(lanczos_min_eigenvalue)) {
733 << lanczos_min_eigenvalue << std::flush;
734 if (lanczos_min_eigenvalue < 0.0) {
737 <<
" NEGATIVE -- strong evidence the operator is not "
738 "positive-definite (see "
739 "PcgIndefinitenessResult::lanczos_min_eigenvalue for what "
740 "this does and does not guarantee)."
745 if (indefinite_at_iteration >= 0) {
746 throw std::runtime_error(
747 "EwaldBackground: PCG's own operator was found to be NOT "
748 "positive-definite at iteration " +
749 std::to_string(indefinite_at_iteration) +
750 " (p.A.p = " + std::to_string(indefinite_curvature) +
751 " <= 0) -- this is a direct algebraic certificate, not an "
752 "inference from residual behavior.");
756 throw std::runtime_error(
757 "EwaldBackground: PCG did not converge (max_iter reached, "
758 "operator was never found indefinite along the way)");
762 for (std::size_t n = 0; n < ids.size(); ++n) {
764 Index base = offsets[n];
765 for (
Index s = 0; s < segment.
size(); ++s) {
766 segment[s].setInduced_Dipole(x.segment<3>(base + 3 * s));
777 <<
" polarmethod.induce is false: skipping the PCG solve, "
778 "induced dipoles left at zero"
782 auto t_cpt = std::chrono::steady_clock::now();
797 for (std::size_t n = 0; n < ids.size(); ++n) {
799 Index base = offsets[n];
800 for (
Index s = 0; s < segment.
size(); ++s) {
801 segment[s].V() = b.segment<3>(base + 3 * s);
832 <<
"s)" << std::flush;
834 <<
" Ewald background calculation done, total "
835 << elapsed_s(t_start) <<
"s" << std::flush;