71 if (std::any_of(
opt_.dims.begin(),
opt_.dims.end(),
72 [](
Index d) { return d < 1; })) {
73 throw std::runtime_error(
"LatticeModel: dimensions must be positive");
75 if (
opt_.spacing <= 0.0 ||
opt_.cutoff <= 0.0 ||
opt_.decay_length <= 0.0) {
76 throw std::runtime_error(
77 "LatticeModel: spacing, cutoff and decay length must be positive");
79 if (
opt_.reorganization <= 0.0) {
80 throw std::runtime_error(
81 "LatticeModel: the reorganization energy must be positive");
83 box_ = Eigen::Matrix3d::Zero();
84 for (
Index d = 0; d < 3; ++d) {
88 double half_box = 0.5 *
box_.diagonal().minCoeff();
89 if (
opt_.cutoff >= half_box) {
90 throw std::runtime_error(
91 "LatticeModel: the cutoff must be smaller than half the box");
95 for (
Index ix = 0; ix <
opt_.dims[0]; ++ix) {
96 for (
Index iy = 0; iy <
opt_.dims[1]; ++iy) {
97 for (
Index iz = 0; iz <
opt_.dims[2]; ++iz) {
98 positions_.push_back(
opt_.spacing * Eigen::Vector3d(
double(ix) + 0.5,
122 double radius)
const {
125 std::vector<std::pair<double, std::array<Index, 3>>> result;
126 for (
Index ix = 0; ix <
opt_.dims[0]; ++ix) {
127 for (
Index iy = 0; iy <
opt_.dims[1]; ++iy) {
128 for (
Index iz = 0; iz <
opt_.dims[2]; ++iz) {
135 result.push_back({d, {ix, iy, iz}});
146 throw std::runtime_error(
147 "LatticeModel: more neighbours requested for the correlated energies "
149 std::to_string(l) +
") than other sites (" + std::to_string(n - 1) +
153 return std::vector<std::vector<Index>>(n);
159 double tol = 1
e-9 *
opt_.spacing;
160 std::vector<std::pair<double, std::array<Index, 3>>> all =
161 Offsets(std::numeric_limits<double>::max());
162 std::vector<double> distances(all.size());
163 std::transform(all.begin(), all.end(), distances.begin(),
164 [](
const auto& o) { return o.first; });
165 std::nth_element(distances.begin(), distances.begin() + (l - 1),
167 std::vector<std::pair<double, std::array<Index, 3>>> offsets =
168 Offsets(distances[l - 1] + tol);
170 auto less = [tol](
const std::pair<double, Index>& a,
171 const std::pair<double, Index>& b) {
172 if (std::abs(a.first - b.first) > tol) {
173 return a.first < b.first;
175 return a.second < b.second;
177 std::vector<std::vector<Index>> result(n);
178 std::vector<std::pair<double, Index>> dist(offsets.size());
179 for (
Index ix = 0; ix <
opt_.dims[0]; ++ix) {
180 for (
Index iy = 0; iy <
opt_.dims[1]; ++iy) {
181 for (
Index iz = 0; iz <
opt_.dims[2]; ++iz) {
183 for (
Index k = 0; k <
Index(offsets.size()); ++k) {
184 const std::array<Index, 3>& o = offsets[k].second;
189 std::partial_sort(dist.begin(), dist.begin() + l, dist.end(), less);
190 result[i].reserve(l);
191 for (
Index m = 0; m < l; ++m) {
192 result[i].push_back(dist[m].second);
209 std::vector<double> noise(
energies_.size());
210 std::generate(noise.begin(), noise.end(), [&normal]() { return normal(); });
213 [
this](
double e,
double z) { return e + opt_.sigma * z; });
221 double eta_c = 1.0 -
opt_.eta_a -
opt_.eta_b;
222 if (
opt_.eta_a < 0.0 ||
opt_.eta_b < 0.0 || eta_c < -1
e-12) {
223 throw std::runtime_error(
224 "LatticeModel: weights eta_a, eta_b must be non-negative with "
225 "eta_a + eta_b <= 1");
227 eta_c = std::max(eta_c, 0.0);
229 throw std::runtime_error(
230 "LatticeModel: the neighbour numbers l_b and l_c must be positive");
232 std::vector<double> Ma(n), Mb(n), Mc(n);
233 for (
Index i = 0; i < n; ++i) {
234 Ma[i] =
opt_.sigma * normal();
236 for (
Index i = 0; i < n; ++i) {
237 Mb[i] =
opt_.sigma * normal();
239 for (
Index i = 0; i < n; ++i) {
240 Mc[i] =
opt_.sigma * normal();
243 const Index self =
opt_.include_self ? 1 : 0;
244 std::vector<std::vector<Index>> neighbours =
246 double wa = std::sqrt(
opt_.eta_a);
247 double wb = std::sqrt(
opt_.eta_b /
double(
opt_.l_b));
248 double wc = std::sqrt(eta_c /
double(
opt_.l_c));
249 for (
Index i = 0; i < n; ++i) {
250 double sb =
opt_.include_self ? Mb[i] : 0.0;
251 for (
Index m = 0; m <
opt_.l_b - self; ++m) {
252 sb += Mb[neighbours[i][m]];
254 double sc =
opt_.include_self ? Mc[i] : 0.0;
255 for (
Index m = 0; m <
opt_.l_c - self; ++m) {
256 sc += Mc[neighbours[i][m]];
258 energies_[i] += wa * Ma[i] + wb * sb + wc * sc;
264 throw std::runtime_error(
"LatticeModel: topology must be empty");
272 double half_lambda = 0.5 *
opt_.reorganization;
273 for (
Index i = 0; i < n; ++i) {
283 const std::vector<Segment>& segs = top.
Segments();
288 std::vector<std::pair<double, std::array<Index, 3>>> offsets =
290 std::vector<Index> partners;
291 for (
Index i = 0; i < n; ++i) {
296 for (
const auto& o : offsets) {
297 Index j =
SiteIndex(ix + o.second[0], iy + o.second[1], iz + o.second[2]);
299 partners.push_back(j);
302 std::sort(partners.begin(), partners.end());
303 partners.erase(std::unique(partners.begin(), partners.end()),
305 for (
Index j : partners) {
307 double dist = r.norm();
308 if (dist >=
opt_.cutoff) {
311 QMPair& pair = nblist.
Add(segs[i], segs[j], r);
313 opt_.J2 * std::exp(-(dist -
opt_.spacing) /
opt_.decay_length);
315 pair.setJeff2(J2, s);