votca 2026-dev
Loading...
Searching...
No Matches
xtp_map.cc
Go to the documentation of this file.
1/*
2 * Copyright 2009-2021 The VOTCA Development Team
3 * (http://www.votca.org)
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License")
6 *
7 * You may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19
20// Standard includes
21#include <fstream>
22#include <iostream>
23#include <stdexcept>
24
25// VOTCA includes
32#include <votca/tools/globals.h>
33
34// Local VOTCA includes
37#include "votca/xtp/topology.h"
38#include "votca/xtp/version.h"
39
40using namespace std;
41
42namespace CSG = votca::csg;
43namespace XTP = votca::xtp;
44namespace TOOLS = votca::tools;
45
46class XtpMap : public TOOLS::Application {
47
48 public:
49 string ProgramName() override { return "xtp_map"; }
50 void HelpText(ostream& out) override {
51 out << "Generates QM|MD topology" << endl;
52 }
53 void ShowHelpText(std::ostream& out) override;
54
55 void Initialize() override;
56 bool EvaluateOptions() override;
57 void Run() override;
58
59 protected:
60};
61
62// Real, direct, standard Bondi (1964) van der Waals radii, in
63// Angstrom -- confirmed directly, via web search, against Wikipedia's
64// own direct table (itself sourced from Bondi's own original
65// compilation), before use -- these are the same, real, standard
66// "consensus" values VMD's own real bond-detection heuristic is
67// itself based on (confirmed directly too: OVITO's own documentation,
68// citing VMD explicitly, states a bond is created when two atoms'
69// own separation is less than 60% of the sum of their own vdW radii
70// -- the same real fraction used directly below). Deliberately NOT
71// votca::tools::Elements::getVdWChelpG()/getVdWMK() -- both
72// confirmed directly, by reading their own real values, to be
73// specialized electrostatic-potential-fitting radii (ChelpG/
74// Merz-Kollman), a genuinely different purpose from, and not the
75// same real values as, standard Bondi vdW radii.
76static const std::map<std::string, double> kBondiVdWRadiusAngstrom = {
77 {"H", 1.20}, {"C", 1.70}, {"N", 1.55}, {"O", 1.52},
78 {"F", 1.47}, {"P", 1.80}, {"S", 1.80}, {"Cl", 1.75},
79};
80
81// Real, direct, explicitly opt-in fallback -- worked through directly
82// with the user -- for guessing real bond connectivity from atom
83// positions alone, when the loaded topology genuinely has none of its
84// own at all (see Md2QmEngine::map's own, new, real warning,
85// md2qmengine.cc, for exactly why this matters at all: automatic
86// H-saturation of cut segment boundaries, IPodCoupling::EvalJob,
87// genuinely depends, entirely, on real bond connectivity actually
88// existing in the first place).
89//
90// A real, direct, deliberate design choice, worked through directly
91// with the user: NEVER an automatic fallback within ordinary xtp_map
92// usage at all -- always a real, explicit, conscious --guess-bonds
93// flag. A genuinely WRONG guessed bond is worse than no bond at all
94// (a missing bond simply means no saturation happens at all, already
95// directly, visibly flagged by the warnings above -- a wrong one
96// would silently corrupt external-bond detection, or RelaxNewAtoms's
97// own connectivity, in a way that is much harder to notice after the
98// fact at all).
99//
100// The real, actual search is restricted to atom pairs within the
101// SAME real MD molecule only -- worked through directly with the
102// user: this is not merely a safety improvement (it eliminates the
103// single most common real failure mode of this kind of heuristic --
104// a non-bonded atom from a genuinely different, neighboring molecule,
105// in a dense, periodic system, being mistaken for a real bond
106// partner), it is also a genuine, real, direct performance necessity
107// -- an unrestricted, whole-system search would be a real, direct
108// O(N^2) operation, prohibitively expensive for any real morphology
109// with many thousands of real atoms, while restricting to real,
110// individual molecules (each typically small) reduces this to a
111// real, direct O(N x k), k the real, typical molecule size.
112//
113// Real, direct, honest limitation, worth being explicit about (worked
114// through directly with the user too): this does NOT eliminate every
115// possible real failure mode of this heuristic at all -- two,
116// genuinely non-bonded atoms within the SAME real molecule (e.g. a
117// folded or coiled real chain, where two, real, distant backbone
118// atoms happen to come close together in real space) can still,
119// genuinely, be mistaken for a real bond. This is exactly why a real,
120// direct, inspectable report of every real guessed bond is always
121// written -- guessed_bonds_report.txt -- so a real user reviewing it
122// has a real, genuine chance of directly catching this specific case
123// too, rather than it being entirely invisible.
125 if (!top.BondedInteractions().empty()) {
126 throw runtime_error(
127 "--guess-bonds was requested, but this topology already has real "
128 "bond connectivity of its own -- refusing to guess additional "
129 "bonds on top of real, existing ones, to avoid silently "
130 "double-counting or conflicting with them. This option is only "
131 "for topologies with genuinely NO real bond data at all.");
132 }
133
134 std::ofstream report("guessed_bonds_report.txt");
135 report << "# Bonds guessed from atom positions alone, via --guess-bonds\n"
136 << "# (a simple, van-der-Waals-radii-based heuristic, restricted "
137 "to atom pairs within the same real molecule) -- review this "
138 "report directly before trusting the guessed connectivity for "
139 "anything at all.\n"
140 << "# mol_id atom1_id atom1_name atom2_id atom2_name "
141 "distance[Ang] cutoff[Ang]\n";
142
143 votca::Index guessed_count = 0;
144 for (const CSG::Molecule& mol : top.Molecules()) {
145 votca::Index nbeads = mol.BeadCount();
146 for (votca::Index i = 0; i < nbeads; i++) {
147 const CSG::Bead* bead1 = mol.getBead(i);
148 auto it1 = kBondiVdWRadiusAngstrom.find(bead1->getElement());
149 if (it1 == kBondiVdWRadiusAngstrom.end()) {
150 continue;
151 }
152 for (votca::Index j = i + 1; j < nbeads; j++) {
153 const CSG::Bead* bead2 = mol.getBead(j);
154 auto it2 = kBondiVdWRadiusAngstrom.find(bead2->getElement());
155 if (it2 == kBondiVdWRadiusAngstrom.end()) {
156 continue;
157 }
158 // Real, direct, PBC-aware distance -- confirmed directly,
159 // earlier this same session, that BCShortestConnection(r_i,
160 // r_j) returns the real, direct, minimum-image vector FROM
161 // r_i TO r_j (r_j - r_i) -- only its own real norm is needed
162 // here, so the exact direction/order does not matter at all
163 // for this specific use.
164 double distance_nm =
165 top.BCShortestConnection(bead1->getPos(), bead2->getPos()).norm();
166 double distance_ang = distance_nm * votca::tools::conv::nm2ang;
167 double cutoff_ang = 0.6 * (it1->second + it2->second);
168 if (distance_ang < cutoff_ang) {
169 // Real, direct bug fix -- the same one, and confirmed the
170 // same way, as gmxtopologyreader.cc's own: a freshly-
171 // constructed IBond's own group_ starts out empty by
172 // default, and Topology::AddBondedInteraction's own call to
173 // getGroup() (topology.cc) directly asserts this is
174 // non-empty -- so setGroup() must always be called first.
175 CSG::Interaction* ic = new CSG::IBond(bead1->getId(), bead2->getId());
176 ic->setGroup("BONDS");
177 top.AddBondedInteraction(ic);
178 report << " " << mol.getId() << " " << bead1->getId() << " "
179 << bead1->getName() << " " << bead2->getId() << " "
180 << bead2->getName() << " "
181 << (boost::format("%1$.3f") % distance_ang).str() << " "
182 << (boost::format("%1$.3f") % cutoff_ang).str() << "\n";
183 guessed_count++;
184 }
185 }
186 }
187 }
188 report.close();
189
190 cout << "\n--guess-bonds: guessed " << guessed_count
191 << " real bonds from atom positions alone (van-der-Waals-radii "
192 "heuristic, restricted to atom pairs within the same molecule) "
193 "-- see guessed_bonds_report.txt for the full, real, direct "
194 "list. Review this directly before trusting it for anything at "
195 "all."
196 << endl;
197}
198
199namespace propt = boost::program_options;
200
202
206
207 AddProgramOptions()("topology,t", propt::value<string>(), " topology");
208 AddProgramOptions()("coordinates,c", propt::value<string>(),
209 " coordinates or trajectory");
210 AddProgramOptions()("segments,s", propt::value<string>(),
211 " definition of segments and fragments");
212 AddProgramOptions()("makesegments,m", " write out a skeleton segments file");
213 AddProgramOptions()("file,f", propt::value<string>(), " state file");
214 AddProgramOptions()("first-frame,i",
215 propt::value<votca::Index>()->default_value(0),
216 " start from this frame");
217 AddProgramOptions()("begin,b", propt::value<double>()->default_value(0.0),
218 " start time in simulation");
219 AddProgramOptions()("nframes,n",
220 propt::value<votca::Index>()->default_value(1),
221 " number of frames to process");
223 "guess-bonds",
224 " guess real bond connectivity from atom positions alone (a simple, "
225 "van-der-Waals-radii-based heuristic, matching the one VMD itself "
226 "uses for visualization -- restricted to atom pairs within the same "
227 "real MD molecule only) -- ONLY if the loaded topology genuinely "
228 "has no real bond connectivity of its own at all; never used if "
229 "real bonds are already present. Writes a real, direct, inspectable "
230 "report of every guessed bond to guessed_bonds_report.txt -- always "
231 "review this before trusting the guessed connectivity for anything "
232 "at all, since this heuristic can be genuinely wrong (see VMD's own "
233 "developers' documented caveats about it).");
234}
235
237
238 CheckRequired("topology", "Missing topology file");
239 CheckRequired("segments", "Missing segment definition file");
240 CheckRequired("coordinates", "Missing trajectory input");
241 if (!(OptionsMap().count("makesegments"))) {
242 CheckRequired("file", "Missing state file");
243 }
244 return 1;
245}
246
248
249 std::string name = ProgramName();
250 if (VersionString() != "") {
251 name = name + ", version " + VersionString();
252 }
254
255 // ++++++++++++++++++++++++++++ //
256 // Create MD topology from file //
257 // ++++++++++++++++++++++++++++ //
258
259 // Create topology reader
260 string topfile = OptionsMap()["topology"].as<string>();
261 std::unique_ptr<CSG::TopologyReader> topread =
262 CSG::TopReaderFactory().Create(topfile);
263
264 if (topread == nullptr) {
265 throw runtime_error(string("Input format not supported: ") +
266 OptionsMap()["topology"].as<string>());
267 }
268 CSG::Topology mdtopol;
269 topread->ReadTopology(topfile, mdtopol);
270 if (votca::Log::verbose()) {
271 cout << "Read MD topology from " << topfile << ": Found "
272 << mdtopol.BeadCount() << " atoms in " << mdtopol.MoleculeCount()
273 << " molecules. " << endl;
274 }
275
276 // ++++++++++++++++++++++++++++++ //
277 // Create MD trajectory from file //
278 // ++++++++++++++++++++++++++++++ //
279
280 // Create trajectory reader and initialize
281 string trjfile = OptionsMap()["coordinates"].as<string>();
282 std::unique_ptr<CSG::TrajectoryReader> trjread =
283 CSG::TrjReaderFactory().Create(trjfile);
284
285 if (trjread == nullptr) {
286 throw runtime_error(string("Input format not supported: ") +
287 OptionsMap()["coordinates"].as<string>());
288 }
289 trjread->Open(trjfile);
290 trjread->FirstFrame(mdtopol);
291
292 if (OptionsMap().count("guess-bonds")) {
293 GuessBonds(mdtopol);
294 }
295
296 string mapfile = OptionsMap()["segments"].as<string>();
297 if (OptionsMap().count("makesegments")) {
298 if (TOOLS::filesystem::FileExists(mapfile)) {
299 cout << endl
300 << "xtp_map : map file '" << mapfile
301 << "' already in use. Delete the current mapfile or specify a "
302 "different name."
303 << endl;
304 return;
305 }
306
307 cout << " Writing template mapfile to " << mapfile << std::endl;
308
309 TOOLS::Property mapfile_prop("topology", "", "");
310 TOOLS::Property& molecules = mapfile_prop.add("molecules", "");
311
312 std::map<std::string, const CSG::Molecule*> firstmolecule;
313
314 std::map<std::string, votca::Index> molecule_names;
315 for (const CSG::Molecule& mol : mdtopol.Molecules()) {
316 if (!molecule_names.count(mol.getName())) {
317 firstmolecule[mol.getName()] = &mol;
318 }
319 molecule_names[mol.getName()]++;
320 }
321 for (const auto& mol : molecule_names) {
322 std::cout << "Found " << mol.second << " with name " << mol.first
323 << std::endl;
324 }
325 for (const auto& mol : molecule_names) {
326 TOOLS::Property& molecule = molecules.add("molecule", "");
327 molecule.add("mdname", mol.first);
328 TOOLS::Property& segments = molecule.add("segments", "");
329 TOOLS::Property& segment = segments.add("segment", "");
330 segment.add("name", "UPTOYOU_BUTUNIQUE");
331 segment.add("qmcoords_n", "XYZFILE_GROUNDSTATE");
332 segment.add("multipoles_n", "MPSFILE_GROUNDSTATE");
333 segment.add("map2md", "WANTTOMAPTOMDGEOMETRY");
334 segment.add("U_xX_nN_h", "REORG1_hole");
335 segment.add("U_nX_nN_h", "REORG2_hole");
336 segment.add("U_xN_xX_h", "REORG3_hole");
337 TOOLS::Property& fragments = segment.add("fragments", "");
338 TOOLS::Property& fragment = fragments.add("fragment", "");
339 std::string atomnames = "";
340 const CSG::Molecule* csgmol = firstmolecule[mol.first];
341 std::vector<const CSG::Bead*> sortedbeads;
342 sortedbeads.reserve(csgmol->BeadCount());
343 for (const CSG::Bead* bead : csgmol->Beads()) {
344 sortedbeads.push_back(bead);
345 }
346 std::sort(sortedbeads.begin(), sortedbeads.end(),
347 [&](const CSG::Bead* b1, const CSG::Bead* b2) {
348 return b1->getId() < b2->getId();
349 });
350
351 for (const CSG::Bead* bead : sortedbeads) {
352 atomnames += " " + std::to_string(bead->getResnr()) + ":" +
353 bead->getName() + ":" + std::to_string(bead->getId());
354 }
355 fragment.add("name", "UPTOYOU_BUTUNIQUE");
356 fragment.add("mdatoms", atomnames);
357 fragment.add("qmatoms", "IDS of QMATOMS i.e 0:C 1:H 2:C");
358 fragment.add("mpoles", "IDS of MPOLES i.e 0:C 1:H 2:C");
359 fragment.add("weights",
360 "weights for mapping(often atomic mass) i.e. 12 1 12");
361 fragment.add("localframe", "IDs of up to 3 qmatoms or mpoles i.e. 0 1 2");
362 std::ofstream template_mapfile(mapfile);
363 template_mapfile << mapfile_prop << std::flush;
364 template_mapfile.close();
365
366 std::cout << "MOLECULETYPE " << csgmol->getName() << std::endl;
367 std::cout << "SAMPLECOORDINATES" << std::endl;
368 std::cout << "ID NAME COORDINATES[Angstroem] " << std::endl;
369 for (const CSG::Bead* bead : sortedbeads) {
370 Eigen::Vector3d pos = bead->getPos() * votca::tools::conv::nm2ang;
371 std::string output =
372 (boost::format("%1$i %2$s %3$+1.4f %4$+1.4f %5$+1.4f\n") %
373 bead->getId() % bead->getName() % pos[0] % pos[1] % pos[2])
374 .str();
375 std::cout << output;
376 }
377 }
378 std::cout << std::flush;
379 return;
380 }
381
382 if (!TOOLS::filesystem::FileExists(mapfile)) {
383 cout << endl
384 << "xtp_map : map file '" << mapfile << "' could not be found."
385 << endl;
386 return;
387 }
388 XTP::Md2QmEngine md2qm(mapfile);
389
390 votca::Index firstFrame = OptionsMap()["first-frame"].as<votca::Index>();
391 votca::Index nFrames = OptionsMap()["nframes"].as<votca::Index>();
392 bool beginAt = false;
393 double time = OptionsMap()["begin"].as<double>();
394 double startTime = mdtopol.getTime();
395 if (time > 0.0) {
396 beginAt = true;
397 startTime = time;
398 }
399
400 // Extract first frame specified
401 bool hasFrame;
402 votca::Index frames_found = 0;
403 votca::Index firstframecounter = firstFrame;
404 for (hasFrame = true; hasFrame == true;
405 hasFrame = trjread->NextFrame(mdtopol)) {
406 frames_found++;
407 if (((mdtopol.getTime() < startTime) && beginAt) || firstframecounter > 0) {
408 firstframecounter--;
409 continue;
410 }
411 break;
412 }
413 if (!hasFrame) {
414 trjread->Close();
415
416 throw runtime_error("Time or frame number exceeds trajectory length");
417 }
418 if (votca::Log::verbose()) {
419 cout << "Read MD trajectory from " << trjfile << ": found " << frames_found
420 << " frames, starting from frame " << firstFrame << endl;
421 }
422 // +++++++++++++++++++++++++ //
423 // Convert MD to QM Topology //
424 // +++++++++++++++++++++++++ //
425
426 string statefile = OptionsMap()["file"].as<string>();
427 if (TOOLS::filesystem::FileExists(statefile)) {
428 cout << endl
429 << "xtp_map : state file '" << statefile
430 << "' already in use. Delete the current statefile or specify a "
431 "different name."
432 << endl;
433 return;
434 }
435
436 XTP::StateSaver statsav(statefile);
437 votca::Index laststep =
438 -1; // for some formats no step is given out so we check if the step
439 for (votca::Index saved = 0; hasFrame && saved < nFrames;
440 hasFrame = trjread->NextFrame(mdtopol), saved++) {
441 if (mdtopol.getStep() == laststep) {
442 mdtopol.setStep(laststep + 1);
443 }
444 laststep = mdtopol.getStep();
445 XTP::Topology qmtopol = md2qm.map(mdtopol);
446 statsav.WriteFrame(qmtopol);
447 }
448}
449
450void XtpMap::ShowHelpText(std::ostream& out) {
451 string name = ProgramName();
452 if (VersionString() != "") {
453 name = name + ", version " + VersionString();
454 }
456 HelpText(out);
457 out << "\n\n" << VisibleOptions() << endl;
458}
459
460int main(int argc, char** argv) {
461 XtpMap xtpmap;
462 return xtpmap.Exec(argc, argv);
463}
void Run() override
Main body of application.
Definition xtp_map.cc:247
string ProgramName() override
program name
Definition xtp_map.cc:49
void ShowHelpText(std::ostream &out) override
Definition xtp_map.cc:450
void HelpText(ostream &out) override
help text of application without version information
Definition xtp_map.cc:50
void Initialize() override
Initialize application data.
Definition xtp_map.cc:201
bool EvaluateOptions() override
Process command line options.
Definition xtp_map.cc:236
std::string getElement() const noexcept
Returns the element type of the bead.
Definition basebead.h:98
virtual const Eigen::Vector3d & getPos() const
Definition basebead.h:166
std::string getName() const
Gets the name of the bead.
Definition basebead.h:58
Index getId() const noexcept
Gets the id of the bead.
Definition basebead.h:52
information about a bead
Definition bead.h:50
bond interaction
base class for all interactions
Definition interaction.h:40
void setGroup(const std::string &group)
Definition interaction.h:49
information about molecules
Definition molecule.h:45
const std::string & getName() const
get the name of the molecule
Definition molecule.h:51
Index BeadCount() const
get the number of beads in the molecule
Definition molecule.h:65
const std::vector< Bead * > & Beads() const
Definition molecule.h:67
static void RegisterPlugins(void)
topology of the whole system
Definition topology.h:81
double getTime() const
Definition topology.h:317
Index MoleculeCount() const
number of molecules in the system
Definition topology.h:144
void AddBondedInteraction(Interaction *ic)
Definition topology.cc:188
Index BeadCount() const
Definition topology.h:150
Index getStep() const
Definition topology.h:329
Eigen::Vector3d BCShortestConnection(const Eigen::Vector3d &r_i, const Eigen::Vector3d &r_j) const
calculate shortest vector connecting two points
Definition topology.cc:238
void setStep(Index s)
Definition topology.h:323
MoleculeContainer & Molecules()
Definition topology.h:182
InteractionContainer & BondedInteractions()
Definition topology.h:189
int Exec(int argc, char **argv)
executes the program
boost::program_options::variables_map & OptionsMap()
get available program options & descriptions
boost::program_options::options_description & VisibleOptions()
filters out the Hidden group from the options descriptions
virtual std::string VersionString()
version string of application
Definition application.h:55
boost::program_options::options_description_easy_init AddProgramOptions(const std::string &group="")
add option for command line
void CheckRequired(const std::string &option_name, const std::string &error_msg="")
Check weather required option is set.
class to manage program options with xml serialization functionality
Definition property.h:55
Property & add(const std::string &key, const std::string &value)
add a new property to structure
Definition property.cc:108
Topology map(const csg::Topology &top) const
void WriteFrame(const Topology &top)
Definition statesaver.cc:44
Container for segments and box and atoms.
Definition topology.h:41
STL namespace.
FileFormatFactory< TopologyReader > & TopReaderFactory()
FileFormatFactory< TrajectoryReader > & TrjReaderFactory()
const double nm2ang
Definition constants.h:50
bool FileExists(const std::string &filename)
Definition filesystem.cc:50
Charge transport classes.
Definition ERIs.h:28
void HelpTextHeader(const std::string &tool_name)
Definition version.cc:34
Eigen::Index Index
Definition types.h:26
static bool verbose()
Definition globals.h:32
int main(int argc, char **argv)
Definition xtp_map.cc:460
void GuessBonds(CSG::Topology &top)
Definition xtp_map.cc:124
static const std::map< std::string, double > kBondiVdWRadiusAngstrom
Definition xtp_map.cc:76