Loading...
Searching...
No Matches
MobileAtoms.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12#include "eon/MobileAtoms.h"
13
14#include <cctype>
15#include <stdexcept>
16#include <string>
17#include <unordered_set>
18#include <vector>
19
20namespace eonc {
21
22bool atomListMeansAll(const std::string &atomList) {
23 if (atomList.empty()) {
24 return true;
25 }
26 // Trim and case-fold "all"
27 size_t b = 0;
28 while (b < atomList.size() &&
29 std::isspace(static_cast<unsigned char>(atomList[b]))) {
30 ++b;
31 }
32 size_t e = atomList.size();
33 while (e > b && std::isspace(static_cast<unsigned char>(atomList[e - 1]))) {
34 --e;
35 }
36 if (e <= b) {
37 return true;
38 }
39 if (e - b != 3) {
40 return false;
41 }
42 auto lower = [](char c) {
43 return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
44 };
45 return lower(atomList[b]) == 'a' && lower(atomList[b + 1]) == 'l' &&
46 lower(atomList[b + 2]) == 'l';
47}
48
49VectorXi freeAtomIndices(const Matter *matter) {
50 if (!matter) {
51 throw std::invalid_argument("freeAtomIndices: null Matter");
52 }
53 const long n = matter->numberOfAtoms();
54 std::vector<int> free;
55 free.reserve(static_cast<size_t>(matter->numberOfFreeAtoms()));
56 for (long i = 0; i < n; ++i) {
57 if (!matter->getFixed(i)) {
58 free.push_back(static_cast<int>(i));
59 }
60 }
61 VectorXi out(static_cast<Eigen::Index>(free.size()));
62 for (Eigen::Index k = 0; k < out.size(); ++k) {
63 out(k) = free[static_cast<size_t>(k)];
64 }
65 return out;
66}
67
68VectorXi resolveMobileAtoms(const Matter *matter, const std::string &atomList) {
69 if (!matter) {
70 throw std::invalid_argument("resolveMobileAtoms: null Matter");
71 }
72 if (atomListMeansAll(atomList)) {
73 return freeAtomIndices(matter);
74 }
75 const long n = matter->numberOfAtoms();
76 std::vector<int> mobile;
77 std::unordered_set<int> seen;
78 std::string token;
79 for (size_t p = 0; p <= atomList.size(); ++p) {
80 const char c = (p < atomList.size()) ? atomList[p] : ',';
81 if (c == ',' || c == ' ' || c == '\t' || p == atomList.size()) {
82 if (!token.empty()) {
83 try {
84 const long idx = std::stol(token);
85 if (idx >= 0 && idx < n && !matter->getFixed(idx) &&
86 seen.insert(static_cast<int>(idx)).second) {
87 mobile.push_back(static_cast<int>(idx));
88 }
89 } catch (const std::exception &) {
90 // skip non-integer tokens
91 }
92 token.clear();
93 }
94 } else {
95 token.push_back(c);
96 }
97 }
98 VectorXi out(static_cast<Eigen::Index>(mobile.size()));
99 for (Eigen::Index k = 0; k < out.size(); ++k) {
100 out(k) = mobile[static_cast<size_t>(k)];
101 }
102 return out;
103}
104
105VectorXi resolveMobileAtoms(const Matter *matter, const VectorXi &candidates) {
106 if (!matter) {
107 throw std::invalid_argument("resolveMobileAtoms: null Matter");
108 }
109 const long n = matter->numberOfAtoms();
110 std::vector<int> mobile;
111 std::unordered_set<int> seen;
112 mobile.reserve(static_cast<size_t>(candidates.size()));
113 for (Eigen::Index k = 0; k < candidates.size(); ++k) {
114 const long idx = candidates(k);
115 if (idx >= 0 && idx < n && !matter->getFixed(idx) &&
116 seen.insert(static_cast<int>(idx)).second) {
117 mobile.push_back(static_cast<int>(idx));
118 }
119 }
120 VectorXi out(static_cast<Eigen::Index>(mobile.size()));
121 for (Eigen::Index k = 0; k < out.size(); ++k) {
122 out(k) = mobile[static_cast<size_t>(k)];
123 }
124 return out;
125}
126
127VectorXd packMobileRows(const AtomMatrix &full, const VectorXi &mobile) {
128 VectorXd packed(3 * mobile.size());
129 for (Eigen::Index a = 0; a < mobile.size(); ++a) {
130 packed.segment<3>(3 * a) = full.row(mobile(a));
131 }
132 return packed;
133}
134
135void unpackMobileRows(const VectorXd &packed, const VectorXi &mobile,
136 AtomMatrix &full) {
137 for (Eigen::Index a = 0; a < mobile.size(); ++a) {
138 full.row(mobile(a)) = packed.segment<3>(3 * a);
139 }
140}
141
142VectorXd mobileForces(Matter *matter, const VectorXi &mobile) {
143 const AtomMatrix forces = matter->getForces();
144 return packMobileRows(forces, mobile);
145}
146
147} // namespace eonc
Eigen::Matrix< double, Eigen::Dynamic, 3, eOnStorageOrder > AtomMatrix
Definition Eigen.h:37
long int numberOfAtoms() const
Definition Matter.cpp:273
long int numberOfFreeAtoms() const
Definition Matter.cpp:583
const AtomMatrix & getForces() const
Definition Matter.cpp:412
int getFixed(long int atom) const
1 if every Cartesian axis of the atom is fixed, else 0.
Definition Matter.cpp:505
RAII resource manager for the ARTn C library with global synchronization.
bool atomListMeansAll(const std::string &atomList)
Whether atomList means "every free atom" (empty, "all", case-insensitive).
VectorXi resolveMobileAtoms(const Matter *matter, const std::string &atomList)
PHVA-class mobile set for FD Hessian and matrix-free Krylov (Lanczos / Davidson).
void unpackMobileRows(const VectorXd &packed, const VectorXi &mobile, AtomMatrix &full)
Write a 3*n_mobile vector into full AtomMatrix rows (other rows unchanged).
VectorXi freeAtomIndices(const Matter *matter)
Free (unfixed) atom indices in ascending order.
VectorXd packMobileRows(const AtomMatrix &full, const VectorXi &mobile)
Pack full (n_atoms,3) rows of mobile atoms into a 3*n_mobile vector.
VectorXd mobileForces(Matter *matter, const VectorXi &mobile)
Force components on mobile atoms after Matter has a valid force cache (calls getForces under the hood...