votca 2024.2-dev
Loading...
Searching...
No Matches
segmentmapper.cc
Go to the documentation of this file.
1/*
2 * Copyright 2016 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 <regex>
22
23// Local VOTCA includes
25
26namespace votca {
27namespace xtp {
28
29template <class AtomContainer>
33
34template <class AtomContainer>
36 const SegId& segid) const {
37 if (segid.hasFile()) {
38 std::string filename = segid.FileName();
39 filename = std::regex_replace(filename, std::regex("\\$SEGID"),
40 std::to_string(seg.getId()));
41 filename =
42 std::regex_replace(filename, std::regex("\\$SEGNAME"), seg.getType());
43 return map(seg, filename);
44 } else {
45 QMState state = segid.getQMState();
46 return map(seg, state);
47 }
48}
49
50template <class AtomContainer>
51template <typename T>
53 const std::vector<double>& weights, const T& atoms) const {
54 Eigen::Vector3d map_pos = Eigen::Vector3d::Zero();
55 double map_weight = 0.0;
56 for (Index i = 0; i < Index(atoms.size()); i++) {
57 map_pos += atoms[i]->getPos() * weights[i];
58 map_weight += weights[i];
59 }
60 return map_pos = map_pos / map_weight;
61}
62
63template <class AtomContainer>
65 const tools::Property& frag) const {
66
67 std::vector<double> weights;
68 if (frag.exists(mapatom_xml_.at("weights"))) {
69 weights =
70 frag.get(mapatom_xml_.at("weights")).template as<std::vector<double>>();
71 } else if (frag.exists("weights")) {
72 weights = frag.get("weights").template as<std::vector<double>>();
73 } else {
74 XTP_LOG(Log::error, log_) << " Did not find weights for fragment "
75 << frag.get("name").as<std::string>()
76 << " Using atomic masses" << std::flush;
77 std::string frags =
78 frag.get(mapatom_xml_.at("atoms")).template as<std::string>();
79 tools::Tokenizer tok_atoms(frags, " \t\n");
80 std::vector<std::string> atom_strings = tok_atoms.ToVector();
82 for (auto a_string : atom_strings) {
83 tools::Tokenizer tok_atom(a_string, ":");
84 std::vector<std::string> entries = tok_atom.ToVector();
85 if (entries.size() != 2) {
86 throw std::runtime_error("Cannot get weight from Element for " +
87 a_string);
88 }
89
90 double weight = e.getMass(entries[1]);
91 XTP_LOG(Log::info, log_) << entries[1] << ":" << weight << " ";
92 weights.push_back(weight);
93 }
94 XTP_LOG(Log::error, log_) << std::endl;
95 }
96
97 return weights;
98}
99
100template <class AtomContainer>
102 const tools::Property& frag) {
103 std::vector<std::string> map_atoms =
104 frag.get(mapatom_xml_["atoms"]).template as<std::vector<std::string>>();
105 std::vector<std::string> md_atoms =
106 frag.get("mdatoms").as<std::vector<std::string>>();
107
108 if (md_atoms.size() != map_atoms.size()) {
109 throw std::runtime_error(
110 "Mapping for segment " + seginfo.segname + " fragment " +
111 frag.get("name").as<std::string>() +
112 " does not have same numbers of md and " + mapatom_xml_["atoms"] +
113 "."
114 "If you want to leave a qmatom out, place a ':' instead");
115 }
116
117 std::vector<double> weights = getWeights(frag);
118
119 if (md_atoms.size() != weights.size()) {
120 throw std::runtime_error("Mapping for segment " + seginfo.segname +
121 " fragment " + frag.get("name").as<std::string>() +
122 " does not have same numbers of md and weights. "
123 "If you want to leave a " +
124 mapatom_xml_["name"] +
125 " out, place a '0' instead");
126 }
127
128 FragInfo mapfragment;
129 std::vector<Index> mapatom_ids;
130
131 for (Index i = 0; i < Index(map_atoms.size()); i++) {
132 const std::string& map_string = map_atoms[i];
133 const std::string& md_string = md_atoms[i];
134 const double& weight = weights[i];
135 atom_id md_result = StringToMDIndex(md_string);
136 seginfo.mdatoms.push_back(md_result.first);
137
138 if (map_string == ":") {
139 continue;
140 }
141 atom_id map_result = StringToMapIndex(map_string);
142 mapatom_ids.push_back(map_result.first);
143 seginfo.mapatoms.push_back(map_result);
144 if (Atom::GetElementFromString(md_result.second) != map_result.second) {
145 XTP_LOG(Log::error, log_)
146 << "WARNING: mdatom'" << md_result.second << "' and "
147 << mapatom_xml_["name"] << " '" << map_result.second
148 << "' do not have same element" << std::flush;
149 }
150 mapfragment.mapatom_ids.push_back(map_result);
151 mapfragment.weights.push_back(weight);
152 mapfragment.mdatom_ids.push_back(md_result);
153 }
154
155 std::vector<Index> frame =
156 tools::Tokenizer(getFrame(frag), " \t\n").ToVector<Index>();
157 if (frame.size() > 3) {
158 throw std::runtime_error(
159 "Local frame for segment " + seginfo.segname + " fragment " +
160 frag.get("name").as<std::string>() +
161 " has more than 3 atoms, please specify only up to three atoms");
162 } else if (frame.empty()) {
163 throw std::runtime_error("No local frame for segment " + seginfo.segname +
164 " fragment " + frag.get("name").as<std::string>() +
165 " specified");
166 }
167 for (Index atomid : frame) {
168 if (std::find(mapatom_ids.begin(), mapatom_ids.end(), atomid) ==
169 mapatom_ids.end()) {
170 throw std::runtime_error("Atom " + std::to_string(atomid) +
171 " in local frame cannot be found in " +
172 mapatom_xml_["atoms"] + ".");
173 }
174 }
175
176 mapfragment.map_local_frame = frame;
177 seginfo.fragments.push_back(mapfragment);
178}
179
180template <class AtomContainer>
181void SegmentMapper<AtomContainer>::LoadMappingFile(const std::string& mapfile) {
182 tools::Property topology_map;
183 topology_map.LoadFromXML(mapfile);
184
185 std::string molkey = "topology.molecules.molecule";
186 std::vector<tools::Property*> molecules = topology_map.Select(molkey);
187 std::string segkey = "segments.segment";
188 for (tools::Property* mol : molecules) {
189 std::vector<tools::Property*> segments = mol->Select(segkey);
190 for (tools::Property* seg : segments) {
191 Seginfo seginfo;
192
193 std::string coordfile_key = mapatom_xml_["coords"] + "_*";
194 std::vector<tools::Property*> files = seg->Select(coordfile_key);
195 for (tools::Property* file : files) {
196 seginfo.coordfiles[file->name()] = file->as<std::string>();
197 }
198 std::string segname = seg->get("name").as<std::string>();
199 seginfo.segname = segname;
200 std::string fragkey = "fragments.fragment";
201
202 seginfo.map2md = seg->get("map2md").as<bool>();
203
204 std::vector<tools::Property*> fragments = seg->Select(fragkey);
205 for (tools::Property* frag : fragments) {
206 ParseFragment(seginfo, *frag);
207 }
208
209 Index map_atom_min_id =
210 std::min_element(seginfo.mapatoms.begin(), seginfo.mapatoms.end(),
211 [](const atom_id& a, const atom_id& b) {
212 return a.first < b.first;
213 })
214 ->first;
215 if (map_atom_min_id != 0) {
216 throw std::runtime_error(
217 mapatom_xml_["atoms"] + " for segment " + seginfo.segname +
218 " do not start at zero index. Each segment "
219 "should have its own coordinate file. If you use an old ctp "
220 "mapping file run 'xtp_update_mapfile' on it.");
221 }
222
223 seginfo.minmax = CalcAtomIdRange(seginfo.mdatoms);
224 segment_info_[segname] = seginfo;
225 }
226 }
227}
228
229template <class AtomContainer>
231 const std::string& map_string) const {
232 tools::Tokenizer tok(map_string, ":");
233 std::vector<std::string> result = tok.ToVector();
234 if (result.size() != 2) {
235 throw std::runtime_error("Entry " + map_string +
236 " is not properly formatted.");
237 }
238 return std::pair<Index, std::string>(std::stoi(result[0]), result[1]);
239}
240template <class AtomContainer>
242 const std::string& md_string) const {
243 tools::Tokenizer tok(md_string, ":");
244 std::vector<std::string> result = tok.ToVector();
245 if (result.size() != 3) {
246 throw std::runtime_error("Entry " + md_string +
247 " is not properly formatted.");
248 }
249 Index atomid = 0;
250 try {
251 atomid = std::stoi(result[2]);
252 } catch (std::invalid_argument&) {
253 throw std::runtime_error("Atom entry " + md_string +
254 " is not well formatted");
255 }
256 return std::pair<Index, std::string>(atomid, result[1]);
257}
258
259template <class AtomContainer>
261 const std::vector<Index>& seg) const {
262 Index max_res_id = *std::max_element(seg.begin(), seg.end());
263 Index min_res_id = *std::min_element(seg.begin(), seg.end());
264 return std::pair<Index, Index>(min_res_id, max_res_id);
265}
266template <class AtomContainer>
268 const Segment& seg) const {
269 Index max_res_id = std::max_element(seg.begin(), seg.end(),
270 [](const Atom& a, const Atom& b) {
271 return a.getId() < b.getId();
272 })
273 ->getId();
274
275 Index min_res_id = std::min_element(seg.begin(), seg.end(),
276 [](const Atom& a, const Atom& b) {
277 return a.getId() < b.getId();
278 })
279 ->getId();
280 return std::pair<Index, Index>(min_res_id, max_res_id);
281}
282
283template <class AtomContainer>
285 const std::vector<mapAtom*>& fragment_mapatoms,
286 const std::vector<const Atom*>& fragment_mdatoms) const {
287 for (Index i = 0; i < Index(fragment_mapatoms.size()); i++) {
288 const Atom* a = fragment_mdatoms[i];
289 mapAtom* b = fragment_mapatoms[i];
290 b->setPos(a->getPos());
291 }
292}
293
294template <class AtomContainer>
296 Index atomid, const std::vector<mapAtom*>& fragment_mapatoms) const {
297 Index i = 0;
298 for (; i < Index(fragment_mapatoms.size()); i++) {
299 if (fragment_mapatoms[i]->getId() == atomid) {
300 break;
301 }
302 }
303 return i;
304}
305template <class AtomContainer>
307 const FragInfo& frag, const std::vector<mapAtom*>& fragment_mapatoms,
308 const std::vector<const Atom*>& fragment_mdatoms) const {
309 std::vector<Eigen::Vector3d> local_map_frame;
310 std::vector<Eigen::Vector3d> local_md_frame;
311 for (Index id : frag.map_local_frame) {
312 Index i = FindVectorIndexFromAtomId(id, fragment_mapatoms);
313 local_map_frame.push_back(fragment_mapatoms[i]->getPos());
314 local_md_frame.push_back(fragment_mdatoms[i]->getPos());
315 }
316
317 Index symmetry = frag.map_local_frame.size();
318 Eigen::Vector3d map_com = CalcWeightedPos(frag.weights, fragment_mapatoms);
319 Eigen::Vector3d md_com = CalcWeightedPos(frag.weights, fragment_mdatoms);
320
321 Eigen::Vector3d shift_map2md = md_com - map_com;
322
323 Eigen::Matrix3d rot_map = Eigen::Matrix3d::Identity();
324 Eigen::Matrix3d rot_md = Eigen::Matrix3d::Identity();
325
326 // building local frame
327 if (symmetry > 1) {
328 // middle atom is the origin
329 Eigen::Vector3d x_map = local_map_frame[0] - local_map_frame[1];
330 Eigen::Vector3d x_md = local_md_frame[0] - local_md_frame[1];
331 Eigen::Vector3d y_map;
332 Eigen::Vector3d y_md;
333 Eigen::Vector3d z_map;
334 Eigen::Vector3d z_md;
335 if (symmetry == 3) {
336 y_map = local_map_frame[2] - local_map_frame[1];
337 y_md = local_md_frame[2] - local_md_frame[1];
338 z_map = x_map.cross(y_map);
339 z_md = x_md.cross(y_md);
340 y_map = z_map.cross(x_map);
341 y_md = z_md.cross(x_md);
342
343 } else {
344 Eigen::Vector3d unit = Eigen::Vector3d::UnitX();
345 if (std::abs(unit.dot(x_map) / x_map.norm()) < 1e-6) {
346 unit = Eigen::Vector3d::UnitY();
347 }
348 y_map = (x_map.cross(unit));
349 if (std::abs(unit.dot(x_md) / x_md.norm()) < 1e-6) {
350 unit = Eigen::Vector3d::UnitX();
351 }
352 y_md = (x_md.cross(unit));
353 z_map = x_map.cross(y_map);
354 z_md = x_md.cross(y_md);
355 }
356
357 if (x_map.squaredNorm() < 1e-18 || y_map.squaredNorm() < 1e-18 ||
358 z_map.squaredNorm() < 1e-18) {
359 throw std::runtime_error(
360 mapatom_xml_.at("tag") +
361 " basis vectors are very small, choose different local basis");
362 }
363 rot_map.col(0) = x_map.normalized();
364 rot_map.col(1) = y_map.normalized();
365 rot_map.col(2) = z_map.normalized();
366
367 if (x_md.squaredNorm() < 1e-18 || y_md.squaredNorm() < 1e-18 ||
368 z_md.squaredNorm() < 1e-18) {
369 throw std::runtime_error(
370 "MD basis vectors are very small, choose different local basis");
371 }
372 rot_md.col(0) = x_md.normalized();
373 rot_md.col(1) = y_md.normalized();
374 rot_md.col(2) = z_md.normalized();
375 }
376 Eigen::Matrix3d rotateMAP2MD = rot_md * rot_map.transpose();
377 for (mapAtom* atom : fragment_mapatoms) {
378 if (getRank(*atom) > 0 && symmetry < 3) {
379 throw std::runtime_error(
380 "Local frame has less than 3 atoms, thus higher rank multipoles "
381 "cannot be mapped.");
382 }
383 atom->Translate(shift_map2md);
384 atom->Rotate(rotateMAP2MD, md_com);
385 }
386}
387template <class AtomContainer>
389 QMState state) const {
390 if (segment_info_.count(seg.getType()) == 0) {
391 throw std::runtime_error(
392 "Could not find a Segment of name: " + seg.getType() + " in mapfile.");
393 }
394 Seginfo seginfo = segment_info_.at(seg.getType());
395 std::string coordsfiletag =
396 mapatom_xml_.at("coords") + "_" + state.Type().ToString();
397 if (seginfo.coordfiles.count(coordsfiletag) == 0) {
398 throw std::runtime_error("Could not find a coordinate file for " +
399 seg.getType() +
400 " id:" + std::to_string(seg.getId()) +
401 " segment/state: " + coordsfiletag);
402 }
403 std::string coordsfilename = seginfo.coordfiles.at(coordsfiletag);
404 return map(seg, coordsfilename);
405}
406
407template <class AtomContainer>
409 const Segment& seg, const std::string& coordfilename) const {
410
411 if (segment_info_.count(seg.getType()) == 0) {
412 throw std::runtime_error(
413 "Could not find a Segment of name: " + seg.getType() + " in mapfile.");
414 }
415 Seginfo seginfo = segment_info_.at(seg.getType());
416 if (Index(seginfo.mdatoms.size()) != seg.size()) {
417 throw std::runtime_error(
418 "Segment '" + seg.getType() +
419 "' does not contain the same number of atoms as mapping file: " +
420 std::to_string(seginfo.mdatoms.size()) + " vs. " +
421 std::to_string(seg.size()));
422 }
423
424 std::pair<Index, Index> minmax_map = seginfo.minmax;
425 std::pair<Index, Index> minmax = CalcAtomIdRange(seg);
426
427 if ((minmax_map.first - minmax_map.second) !=
428 (minmax.first - minmax.second)) {
429 throw std::runtime_error("AtomId range for segment " + seg.getType() + ":" +
430 std::to_string(seg.getId()) +
431 " and the mapping do not agree: Segment[" +
432 std::to_string(minmax.first) + "," +
433 std::to_string(minmax.second) + "] Map[" +
434 std::to_string(minmax_map.first) + "," +
435 std::to_string(minmax_map.second) + "]");
436 }
437
438 Index atomidoffset = minmax.first - minmax_map.first;
439
440 AtomContainer Result(seg.getType(), seg.getId());
441 Result.LoadFromFile(coordfilename);
442
443 if (Index(seginfo.mapatoms.size()) != Result.size()) {
444 throw std::runtime_error(
445 mapatom_xml_.at("tag") + "Segment '" + seg.getType() +
446 "' does not contain the same number of atoms as mapping file: " +
447 std::to_string(seginfo.mapatoms.size()) + " vs. " +
448 std::to_string(Result.size()));
449 }
450
451 for (FragInfo& frag : seginfo.fragments) {
452 for (atom_id& id : frag.mdatom_ids) {
453 id.first += atomidoffset;
454 }
455
456 std::vector<mapAtom*> fragment_mapatoms;
457 for (const atom_id& id : frag.mapatom_ids) {
458 if (id.second != Result[id.first].getElement()) {
459 throw std::runtime_error(
460 "Element of mapping atom " + std::to_string(id.first) + ":" +
461 id.second + " does not agree with Element of parsed Atom " +
462 Result[id.first].getElement());
463 }
464 fragment_mapatoms.push_back(&Result[id.first]);
465 }
466 std::vector<const Atom*> fragment_mdatoms;
467 for (const atom_id& id : frag.mdatom_ids) {
468 const Atom* atom = seg.getAtom(id.first);
469 if (atom == nullptr) {
470 throw std::runtime_error(
471 "Could not find an atom with name:" + id.second + "id" +
472 std::to_string(id.first) + " in segment " + seg.getType());
473 }
474 fragment_mdatoms.push_back(atom);
475 }
476
477 if (seginfo.map2md) {
478 PlaceMapAtomonMD(fragment_mapatoms, fragment_mdatoms);
479 } else {
480 MapMapAtomonMD(frag, fragment_mapatoms, fragment_mdatoms);
481 }
482 }
483
484 Result.calcPos();
485 return Result;
486}
487
488template class SegmentMapper<QMMolecule>;
489template class SegmentMapper<StaticSegment>;
490template class SegmentMapper<PolarSegment>;
491
492} // namespace xtp
493} // namespace votca
information about an element
Definition elements.h:42
class to manage program options with xml serialization functionality
Definition property.h:55
Property & get(const std::string &key)
get existing property
Definition property.cc:79
bool exists(const std::string &key) const
check whether property exists
Definition property.cc:122
T as() const
return value as type
Definition property.h:283
std::vector< Property * > Select(const std::string &filter)
select property based on a filter
Definition property.cc:185
void LoadFromXML(std::string filename)
Definition property.cc:238
break string into words
Definition tokenizer.h:72
std::vector< T > ToVector()
store all words in a vector of type T, does type conversion.
Definition tokenizer.h:109
const std::string & getType() const
std::vector< T >::iterator end()
std::vector< T >::iterator begin()
static std::string GetElementFromString(const std::string &MDName)
Definition atom.cc:71
const Eigen::Vector3d & getPos() const
Definition atom.h:66
Logger is used for thread-safe output of messages.
Definition logger.h:164
std::string ToString() const
Definition qmstate.cc:35
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
Definition qmstate.h:132
const QMStateType & Type() const
Definition qmstate.h:151
QMState getQMState() const
Definition segid.h:63
std::string FileName() const
Definition segid.h:62
bool hasFile() const
Definition segid.h:61
std::pair< Index, std::string > atom_id
void LoadMappingFile(const std::string &mapfile)
void MapMapAtomonMD(const FragInfo &frag, const std::vector< mapAtom * > &fragment_mapatoms, const std::vector< const Atom * > &fragment_mdatoms) const
void ParseFragment(Seginfo &seginfo, const tools::Property &frag)
std::pair< Index, Index > CalcAtomIdRange(const Segment &seg) const
atom_id StringToMapIndex(const std::string &map_string) const
atom_id StringToMDIndex(const std::string &md_string) const
void PlaceMapAtomonMD(const std::vector< mapAtom * > &fragment_mapatoms, const std::vector< const Atom * > &fragment_mdatoms) const
Index FindVectorIndexFromAtomId(Index atomid, const std::vector< mapAtom * > &fragment_mapatoms) const
Eigen::Vector3d CalcWeightedPos(const std::vector< double > &weights, const T &atoms) const
AtomContainer map(const Segment &seg, const SegId &segid) const
typename AtomContainer::Atom_Type mapAtom
std::vector< double > getWeights(const tools::Property &frag) const
const Atom * getAtom(Index id) const
Definition segment.cc:32
#define XTP_LOG(level, log)
Definition logger.h:40
base class for all analysis tools
Definition basebead.h:33
Eigen::Index Index
Definition types.h:26
std::vector< Index > map_local_frame
std::vector< atom_id > mapatom_ids
std::vector< atom_id > mdatom_ids
std::pair< Index, Index > minmax
std::vector< FragInfo > fragments
std::map< std::string, std::string > coordfiles
std::vector< atom_id > mapatoms