52 std::ifstream in(filename);
54 throw std::runtime_error(
"File:" + filename +
" could not be opened");
57 std::vector<std::string> tokens;
62 std::vector<std::string> split = toker.
ToVector();
63 if (split.empty() || split[0].substr(0, 1) ==
"!") {
66 tokens.insert(tokens.end(), split.begin(), split.end());
68 auto fail = [&](
const std::string& what) {
69 throw std::runtime_error(
"Charge-response file " + filename +
": " + what);
76 n = boost::lexical_cast<Index>(tokens[0]);
77 }
catch (
const boost::bad_lexical_cast&) {
78 fail(
"the first entry must be the number of atoms, got '" + tokens[0] +
82 fail(
"number of atoms must be positive");
84 const std::size_t expected = 1 + 4 * std::size_t(n) + std::size_t(n * n);
85 if (tokens.size() != expected) {
86 fail(
"expected " + std::to_string(expected) +
" entries for " +
88 " atoms (N, N lines 'El x y z', N x N matrix), "
90 std::to_string(tokens.size()));
95 for (
Index i = 0; i < n; ++i) {
99 for (
Index c = 0; c < 3; ++c) {
100 (void)boost::lexical_cast<double>(tokens[k + 1 + std::size_t(c)]);
105 for (
Index i = 0; i < n; ++i) {
106 for (
Index j = 0; j < n; ++j) {
107 pi_(i, j) = boost::lexical_cast<double>(tokens[k++]);
110 }
catch (
const boost::bad_lexical_cast&) {
111 fail(
"could not read entry " + std::to_string(k) +
" ('" + tokens[k] +
118 auto fail = [&](
const std::string& what) {
119 throw std::runtime_error(
"Charge-response matrix from " + source +
": " +
123 if (
pi_.cols() != n || n == 0) {
124 fail(
"must be a non-empty square matrix");
127 fail(
"has " + std::to_string(n) +
" rows but " +
128 std::to_string(
elements_.size()) +
" atoms");
130 const double scale =
pi_.cwiseAbs().maxCoeff();
131 if (!(scale > 0.0) || !
pi_.allFinite()) {
132 fail(
"must be finite and not zero");
134 const double asym = (
pi_ -
pi_.transpose()).cwiseAbs().maxCoeff();
135 if (asym > crm_tolerance * scale) {
136 fail(
"is not symmetric (largest |Pi_ij - Pi_ji| " + std::to_string(asym) +
139 const double rowsum =
pi_.rowwise().sum().cwiseAbs().maxCoeff();
140 if (rowsum > crm_tolerance * scale *
double(n)) {
141 fail(
"does not conserve charge (largest row sum " + std::to_string(rowsum) +
146 const Eigen::MatrixXd
P = Eigen::MatrixXd::Identity(n, n) -
147 Eigen::MatrixXd::Constant(n, n, 1.0 /
double(n));
150 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> es(
pi_,
151 Eigen::EigenvaluesOnly);
152 const double lowest = es.eigenvalues()(0);
153 const double highest = es.eigenvalues()(n - 1);
154 if (lowest < -crm_tolerance * highest) {
155 fail(
"is not positive semidefinite (lowest eigenvalue " +
156 std::to_string(lowest) +
", highest " + std::to_string(highest) +
")");
162 if (
Index(order.size()) != n) {
163 throw std::runtime_error(
164 "ChargeResponse::Permuted: the charge response of " +
165 std::to_string(n) +
" atoms needs all of them, got " +
166 std::to_string(order.size()) +
167 " (a subset of a molecule does not conserve charge)");
169 std::vector<bool> seen(std::size_t(n),
false);
170 for (
Index i : order) {
171 if (i < 0 || i >= n || seen[std::size_t(i)]) {
172 throw std::runtime_error(
173 "ChargeResponse::Permuted: the atom order is not a permutation of "
175 std::to_string(n - 1));
177 seen[std::size_t(i)] =
true;
180 result.
pi_.resize(n, n);
181 for (
Index i = 0; i < n; ++i) {
183 for (
Index j = 0; j < n; ++j) {
184 result.
pi_(i, j) =
pi_(order[std::size_t(i)], order[std::size_t(j)]);