29template <
class AtomContainer>
34template <
class AtomContainer>
36 const SegId& segid)
const {
38 std::string filename = segid.
FileName();
39 filename = std::regex_replace(filename, std::regex(
"\\$SEGID"),
40 std::to_string(seg.
getId()));
42 std::regex_replace(filename, std::regex(
"\\$SEGNAME"), seg.
getType());
43 return map(seg, filename);
46 return map(seg, state);
50template <
class AtomContainer>
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];
60 return map_pos = map_pos / map_weight;
63template <
class AtomContainer>
67 std::vector<double> 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>>();
75 << frag.
get(
"name").
as<std::string>()
76 <<
" Using atomic masses" << std::flush;
80 std::vector<std::string> atom_strings = tok_atoms.
ToVector();
82 for (
auto a_string : atom_strings) {
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 " +
90 double weight =
e.getMass(entries[1]);
92 weights.push_back(weight);
100template <
class AtomContainer>
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>>();
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"] +
114 "If you want to leave a qmatom out, place a ':' instead");
117 std::vector<double> weights =
getWeights(frag);
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 " +
125 " out, place a '0' instead");
129 std::vector<Index> mapatom_ids;
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];
136 seginfo.
mdatoms.push_back(md_result.first);
138 if (map_string ==
":") {
142 mapatom_ids.push_back(map_result.first);
143 seginfo.
mapatoms.push_back(map_result);
146 <<
"WARNING: mdatom'" << md_result.second <<
"' and "
148 <<
"' do not have same element" << std::flush;
151 mapfragment.
weights.push_back(weight);
155 std::vector<Index> frame =
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>() +
167 for (
Index atomid : frame) {
168 if (std::find(mapatom_ids.begin(), mapatom_ids.end(), atomid) ==
170 throw std::runtime_error(
"Atom " + std::to_string(atomid) +
171 " in local frame cannot be found in " +
177 seginfo.
fragments.push_back(mapfragment);
180template <
class AtomContainer>
185 std::string molkey =
"topology.molecules.molecule";
186 std::vector<tools::Property*> molecules = topology_map.
Select(molkey);
187 std::string segkey =
"segments.segment";
189 std::vector<tools::Property*> segments = mol->Select(segkey);
193 std::string coordfile_key =
mapatom_xml_[
"coords"] +
"_*";
194 std::vector<tools::Property*> files = seg->Select(coordfile_key);
196 seginfo.
coordfiles[file->name()] = file->as<std::string>();
199 seginfo.
crfiles[file->name()] = file->as<std::string>();
201 std::string segname = seg->get(
"name").as<std::string>();
203 std::string fragkey =
"fragments.fragment";
205 seginfo.
map2md = seg->get(
"map2md").as<
bool>();
207 std::vector<tools::Property*> fragments = seg->Select(fragkey);
212 Index map_atom_min_id =
215 return a.first < b.first;
218 if (map_atom_min_id != 0) {
219 throw std::runtime_error(
221 " do not start at zero index. Each segment "
222 "should have its own coordinate file. If you use an old ctp "
223 "mapping file run 'xtp_update_mapfile' on it.");
232template <
class AtomContainer>
234 const std::string& map_string)
const {
236 std::vector<std::string> result = tok.
ToVector();
237 if (result.size() != 2) {
238 throw std::runtime_error(
"Entry " + map_string +
239 " is not properly formatted.");
241 return std::pair<Index, std::string>(std::stoi(result[0]), result[1]);
243template <
class AtomContainer>
245 const std::string& md_string)
const {
247 std::vector<std::string> result = tok.
ToVector();
248 if (result.size() != 3) {
249 throw std::runtime_error(
"Entry " + md_string +
250 " is not properly formatted.");
254 atomid = std::stoi(result[2]);
255 }
catch (std::invalid_argument&) {
256 throw std::runtime_error(
"Atom entry " + md_string +
257 " is not well formatted");
259 return std::pair<Index, std::string>(atomid, result[1]);
262template <
class AtomContainer>
264 const std::vector<Index>& seg)
const {
265 Index max_res_id = *std::max_element(seg.begin(), seg.end());
266 Index min_res_id = *std::min_element(seg.begin(), seg.end());
267 return std::pair<Index, Index>(min_res_id, max_res_id);
269template <
class AtomContainer>
274 return a.getId() < b.getId();
280 return a.getId() < b.getId();
283 return std::pair<Index, Index>(min_res_id, max_res_id);
286template <
class AtomContainer>
288 const std::vector<mapAtom*>& fragment_mapatoms,
289 const std::vector<const Atom*>& fragment_mdatoms)
const {
290 for (
Index i = 0; i <
Index(fragment_mapatoms.size()); i++) {
291 const Atom* a = fragment_mdatoms[i];
292 mapAtom* b = fragment_mapatoms[i];
300template <
class AtomContainer>
302 Index atomid,
const std::vector<mapAtom*>& fragment_mapatoms)
const {
304 for (; i <
Index(fragment_mapatoms.size()); i++) {
305 if (fragment_mapatoms[i]->getId() == atomid) {
311template <
class AtomContainer>
313 const FragInfo& frag,
const std::vector<mapAtom*>& fragment_mapatoms,
314 const std::vector<const Atom*>& fragment_mdatoms)
const {
315 std::vector<Eigen::Vector3d> local_map_frame;
316 std::vector<Eigen::Vector3d> local_md_frame;
319 local_map_frame.push_back(fragment_mapatoms[i]->getPos());
320 local_md_frame.push_back(fragment_mdatoms[i]->getPos());
327 Eigen::Vector3d shift_map2md = md_com - map_com;
329 Eigen::Matrix3d rot_map = Eigen::Matrix3d::Identity();
330 Eigen::Matrix3d rot_md = Eigen::Matrix3d::Identity();
335 Eigen::Vector3d x_map = local_map_frame[0] - local_map_frame[1];
336 Eigen::Vector3d x_md = local_md_frame[0] - local_md_frame[1];
337 Eigen::Vector3d y_map;
338 Eigen::Vector3d y_md;
339 Eigen::Vector3d z_map;
340 Eigen::Vector3d z_md;
342 y_map = local_map_frame[2] - local_map_frame[1];
343 y_md = local_md_frame[2] - local_md_frame[1];
344 z_map = x_map.cross(y_map);
345 z_md = x_md.cross(y_md);
346 y_map = z_map.cross(x_map);
347 y_md = z_md.cross(x_md);
350 Eigen::Vector3d unit = Eigen::Vector3d::UnitX();
351 if (std::abs(unit.dot(x_map) / x_map.norm()) < 1
e-6) {
352 unit = Eigen::Vector3d::UnitY();
354 y_map = (x_map.cross(unit));
355 if (std::abs(unit.dot(x_md) / x_md.norm()) < 1
e-6) {
356 unit = Eigen::Vector3d::UnitX();
358 y_md = (x_md.cross(unit));
359 z_map = x_map.cross(y_map);
360 z_md = x_md.cross(y_md);
363 if (x_map.squaredNorm() < 1
e-18 || y_map.squaredNorm() < 1
e-18 ||
364 z_map.squaredNorm() < 1
e-18) {
365 throw std::runtime_error(
367 " basis vectors are very small, choose different local basis");
369 rot_map.col(0) = x_map.normalized();
370 rot_map.col(1) = y_map.normalized();
371 rot_map.col(2) = z_map.normalized();
373 if (x_md.squaredNorm() < 1
e-18 || y_md.squaredNorm() < 1
e-18 ||
374 z_md.squaredNorm() < 1
e-18) {
375 throw std::runtime_error(
376 "MD basis vectors are very small, choose different local basis");
378 rot_md.col(0) = x_md.normalized();
379 rot_md.col(1) = y_md.normalized();
380 rot_md.col(2) = z_md.normalized();
382 Eigen::Matrix3d rotateMAP2MD = rot_md * rot_map.transpose();
383 for (
Index i = 0; i <
Index(fragment_mapatoms.size()); i++) {
384 mapAtom* atom = fragment_mapatoms[i];
385 if (
getRank(*atom) > 0 && symmetry < 3) {
386 throw std::runtime_error(
387 "Local frame has less than 3 atoms, thus higher rank multipoles "
388 "cannot be mapped.");
390 atom->Translate(shift_map2md);
391 atom->Rotate(rotateMAP2MD, md_com);
396template <
class AtomContainer>
400 throw std::runtime_error(
401 "Could not find a Segment of name: " + seg.
getType() +
" in mapfile.");
404 std::string coordsfiletag =
406 if (seginfo.
coordfiles.count(coordsfiletag) == 0) {
407 throw std::runtime_error(
"Could not find a coordinate file for " +
409 " id:" + std::to_string(seg.
getId()) +
410 " segment/state: " + coordsfiletag);
412 std::string coordsfilename = seginfo.
coordfiles.at(coordsfiletag);
421 const std::string tag =
"chargeresponse_" + state.
Type().
ToString();
422 if (seginfo.crfiles.count(tag) == 0) {
425 const std::string& filename = seginfo.crfiles.at(tag);
431 std::vector<Index> order;
432 for (
const atom_id&
id : seginfo.mapatoms) {
433 if (
id.first >= cr.
size() ||
434 cr.
Elements()[std::size_t(
id.first)] !=
id.second) {
435 throw std::runtime_error(
436 "Segment " + seginfo.segname +
": " +
mapatom_xml_.at(
"atoms") +
437 " entry " + std::to_string(
id.first) +
":" +
id.second +
438 " does not match the charge-response file " + filename +
439 " (same atoms in the same order as the multipoles file needed)");
441 order.push_back(
id.first);
444 throw std::runtime_error(
445 "Segment " + seginfo.segname +
" maps " + std::to_string(order.size()) +
446 " of the " + std::to_string(cr.
size()) +
447 " atoms of its charge-response file " + filename +
448 "; the charge-response model needs the whole molecule");
452 << result.
getId() <<
": charge response from "
453 << filename << std::flush;
456template <
class AtomContainer>
458 const Segment& seg,
const std::string& coordfilename)
const {
461 throw std::runtime_error(
462 "Could not find a Segment of name: " + seg.
getType() +
" in mapfile.");
466 throw std::runtime_error(
468 "' does not contain the same number of atoms as mapping file: " +
469 std::to_string(seginfo.
mdatoms.size()) +
" vs. " +
470 std::to_string(seg.
size()));
473 std::pair<Index, Index> minmax_map = seginfo.
minmax;
476 if ((minmax_map.first - minmax_map.second) !=
477 (minmax.first - minmax.second)) {
478 throw std::runtime_error(
"AtomId range for segment " + seg.
getType() +
":" +
479 std::to_string(seg.
getId()) +
480 " and the mapping do not agree: Segment[" +
481 std::to_string(minmax.first) +
"," +
482 std::to_string(minmax.second) +
"] Map[" +
483 std::to_string(minmax_map.first) +
"," +
484 std::to_string(minmax_map.second) +
"]");
487 Index atomidoffset = minmax.first - minmax_map.first;
490 Result.LoadFromFile(coordfilename);
496 throw std::runtime_error(
498 "' does not contain enough atoms for the mapping file: needs at "
500 std::to_string(seginfo.
mapatoms.size()) +
", has " +
501 std::to_string(Result.
size()));
506 if (
id.first < 0 || id.first >= Result.
size()) {
507 throw std::runtime_error(
509 "' references " +
mapatom_xml_.at(
"atoms") +
" index " +
510 std::to_string(
id.first) +
511 ", which is out of range for the coordinate file '" + coordfilename +
512 "' (" + std::to_string(Result.
size()) +
" atoms).");
518 std::map<Index, mapAtom*> md_id_to_map_atom;
522 id.first += atomidoffset;
525 std::vector<mapAtom*> fragment_mapatoms;
527 if (
id.second != Result[
id.first].getElement()) {
528 throw std::runtime_error(
529 "Element of mapping atom " + std::to_string(
id.first) +
":" +
530 id.second +
" does not agree with Element of parsed Atom " +
531 Result[
id.first].getElement());
533 fragment_mapatoms.push_back(&Result[
id.first]);
535 std::vector<const Atom*> fragment_mdatoms;
538 if (atom ==
nullptr) {
539 throw std::runtime_error(
540 "Could not find an atom with name:" +
id.second +
"id" +
541 std::to_string(
id.first) +
" in segment " + seg.
getType());
543 fragment_mdatoms.push_back(atom);
546 for (
Index i = 0; i <
Index(fragment_mdatoms.size()); i++) {
547 md_id_to_map_atom[fragment_mdatoms[i]->getId()] = fragment_mapatoms[i];
563 if (md_atom ==
nullptr) {
566 auto it = md_id_to_map_atom.find(
id.first);
567 if (it != md_id_to_map_atom.end()) {
582 std::map<Index, Index> old_to_new;
588 mapAtom atom_copy = Result[
id.first];
589 atom_copy.setID(filtered.
size());
const std::string & getType() const
void push_back(const T &atom)
std::vector< T >::iterator end()
std::vector< T >::iterator begin()
static std::string GetElementFromString(const std::string &MDName)
const Eigen::Vector3d & getPos() const
Charge-response matrix Pi of one molecule type.
const std::vector< std::string > & Elements() const
ChargeResponse Permuted(const std::vector< Index > &order) const
void LoadFromFile(const std::string &filename)
Reads a .crm file and checks it (see Check). Positions are in bohr.
void setChargeResponse(const ChargeResponse &cr)
Logger is used for thread-safe output of messages.
std::string ToString() const
Identifier for QMstates. Strings like S1 are converted into enum +zero indexed int.
const QMStateType & Type() const
QMState getQMState() const
std::string FileName() const
std::pair< Index, std::string > atom_id
void LoadMappingFile(const std::string &mapfile)
std::map< std::string, Seginfo > segment_info_
void MapMapAtomonMD(const FragInfo &frag, const std::vector< mapAtom * > &fragment_mapatoms, const std::vector< const Atom * > &fragment_mdatoms) const
std::map< std::string, std::string > mapatom_xml_
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 AttachChargeResponse(AtomContainer &, const Seginfo &, const QMState &) const
Index getRank(const mapAtom &atom) const
void TransferExternalBondDirection(mapAtom *, const Atom *, const Eigen::Matrix3d &) 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
SegmentMapper(Logger &log)
std::string getFrame(const tools::Property &frag) const
void RemapBondedPartners(mapAtom &, const std::map< Index, Index > &) const
AtomContainer map(const Segment &seg, const SegId &segid) const
typename AtomContainer::Atom_Type mapAtom
std::vector< double > getWeights(const tools::Property &frag) const
void TransferBondedPartners(mapAtom *, const Atom *, const std::map< Index, mapAtom * > &) const
const Atom * getAtom(Index id) const
#define XTP_LOG(level, log)
Charge transport classes.
ClassicalSegment< PolarSite > PolarSegment
Provides a means for comparing floating point numbers.
std::vector< Index > map_local_frame
std::vector< atom_id > mapatom_ids
std::vector< double > weights
std::vector< atom_id > mdatom_ids
std::vector< Index > mdatoms
std::pair< Index, Index > minmax
std::vector< FragInfo > fragments
std::map< std::string, std::string > crfiles
std::map< std::string, std::string > coordfiles
std::vector< atom_id > mapatoms