159 const std::vector<const Segment*>& linkers,
160 const Segment& seg2_positioned)
const {
161 std::vector<Segment> results;
162 if (linkers.empty()) {
170 results.reserve(linkers.size());
180 const Segment* previous_positioned = &seg1_positioned;
181 for (
const Segment* current_raw : linkers) {
183 *previous_positioned, current_raw->
getId());
185 *current_raw, previous_positioned->
getId());
193 Eigen::Vector3d expected_partner_pos =
208 Eigen::Vector3d shift =
211 Segment current_shifted = *current_raw;
213 results.push_back(current_shifted);
214 previous_positioned = &results.back();
237 std::string ipodcoupling_work_dir =
"OR_FILES";
238 std::string frame_dir =
239 "frame_" + boost::lexical_cast<std::string>(top.
getStep());
249 std::vector<tools::Property*> segment_list = job_input.
Select(
"segment");
250 Index ID_A = segment_list.front()->getAttribute<
Index>(
"id");
251 Index ID_B = segment_list.back()->getAttribute<
Index>(
"id");
253 std::string qmgeo_state_A =
"n";
254 if (segment_list.front()->exists(
"qm_geometry")) {
256 segment_list.front()->getAttribute<std::string>(
"qm_geometry");
258 std::string qmgeo_state_B =
"n";
259 if (segment_list.back()->exists(
"qm_geometry")) {
261 segment_list.back()->getAttribute<std::string>(
"qm_geometry");
270 if (pair ==
nullptr) {
272 "No pair " + std::to_string(ID_A) +
":" +
273 std::to_string(ID_B) +
" found in the neighbor list.");
287 std::string pair_dir =
288 (boost::format(
"%1%%2%%3%%4%%5%") %
"pair" %
"_" % ID_A %
"_" % ID_B)
290 std::filesystem::path arg_path;
291 std::string orbFileAB =
292 (arg_path / ipodcoupling_work_dir /
"pairs_ipodcoupling" / frame_dir /
293 (boost::format(
"%1%%2%%3%%4%%5%") %
"pair_" % ID_A %
"_" % ID_B %
".orb")
296 std::string package_append =
"workdir_" +
Identify();
297 std::string work_dir =
298 (arg_path / ipodcoupling_work_dir / package_append / frame_dir / pair_dir)
305 const Segment* seg1 = pair->Seg1();
306 Segment seg2 = pair->Seg2PbCopy();
323 bool seg1_has_any_bonds =
false;
324 for (
const Atom& atom : *seg1) {
325 if (atom.getBondedPartnerIds()[0] != -1) {
326 seg1_has_any_bonds =
true;
330 bool seg2_has_any_bonds =
false;
331 for (
const Atom& atom : seg2) {
332 if (atom.getBondedPartnerIds()[0] != -1) {
333 seg2_has_any_bonds =
true;
337 if (!seg1_has_any_bonds && !seg2_has_any_bonds) {
339 <<
"WARNING: neither segment " << seg1->
getId() <<
" nor segment "
341 <<
" has any real, actual MD-level bond connectivity at all -- "
342 "H-saturation of cut segment boundaries cannot detect anything "
343 "to saturate at all for this pair, and will silently do nothing. "
344 "This usually means the underlying topology reader used at "
345 "xtp_map time provided no real bond data at all -- check that "
358 std::vector<Segment> positioned_linkers;
361 if (!linkers.empty()) {
366 std::vector<const Segment*> segments = {seg1, &seg2};
367 for (
const Segment& linker : positioned_linkers) {
368 segments.push_back(&linker);
387 Index n_fragment_AB_atoms = qmmol.
size();
405 std::set<Index> linker_atom_ids;
406 for (
const Segment& linker : positioned_linkers) {
407 Index linker_start_atom_id = qmmol.
size();
409 for (
Index i = linker_start_atom_id; i < qmmol.
size(); i++) {
410 linker_atom_ids.insert(i);
421 std::set<Index> present_segment_ids;
422 for (
const Segment* seg : segments) {
423 present_segment_ids.insert(seg->getId());
439 for (
QMAtom& atom : qmmol) {
440 if (!atom.hasExternalBond()) {
443 if (present_segment_ids.count(atom.getExternalBondPartnerSegmentId()) > 0) {
444 atom.clearExternalBond();
466 std::vector<Index> fragment_A_atoms;
467 std::vector<Index> fragment_B_atoms;
468 for (
const QMAtom& atom : relaxed) {
469 Index owning_atom_id = atom.getId();
470 if (owning_atom_id >= n_original_atoms) {
473 if (linker_atom_ids.count(owning_atom_id) > 0) {
476 if (owning_atom_id < n_fragment_A_atoms) {
477 fragment_A_atoms.push_back(atom.getId());
478 }
else if (owning_atom_id < n_fragment_AB_atoms) {
479 fragment_B_atoms.push_back(atom.getId());
484 orbitalsAB.
QMAtoms() = relaxed;
487 std::string qmpackage_work_dir = work_dir;
495 std::string
package = dftpackage_options_.get("name").as<std::string>();
497 qmpackage->setLog(&dft_logger);
498 qmpackage->setRunDir(qmpackage_work_dir);
502 std::filesystem::create_directories(qmpackage_work_dir);
514 if (qmpackage->GuessRequested()) {
516 <<
"A DFT guess was requested in the dftpackage options, but "
517 "IPodCoupling does not support this at all (its own "
518 "supermolecule is not a simple monomer combination, given "
519 "the new, saturating H atom(s) have no corresponding "
520 "monomer orbitals to guess from) -- proceeding with the "
521 "DFT package's own default starting guess instead."
524 qmpackage->WriteInputFile(orbitalsAB);
529 bool run_dft_status = qmpackage->Run();
530 if (!run_dft_status) {
531 SetJobToFailed(jres, pLog, qmpackage->getPackageName() +
" run failed");
538 bool parse_log_status = qmpackage->ParseLogFile(orbitalsAB);
539 if (!parse_log_status) {
543 bool parse_orbitals_status = qmpackage->ParseMOsFile(orbitalsAB);
544 if (!parse_orbitals_status) {
549 qmpackage->CleanUp();
554 }
catch (std::runtime_error&) {
556 "Do input: failed loading orbitals from " + orbFileAB);
562 std::filesystem::create_directories(
563 std::filesystem::path(orbFileAB).parent_path());
604 <<
"PODCoupling: fragment_A_atoms (" << fragment_A_atoms.size()
605 <<
" atoms):" << std::flush;
606 for (
Index a : fragment_A_atoms) {
610 <<
"PODCoupling: fragment_B_atoms (" << fragment_B_atoms.size()
611 <<
" atoms):" << std::flush;
612 for (
Index b : fragment_B_atoms) {
615 if (!linker_atom_ids.empty()) {
617 <<
"PODCoupling: linker_atom_ids, excluded from both fragments ("
618 << linker_atom_ids.size() <<
" atoms):" << std::flush;
619 for (
Index l : linker_atom_ids) {
624 PODCoupling pod(orbitalsAB, &pLog, fragment_A_atoms, fragment_B_atoms);
674 coupling.
setAttribute(
"j", (boost::format(
"%1$1.6e") % J_ev).str());
677 }
catch (std::runtime_error& error) {
678 SetJobToFailed(jres, pLog, std::string(
"PODCoupling: ") + error.what());