MADNESS 0.10.1
displacements.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30
31 $Id$
32*/
33#ifndef MADNESS_MRA_DISPLACEMENTS_H__INCLUDED
34#define MADNESS_MRA_DISPLACEMENTS_H__INCLUDED
35
36#include <madness/mra/indexit.h>
39
40#include <algorithm>
41#include <array>
42#include <functional>
43#include <iterator>
44#include <limits>
45#include <optional>
46#include <tuple>
47#include <utility>
48#include <vector>
49
50namespace madness {
51
52 // How should we treat destinations "extra" to the [0, 2^n) standard domain?
53 enum class ExtraDomainPolicy {
54 Discard, // Use case: most computations.
55 Keep, // Use case: PBC w/o lattice sums. Destinations that arise from a source inside the domain and some displacement but are outside [0, 2^n)
56 // are equivalent to a destination inside [0, 2^n) with the same displacement but a source outside the [0, 2^n).
57 // That source needs explicit accounting. Keep it. The caller will correct the destination and (if needed) the source.
58 // We're only responsible for the displacement.
59 Translate // Use case: PBC w/ lattice sums. As above, *except* the source outside [0, 2^n) is accounted for by some source inside [0, 2^n).
60 // The displacement itself needs changing, so that both source and destination are in the standard domain.
61 // We're responsible for changing the displacement.
62 };
63
64 /// Holds displacements for applying operators to avoid replicating for all operators
65 template <std::size_t NDIM>
67
68 inline static std::vector< Key<NDIM> > disp = {}; ///< standard displacements to be used with standard kernels (range-unrestricted, no lattice sum)
69 inline static array_of_bools<NDIM> periodic_axes{false}; ///< along which axes lattice summation is performed?
70 inline static std::array<std::vector< Key<NDIM>>, 64 > disp_periodic{}; ///< displacements to be used with lattice-summed kernels
71 inline static Tensor<double> widths{NDIM}; ///< cell width, used to order displacements from least to most real space distance
72
73 public:
74 static int bmax_default() {
75 // Numbers determined by trial and error. The entire idea of bmax is non-adaptive,
76 // and the decision to have bmax be isotropic is only valid for hypercubes.
77 int bmax;
78 if (NDIM == 1) bmax = 7;
79 else if (NDIM == 2) bmax = 5;
80 else if (NDIM == 3) bmax = 4;
81 else if (NDIM == 4) bmax = 3;
82 else if (NDIM == 5) bmax = 3;
83 else if (NDIM == 6) bmax = 3;
84 else bmax = 2;
85 return bmax;
86 }
87
88 private:
89 static bool cmp_keys(const Key<NDIM>& a, const Key<NDIM>& b) {
90 const auto a_width = a.real_distsq(widths);
91 const auto b_width = b.real_distsq(widths);
92 if (a_width == 0 and a_width == b_width) return a.distsq() < b.distsq();
93 else return a_width < b_width;
94 }
95
96 static bool cmp_keys_periodic(const Key<NDIM>& a, const Key<NDIM>& b) {
97 const auto a_width = a.real_distsq_bc(periodic_axes, widths);
98 const auto b_width = b.real_distsq_bc(periodic_axes, widths);
99 if (a_width == 0 and a_width == b_width) return a.distsq_bc(periodic_axes) < b.distsq_bc(periodic_axes);
100 else return a_width < b_width;
101 }
102
103 static void make_disp(int bmax) {
104 // Note newer loop structure in make_disp_periodic_sum
106
107 int num = 1;
108 for (std::size_t i=0; i<NDIM; ++i) num *= (2*bmax + 1);
109 disp.resize(num,Key<NDIM>(0));
110
111 num = 0;
112 if (NDIM == 1) {
113 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
114 disp[num++] = Key<NDIM>(0,d);
115 }
116 else if (NDIM == 2) {
117 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
118 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
119 disp[num++] = Key<NDIM>(0,d);
120 }
121 else if (NDIM == 3) {
122 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
123 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
124 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
125 disp[num++] = Key<NDIM>(0,d);
126 }
127 else if (NDIM == 4) {
128 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
129 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
130 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
131 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
132 disp[num++] = Key<NDIM>(0,d);
133 }
134 else if (NDIM == 5) {
135 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
136 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
137 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
138 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
139 for (d[4]=-bmax; d[4]<=bmax; ++d[4])
140
141 disp[num++] = Key<NDIM>(0,d);
142 }
143 else if (NDIM == 6) {
144 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
145 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
146 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
147 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
148 for (d[4]=-bmax; d[4]<=bmax; ++d[4])
149 for (d[5]=-bmax; d[5]<=bmax; ++d[5])
150 disp[num++] = Key<NDIM>(0,d);
151 }
152 else {
153 MADNESS_EXCEPTION("make_disp: hard dimension loop",NDIM);
154 }
155
156 std::sort(disp.begin(), disp.end(), cmp_keys);
157 }
158
159 static void make_disp_periodic(int bmax, Level n) {
160 MADNESS_ASSERT(periodic_axes.any()); // else use make_disp
161 Translation twon = Translation(1)<<n;
162
163 if (bmax > (twon-1)) bmax=twon-1;
164
165 // Make permissible 1D translations, periodic and nonperiodic (for mixed BC)
166 std::vector<Translation> bp(4*bmax+1);
167 std::vector<Translation> bnp(2*bmax+1);
168 int ip=0;
169 int inp=0;
170 for (Translation lx=-bmax; lx<=bmax; ++lx) {
171 bp[ip++] = lx;
172 if ((lx < 0) && (lx+twon > bmax)) bp[ip++] = lx + twon;
173 if ((lx > 0) && (lx-twon <-bmax)) bp[ip++] = lx - twon;
174 bnp[inp++] = lx;
175 }
176 MADNESS_ASSERT(ip <= 4*bmax+1);
177 MADNESS_ASSERT(inp <= 2*bmax+1);
178 const int nbp = ip;
179 const int nbnp = inp;
180
181 MADNESS_PRAGMA_CLANG(diagnostic push)
182 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
183
184 disp_periodic[n] = std::vector< Key<NDIM> >();
186 for(size_t i=0; i!=NDIM; ++i) {
187 lim[i] = periodic_axes[i] ? nbp : nbnp;
188 }
189 for (IndexIterator index(lim); index; ++index) {
191 for (std::size_t i=0; i<NDIM; ++i) {
192 d[i] = periodic_axes[i] ? bp[index[i]] : bnp[index[i]];
193 }
194 disp_periodic[n].push_back(Key<NDIM>(n,d));
195 }
196
197 std::sort(disp_periodic[n].begin(), disp_periodic[n].end(), cmp_keys_periodic);
198// print("KEYS AT LEVEL", n);
199// print(disp_periodic[n]);
200
201 MADNESS_PRAGMA_CLANG(diagnostic pop)
202
203 }
204
205
206 public:
207 /// first time this is called displacements are generated.
208 /// if boundary conditions are not periodic, the periodic displacements
209 /// are generated for all axes. This allows to support application of
210 /// operators with boundary conditions periodic along any axis (including all).
211 /// If need to use periodic boundary conditions
212 /// for some axes only, make sure to set the boundary conditions appropriately
213 /// before the first call to this
215 MADNESS_PRAGMA_CLANG(diagnostic push)
216 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
217
218 if (widths.normf() < 1e-8) widths = 1;
219
220 if (disp.empty()) {
222 }
223
224 if constexpr (NDIM <= 3) {
225 if (disp_periodic[0].empty()) { // if not initialized yet
226 if (FunctionDefaults<NDIM>::get_bc().is_periodic().any())
228 FunctionDefaults<NDIM>::get_bc().is_periodic());
229 else
231 }
232 }
233
234 MADNESS_PRAGMA_CLANG(diagnostic pop)
235 }
236
237 const std::vector< Key<NDIM> >& get_disp(Level n,
238 const array_of_bools<NDIM>& kernel_lattice_sum_axes) {
239 MADNESS_PRAGMA_CLANG(diagnostic push)
240 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
241
242 if (kernel_lattice_sum_axes.any()) {
243 MADNESS_ASSERT(NDIM <= 3);
244 MADNESS_ASSERT(n < disp_periodic.size());
245 if ((kernel_lattice_sum_axes && periodic_axes) != kernel_lattice_sum_axes) {
246 std::string msg =
247 "Displacements<" + std::to_string(NDIM) +
248 ">::get_disp(level, kernel_lattice_sum_axes): kernel_lattice_sum_axes is set for some axes that were not periodic in the FunctionDefault's boundary conditions active at the time when Displacements were initialized; invoke Displacements<NDIM>::reset_periodic_axes(kernel_lattice_sum_axes) to rebuild the periodic displacements";
249 MADNESS_EXCEPTION(msg.c_str(), 1);
250 }
251 return disp_periodic[n];
252 }
253 else {
254 return disp;
255 }
256
257 MADNESS_PRAGMA_CLANG(diagnostic pop)
258 }
259
260 /// return the standard displacements appropriate for operators w/o lattice summation
261 const std::vector< Key<NDIM> >& get_disp() {
262 MADNESS_PRAGMA_CLANG(diagnostic push)
263 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
264
265 return disp;
266
267 MADNESS_PRAGMA_CLANG(diagnostic pop)
268 }
269
270 /// rebuilds periodic displacements so that they are optimal for the given set of periodic axes
271
272 /// this must be done while no references to prior periodic displacements are outstanding (i.e. no operator application
273 /// tasks in flight)
274 /// \param new_periodic_axes the new periodic axes
275 static void reset_periodic_axes(const array_of_bools<NDIM>& new_periodic_axes) {
276 MADNESS_PRAGMA_CLANG(diagnostic push)
277 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
278
279 MADNESS_ASSERT(new_periodic_axes.any()); // else why call this?
280 if (new_periodic_axes != periodic_axes) {
281
282 periodic_axes = new_periodic_axes;
283 Level nmax = 8 * sizeof(Translation) - 2;
284 for (Level n = 0; n < nmax; ++n)
286 }
287 MADNESS_PRAGMA_CLANG(diagnostic pop)
288 }
289
290 static void set_width(const Tensor<double>& width) {
291 widths = width;
292 if (!disp.empty()) {
293 std::sort(disp.begin(), disp.end(), cmp_keys);
294 }
295 for (size_t n = 0; n < 64; ++n) {
296 if (!disp_periodic[n].empty()) {
297 std::sort(disp_periodic[n].begin(), disp_periodic[n].end(), cmp_keys_periodic);
298 }
299 }
300 }
301 };
302
303 template <std::size_t N, std::size_t M>
304 constexpr std::enable_if_t<N>=M, std::array<std::size_t, N-M>> iota_array(std::array<std::size_t, M> values_to_skip_sorted) {
305 std::array<std::size_t, N - M> result;
306 if constexpr (N != M) {
307 std::size_t nadded = 0;
308 auto value_to_skip_it = values_to_skip_sorted.begin();
309 assert(*value_to_skip_it < N);
310 auto value_to_skip = *value_to_skip_it;
311 for (std::size_t i = 0; i < N; ++i) {
312 if (i < value_to_skip) {
313 result[nadded++] = i;
314 } else if (value_to_skip_it != values_to_skip_sorted.end()) {
315 ++value_to_skip_it;
316 if (value_to_skip_it != values_to_skip_sorted.end()) {
317 value_to_skip = *value_to_skip_it;
318 } else
319 value_to_skip = N;
320 }
321 }
322 }
323 return result;
324 }
325
326 /**
327 * Generates points at the finite-thickness surface of an N-dimensional box [C1-L1,C1+L1]x...x[CN-LN,CN+LN] centered at point {C1,...CN} in Z^N.
328 * For finite thickness T={T1,...,TN} point {x1,...,xN} is at the surface face perpendicular to axis i xi>=Ci-Li-Ti and xi<=Ci-Li+Ti OR xi>=Ci+Li-Ti and xi<=Ci+Li+Ti.
329 * For dimensions with unlimited size the point coordinates are limited to [0,2^n], with n being the level of the box.
330 * N.B. "points" are really boxes in the standard MADNESS sense, which we'll call "primitive boxes" to disambiguate from box as the product of intervals mentioned above,
331 */
332 template<std::size_t NDIM>
334 public:
338 /// this callable returns whether a given primitive box (or hyperface if only one coordinate is provided) can be filtered out.
339 /// if screening a primitive box, the corresponding displacement should be provided both for further screening and for the displacement to be updated, if displacements are translated to connect two cells in the box.
340 /// the validator should normally be a BoxSurfaceDisplacementFilter object. anything else is probably a hack.
341 using Validator = std::function<bool(Level, const PointPattern&, std::optional<Displacement>&)>;
342
343 private:
344 using BoxRadius = std::array<std::optional<Translation>, NDIM>; // null radius = unlimited size
345 using SurfaceThickness = std::array<std::optional<Translation>, NDIM>; // null thickness for dimensions with null radius
346 using Box = std::array<std::pair<Translation, Translation>, NDIM>;
347 using Hollowness = std::array<bool, NDIM>; // this can be uninitialized, unlike array_of_bools ... hollow = gap between -radius+thickness and +radius-thickness.
349
350 Point center_; ///< Center point of the box
351 BoxRadius box_radius_; ///< halved size of the box in each dimension, in half-SimulationCells.
353 surface_thickness_; ///< surface thickness in each dimension, measured in boxes. Real-space surface size is thus n-dependent.
354 Box box_; ///< box bounds in each dimension.
355 Hollowness hollowness_; ///< does box contain non-surface points along each dimension?
356 Periodicity is_lattice_summed_; ///< which dimensions are lattice summed?
357 Validator validator_; ///< optional validator function
358 std::optional<Translation> probe_offset_radius_; ///< least N such that being N boxes away from origin guarantees
359 /// the point was not a short-range point already considered
360 Displacement probing_displacement_; ///< displacement to a nearby point on the surface; it may not be able to pass the filter, but is sufficiently representative of the surface displacements to allow screening with isotropic kernels
361
362 /**
363 * @brief Iterator class for lazy generation of surface points
364 *
365 * This iterator generates surface points on-demand by tracking the current fixed
366 * dimension and positions in each dimension. It implements the InputIterator concept.
367 */
368 class Iterator {
369 public:
370 enum Type {Begin, End};
371 private:
372 const BoxSurfaceDisplacementRange* parent; ///< Pointer to parent surface.
373 Point point; ///< Current point / box. This is always free to leave the simulation cell.
374 mutable std::optional<Displacement> disp; ///< Memoized displacement from parent->center_ to point, computed by displacement(), reset by advance()
375 size_t fixed_dim; ///< Current fixed dimension (i.e. faces perpendicular to this axis are being iterated over)
376 Box unprocessed_bounds; ///< The bounds for all *unprocessed* displacements in the finite-thickness surface. Updated as displacements are processed.
377 /// For the dimensions of the parent box, without thickness or regard for displacement processing, use parent->box_.
378 /// Tracking `unprocessed_bounds` allows us to avoid double-counting 'edge' boxes that are on multiple hyperfaces.
379 /// e.g., if radius is [5, 5], center is [0, 0] and thickness is [1, 1], the bounds are [-6, 6] x [-6, 6].
380 /// We first evaluate the hyperfaces [-6, -4] x [-5, 5] and then [4, 6] x [-5, 5].
381 /// It remains to evaluate hyperfaces [-5, 5] x [-6, -4] and [-5, 5] x [4, 6], *excluding*
382 /// the edge points shared with the processed hyperfaces. So, we need to evaluate effective hyperfaces
383 /// [-3, 3] x [-6, -4] and [-3, 3] x [4, 6]. The unprocessed_bounds are reset to [-3, 3] x [-6, 6].
384 bool done; ///< Flag indicating iteration completion
385
386 // return true if we have another surface layer for the fixed_dim
387 // if we do, translate point onto that next surface layer
389 Vector<Translation, NDIM> l = point.translation();
390 if (l[fixed_dim] !=
391 parent->box_[fixed_dim].second +
392 parent->surface_thickness_[fixed_dim].value_or(0)) {
393 // if exhausted all layers on the "negative" side of the fixed dimension and there's a gap to the "positive" side,
394 // jump to the positive side. otherwise, just take the next layer.
396 l[fixed_dim] ==
397 parent->box_[fixed_dim].first +
398 parent->surface_thickness_[fixed_dim].value_or(0)) {
399 l[fixed_dim] =
400 parent->box_[fixed_dim].second -
401 parent->surface_thickness_[fixed_dim].value_or(0);
402 } else
403 ++l[fixed_dim];
404 point = Point(point.level(), l);
405 disp.reset();
406 return true;
407 } else
408 return false;
409 };
410
411 /**
412 * @brief Advances the iterator to the next surface point
413 *
414 * This function implements the logic for traversing the box surface by:
415 * (1) Incrementing displacement in non-fixed dimensions
416 * (2) Switching sides in the fixed dimension when needed
417 * (3) Moving to the next fixed dimension when current one is exhausted
418 *
419 * We filter out layers in (2) but not points within a layer in (1).
420 */
421 void advance() {
422 disp.reset();
423
424 auto increment_along_dim = [this](size_t dim) {
426 Vector<Translation, NDIM> unit_displacement(0); unit_displacement[dim] = 1;
427 point = point.neighbor(unit_displacement);
428 };
429
430 // (1) try all displacements on current NDIM-1 dim layer
431 // loop structure is equivalent to NDIM-1 nested, independent for loops
432 // over the NDIM-1 dimension of the layer, with last dim as innermost loop
433 for (size_t i = NDIM; i > 0; --i) {
434 const size_t cur_dim = i - 1;
435 if (cur_dim == fixed_dim) continue;
436
437 if (point[cur_dim] < unprocessed_bounds[cur_dim].second) {
438 increment_along_dim(cur_dim);
439 return;
440 }
441 reset_along_dim(cur_dim);
442 }
443
444 // (2) move to the next surface layer normal to the fixed dimension
445 // if we can filter out the entire layer, do so.
446 while (next_surface_layer()) {
447 const auto filtered_out = [&,this]() {
448 bool result = false;
449 const auto& validator = this->parent->validator_;
450 if (validator) {
451 PointPattern point_pattern;
452 point_pattern[fixed_dim] = point[fixed_dim];
453 std::optional<Displacement> nulldisp;
454 result = !validator(point.level(), point_pattern, nulldisp);
455 }
456 return result;
457 };
458
459 if (!filtered_out())
460 return;
461 }
462
463 // we finished this fixed dimension, so update unprocessed bounds to exclude the layers of the current fixed dimension
464 // if box along this dimension is not hollow, the new interval would be [0, 0] - we are done!
467 parent->box_[fixed_dim].first +
468 parent->surface_thickness_[fixed_dim].value_or(0) + 1,
469 parent->box_[fixed_dim].second -
470 parent->surface_thickness_[fixed_dim].value_or(0) - 1};
471 }
472 else {
473 done = true;
474 return;
475 }
476 // (3) switch to next fixed dimension with finite radius
477 ++fixed_dim;
478 while (!parent->box_radius_[fixed_dim] && fixed_dim < NDIM) {
479 ++fixed_dim;
480 }
481
482 // Exit if we've displaced along all dimensions of finite radius
483 if (fixed_dim >= NDIM) {
484 done = true;
485 return;
486 }
487
488 // reset our search along all non-fixed dimensions
489 // the reset along the fixed_dim returns silently
490 for (size_t i = 0; i < NDIM; ++i) {
492 }
493 }
494
495 /// Perform advance, repeating if you are at a filtered point
497 if (parent->validator_) {
498 const auto filtered_out = [&]() -> bool {
499 this->displacement(); // ensure disp is up to date
500 return !parent->validator_(point.level(), point.translation(), disp);
501 };
502
503 // if displacement has value, filter has already been applied to it, just advance it
504 if (!done && disp) this->advance();
505
506 while (!done && filtered_out()) {
507 this->advance();
508 }
509 }
510 else
511 this->advance();
512 }
513
514 // Recall that the surface is a union of hyperfaces, i.e., direct products of intervals.
515 // Reset state on dimension `dim` to initialize for the start of interval `dim` in the the current direct product
516 void reset_along_dim(size_t dim) {
517 const auto is_fixed_dim = dim == fixed_dim;
518 Vector<Translation, NDIM> l = point.translation();
519 Translation l_dim_min;
520 if (!is_fixed_dim) {
521 // This dimension is contiguous boxes on the hyperface.
522 // Initialize to the start.
523 l_dim_min = unprocessed_bounds[dim].first;
524 } else if (!parent->is_lattice_summed_[dim]) {
525 // This dimension consists of two finite-thickness hyperfaces, not lattice summed.
526 // Initialize to the start of the - hyperface. We trust next_surface_layer()
527 // to move to the + hyperface when ready.
528 l_dim_min = parent->box_[dim].first -
529 parent->surface_thickness_[dim].value_or(0);
530 } else {
531 // This dimension consists of two finite-thickness hyperfaces, lattice summed.
532 // The two hyperfaces are the same interval shifted by parent->surface_radius_[dim]
533 // periods. So by lattice summation, the - hyperface is included. Initialize
534 // to the start of the + hyperface.
535 l_dim_min = parent->box_[dim].second -
536 parent->surface_thickness_[dim].value_or(0);
537 }
538 if (parent->is_lattice_summed_[dim]) {
539 // By lattice summation, boxes that differ by a SimulationCell are equivalent.
540 // Therefore, we need to sum over equivalence classes and not displacements.
541 const auto period = 1 << parent->center_.level();
542 const Translation last_equiv_class = is_fixed_dim ? parent->box_[dim].second +
543 parent->surface_thickness_[dim].value_or(0) : unprocessed_bounds[dim].second;
544 const Translation first_equiv_class = last_equiv_class - period + 1;
545 l_dim_min = std::max(first_equiv_class, l_dim_min);
546 }
547 l[dim] = l_dim_min;
548
549 point = Point(point.level(), l);
550 disp.reset();
551
552 // if the entire surface layer is filtered out, pick the next one
553 if (dim == fixed_dim) {
554
555 const auto filtered_out = [&,this]() {
556 bool result = false;
557 const auto& validator = this->parent->validator_;
558 if (validator) {
559 PointPattern point_pattern;
560 point_pattern[fixed_dim] = point[fixed_dim];
561 std::optional<Displacement> nulldisp;
562 result = !validator(point.level(), point_pattern, nulldisp);
563 }
564 return result;
565 };
566
567 if (filtered_out()) {
568 bool have_another_surface_layer;
569 while ((have_another_surface_layer = next_surface_layer())) {
570 if (!filtered_out())
571 break;
572 }
573 MADNESS_ASSERT(have_another_surface_layer);
574 }
575
576 }
577 };
578
579 /**
580 * @return displacement from the center to the current point
581 */
582 const std::optional<Displacement>& displacement() const {
583 if (!disp) {
585 }
586 return disp;
587 }
588
589 public:
590 // Iterator type definitions for STL compatibility
591 using iterator_category = std::input_iterator_tag;
593 using difference_type = std::ptrdiff_t;
594 using pointer = const Point*;
595 using reference = const Point&;
596
597 /**
598 * @brief Constructs an iterator
599 *
600 * @param p Pointer to the parent BoxSurfaceDisplacementRange
601 * @param type the type of iterator (Begin or End)
602 */
604 : parent(p), point(parent->center_.level()), fixed_dim(type == End ? NDIM : 0), done(type == End) {
605 if (type != End) {
606 // skip to first dimensions with limited range
607 while (!parent->box_radius_[fixed_dim] && fixed_dim < NDIM) {
608 ++fixed_dim;
609 }
610
611 for (size_t d = 0; d != NDIM; ++d) {
612 // min/max displacements along this axis ... N.B. take into account surface thickness!
613 unprocessed_bounds[d] = parent->box_radius_[d] ? std::pair{parent->box_[d].first -
614 parent->surface_thickness_[d].value_or(0),
615 parent->box_[d].second +
616 parent->surface_thickness_[d].value_or(0)} : parent->box_[d];
618 }
620 }
621 }
622
623 /**
624 * @brief Dereferences the iterator
625 * @return A const reference to the current displacement
626 */
627 reference operator*() const { return *displacement(); }
628
629 /**
630 * @brief Arrow operator for member access
631 * @return A const pointer to the current displacement
632 */
633 pointer operator->() const { return &(*(*this)); }
634
635 /**
636 * @brief Pre-increment operator
637 * @return Reference to this iterator after advancement
638 */
641 return *this;
642 }
643
644 /**
645 * @brief Post-increment operator
646 * @return Copy of the iterator before advancement
647 */
649 Iterator tmp = *this;
650 ++(*this);
651 return tmp;
652 }
653
654 /**
655 * @brief Equality comparison operator
656 * @param a First iterator
657 * @param b Second iterator
658 * @return true if iterators are equivalent
659 */
660 friend bool operator==(const Iterator& a, const Iterator& b) {
661 if (a.done && b.done) return true;
662 if (a.done || b.done) return false;
663 return a.fixed_dim == b.fixed_dim &&
664 a.point == b.point;
665 }
666
667 /**
668 * @brief Inequality comparison operator
669 * @param a First iterator
670 * @param b Second iterator
671 * @return true if iterators are not equivalent
672 */
673 friend bool operator!=(const Iterator& a, const Iterator& b) {
674 return !(a == b);
675 }
676 };
677
678 friend class Iterator;
679
680 public:
681 /**
682 * @brief Constructs a box with different radii and thicknesses for each dimension
683 *
684 * @param center Center primitive box of the box. All displacements will share the `n` of this arg.
685 * @param box_radius Box radius in each dimension, in half-SimulationCells. Omit for dim `i` to signal that the bound for dim `i` is simply the simulation cell.
686 * @param surface_thickness Surface thickness in each dimension, measured in number of addl. boxes *on each half* of the surface box proper. Omit for dim `i` if and only if omitted in `box_radius`
687 * @param is_lattice_summed whether each dimension is lattice summed; along lattice summed dimensions only one side of the box is iterated over.
688 * @param validator Optional filter function (if returns false, displacement is dropped; default: no filter); it may update the displacement to make it valid as needed (e.g. map displacement to the simulation cell)
689 * @pre `surface_radius[d]>0 && surface_thickness[d]<=surface_radius[d]`
690 * @param probe_offset_radius the smallest displacement magnitude, in boxes, that `validator` does *not* discard as a duplicate of the standard/short-range displacement list, Pass 0 to signal that nothing is filtered out (the surface then reaches all the way in to `center` and no probe can screen it). Omit if unknown, in which case the half-simulation-cell offset is used.
691 *
692 */
694 const std::array<std::optional<std::int64_t>, NDIM>& box_radius,
695 const std::array<std::optional<std::int64_t>, NDIM>& surface_thickness,
697 Validator validator = {},
698 std::optional<Translation> probe_offset_radius = {})
701 probe_offset_radius_(probe_offset_radius) {
702 // initialize bounds
703 bool has_finite_dimensions = false;
704 const auto n = center_.level();
705 for (size_t d=0; d!= NDIM; ++d) {
706 if (box_radius_[d]) {
707 auto r = *box_radius_[d]; // in units of 2^{n-1}
708 // n = 0 is special b/c << -1 is undefined
709 r = (n == 0) ? (r+1)/2 : (r * Translation(1) << (n-1));
710 MADNESS_ASSERT(r > 0);
711 box_[d] = {center_[d] - r, center_[d] + r};
712 has_finite_dimensions = true;
713 } else {
714 box_[d] = {0, (1 << center_.level()) - 1};
715 }
716 }
717 MADNESS_ASSERT(has_finite_dimensions);
719 for (size_t d=0; d!= NDIM; ++d) {
720 // surface thickness should be only given for finite-radius dimensions
721 MADNESS_ASSERT(!(box_radius_[d].has_value() ^ surface_thickness_[d].has_value()));
722 MADNESS_ASSERT(surface_thickness_[d].value_or(0) >= 0);
723 hollowness_[d] = surface_thickness_[d] ? (box_[d].first + surface_thickness_[d].value() < box_[d].second - surface_thickness_[d].value()) : false;
724 }
725 }
726
727 /**
728 * @brief Returns an iterator to the beginning of the surface points
729 * @return Iterator pointing to the first surface point
730 */
731 auto begin() const { return Iterator(this, Iterator::Begin); }
732
733 /**
734 * @brief Returns an iterator to the end of the surface points
735 * @return Iterator indicating the end of iteration
736 */
737 auto end() const { return Iterator(this, Iterator::End); }
738
739 // /**
740 // * @brief Returns a view over the surface points
741 // *
742 // * This operator allows the class to be used with C++20 ranges.
743 // *
744 // * @return A view over the surface points
745 // */
746 // auto operator()() const {
747 // return std::ranges::subrange(begin(), end());
748 // }
749
750 /* @return the center of the box
751 */
752 const Key<NDIM>& center() const { return center_; }
753
754 /**
755 * @return the radius of the box in each dimension
756 */
757 const std::array<std::optional<int64_t>, NDIM>& box_radius() const { return box_radius_; }
758
759 /**
760 * @return the surface thickness in each dimension
761 */
762 const std::array<std::optional<int64_t>, NDIM>& surface_thickness() const { return surface_thickness_; }
763
764 /**
765 * @return flags indicating whether each dimension is lattice summed
766 */
768
769 /**
770 * @return 'probing" displacement to a nearby point *on* the surface; it may not necessarily be in the range of iteration (e.g., it may not be able to pass the filter) but is representative of the surface displacements for the purposes of screening
771 */
774 }
775
776 private:
778 // Large boxes we must consider are both those near the center (because 1/r is large
779 // for small r), and near the box radius (because going from 1/r to 0 is a sharp change).
780 // The probe displacement is a way to screen out cases where the box radius is negligible.
781 // Our probe displacement must satisfy:
782 // (1) It must actually be on the box, e.g., on a boundary face, perpendicular to a
783 // dimension with finite box_radius_. We call those the target face and dimensions.
784 // To ensure we're probing the box radius effect and not the near-center effect, we require:
785 // (2) If at all possible, it must be distinct from the zero displacement and
786 // from the displacements "near" the center, both of which should have already been considered.
787 // Zero displacements are especially pernicious, because self-interaction is always large.
788 // n.b.: Beware that for lattice summed-dimensions, displacements must be distinct in the space
789 // of equivalence classes. For lattice-summed dimensions of an even number of boxes, the origin
790 // of the target face is equivalent the origin.
791 // n.b.: If N even and lattice-summed and 1D, the entire boundary is already equivalent to
792 // the displacements "near" the center.
793 // To keep the estimate sharp, we prefer:
794 // (3) We want the displacement of minimal real-space r within the above constraints.
795 // Such displacements are more suitable as a heuristic upper bound of the matrix element
796 // controlling the 1/r to 0 change. Not explicitly accounting for this does not seem to
797 // affect whether we're within epsilon, but it's still good practice.
798 // For the same reason, the sigma should matter as well.
799
800 const auto face_origin_is_center = [this](size_t d) {
801 return is_lattice_summed_[d] && (*box_radius_[d] % 2 == 0);
802 };
803
804 // Create a sort key on candidates for the target dimension.
805 // 1. The "effective number of half cells away" the face is. Even cells are treated as 1,
806 // even though they're actually 0, to account for the offset we'll need to add.
807 // 2. Is an offset needed? Avoiding it is preferred.
808 // 3. Choose the smallest N possible.
809 // Requirement (3) would be better formulated in real-space, but the expected
810 // bound improvement doesn't justify expanding the argument signature.
811 const auto sort_key = [&](size_t d) {
812 const auto N = *box_radius_[d];
813 return std::make_tuple(is_lattice_summed_[d] ? Translation(1) : N, face_origin_is_center(d), N);
814 };
815
816 // The initial value registers "not set yet".
817 size_t face_dimension = NDIM;
818 for (size_t d=0; d != NDIM; ++d) {
819 if (!box_radius_[d]) continue;
820 if (face_dimension == NDIM || sort_key(d) < sort_key(face_dimension)) face_dimension = d;
821 }
822 MADNESS_ASSERT(face_dimension != NDIM); // guaranteed by has_finite_dimensions in the ctor
823
824 // Enforce requirement (1)
825 Vector<Translation, NDIM> probing_displacement_vec(0);
826 const auto n = center_.level();
827 auto r = *box_radius_[face_dimension]; // in units of 2^{n-1}
828 // n = 0 is special b/c << -1 is undefined
829 r = (n == 0) ? (r+1)/2 : (r * Translation(1) << (n-1));
830 MADNESS_ASSERT(r > 0);
831 probing_displacement_vec[face_dimension] = r;
832
833 // In these cases, requirement (2) is already satisfied or unsatisfiable.
834 // Choosing 0 for all other dimensions satisfies requirement (3).
835 if (!face_origin_is_center(face_dimension) || n == 0 || NDIM == 1)
836 return Displacement(n, probing_displacement_vec);
837
838 // No (or negative) radius means none of the surface points have been processed
839 //
841 return Displacement(n, probing_displacement_vec);
842
843 // Else, we still need to satisfy requirement (2) while trying to obey (3). We need to displace along
844 // a different dimension.
845
846 // choose the dimension to displace along. The offset magnitude is dimension-independent;
847 // prefer the narrowest finite dimension, else the first unrestricted one.
848 size_t offset_dimension = NDIM;
849 size_t unrestricted_dimension = NDIM;
850 for (size_t d=0; d != NDIM; ++d) {
851 if (d == face_dimension) continue;
852 if (box_radius_[d]) {
853 if (offset_dimension == NDIM || *box_radius_[d] < *box_radius_[offset_dimension])
854 offset_dimension = d;
855 } else if (unrestricted_dimension == NDIM) {
856 unrestricted_dimension = d;
857 }
858 }
859
860 // Cap the offset at half a cell. If our dimension is lattice-summed, it's even, and half a cell
861 // is where it's furthest from the origin. Else, half a cell is the furthest away we can
862 // guarantee we can displace to, in the case of an open dimension and the center_ is the origin.
863 const Translation half_cell = Translation(1) << (n-1);
865 ? std::clamp(*probe_offset_radius_, Translation(1), half_cell)
866 : half_cell;
867 if (offset_dimension != NDIM) {
868 // the offset stays on the face: box_radius_ >= 1 means the box spans at least a half
869 // simulation cell along this dimension, and offset <= half_cell
870 probing_displacement_vec[offset_dimension] = offset;
871 } else {
872 // we're bounded by the simulation cell; displace toward whichever side of center_ has more room
873 MADNESS_ASSERT(unrestricted_dimension != NDIM); // NDIM > 1, so some dimension was found
874 const auto d = unrestricted_dimension;
875 const auto left_distance = center_[d] - box_[d].first;
876 const auto right_distance = box_[d].second - center_[d];
877 const auto sign = right_distance >= left_distance ? +1 : -1;
878 probing_displacement_vec[d] = sign * offset;
879 }
880 return Displacement(n, probing_displacement_vec);
881 } // compute_probing_displacement
882 }; // BoxSurfaceDisplacementRange
883
884
885 /// This is used to filter out box surface displacements that
886 /// - take us outside of the target domain, or
887 /// - were already utilized as part of the the standard displacements list.
888 /// For dealing with the lattice-summed operators the filter
889 /// can adjusts the displacement to make sure that we end up in
890 /// the simulation cell.
891 template <size_t NDIM>
893 public:
898 using DistanceSquaredFunc = std::function<double(const Displacement&)>;
899
900 /// \param is_infinite_domain whether the domain along each axis is finite (simulation cell) or infinite (the entire axis); if true for a given axis then any destination coordinate is valid, else only values in [0,2^n) are valid
901 /// \param is_lattice_summed if true for a given axis, displacement to x and x+2^n are equivalent, hence will be canonicalized to end up in the simulation cell. Periodic axes imply infinite domain, whatever was passed to `is_infinite_domain`.
902 /// \param range the kernel range for each axis
903 /// \param default_distance_squared function that converts a displacement to its effective distance squared (effective may be different from the real distance squared due to periodicity)
904 /// \param max_distsq_reached max effective distance squared reached by standard displacements
906 const array_of_bools<NDIM>& is_infinite_domain,
907 const array_of_bools<NDIM>& is_lattice_summed,
908 const std::array<KernelRange, NDIM>& range,
909 DistanceSquaredFunc default_distance_squared,
910 double max_distsq_reached
911 ) :
912 range_(range),
913 default_distance_squared_(default_distance_squared),
914 max_distsq_reached_(max_distsq_reached) {
915 for (size_t i = 0; i < NDIM; i++) {
916 if (is_lattice_summed[i]) {
918 } else if (is_infinite_domain[i]) {
920 } else {
922 }
923 }
924 }
925
926 /// Apply filter to a displacement ending up at a point or a group of points (point pattern)
927
928 /// @param level the tree level
929 /// @param dest the target point (when all elements are nonnull) or point pattern (when only some are).
930 /// The latter is useful to skip the entire surface layer. The
931 /// point coordinates are only used to determine whether we end up
932 /// in or out of the domain.
933 /// @param displacement the optional displacement; if given then will check if it's among
934 /// the standard displacement and whether it was used as part of
935 /// the standard displacement set; if it has not been used and the
936 /// operator is lattice summed, the displacement will be adjusted
937 /// to end up in the simulation cell. Primary use case for omitting `displacement`
938 /// is if `dest` is not equivalent to a point.
939 /// @return true if the displacement is to be used
941 const Level level,
942 const PointPattern& dest,
943 std::optional<Displacement>& displacement
944 ) const {
945 // preliminaries
946 const auto twon = (static_cast<Translation>(1) << level); // number of boxes along an axis
947 // map_to_range_twon(x) returns for x >= 0 ? x % 2^level : map_to_range_twon(x+2^level)
948 // idiv is generally slow, so instead use bit logic that relies on 2's complement representation of integers
949 const auto map_to_range_twon = [&, mask = ((~(static_cast<std::uint64_t>(0)) << (64-level)) >> (64-level))](std::int64_t x) -> std::int64_t {
950 const std::int64_t x_mapped = x & mask;
951 MADNESS_ASSERT(x_mapped >=0 && x_mapped < twon && (std::abs(x_mapped-x)%twon==0));
952 return x_mapped;
953 };
954
955 const auto out_of_domain = [&](const Translation& t) -> bool {
956 return t < 0 || t >= twon;
957 };
958
959 // check that dest is in the domain
960 const bool dest_is_in_domain = [&]() {
961 for(size_t d=0; d!=NDIM; ++d) {
962 if (domain_policies_[d] == ExtraDomainPolicy::Discard && dest[d].has_value() && out_of_domain(*dest[d])) return false;
963 }
964 return true;
965 }();
966
967 if (dest_is_in_domain) {
968 if (displacement.has_value()) {
969
970 // N.B. avoid duplicates of standard displacements previously included:
971 // A displacement has been possibly considered if along EVERY axis the "effective" displacement size
972 // fits within the box explored by the standard displacement.
973 // If so, skip if <= max magnitude of standard displacements encountered
974 // Otherwise this is a new non-standard displacement, consider it
975 bool among_standard_displacements = true;
976 for(size_t d=0; d!=NDIM; ++d) {
977 const auto disp_d = (*displacement)[d];
978 auto bmax_standard = Displacements<NDIM>::bmax_default();
979
980 // the effective displacement length depends on whether lattice summation is performed along it
981 // compare Displacements::make_disp vs Displacements::make_disp_periodic
982 auto disp_d_eff_abs = std::abs(disp_d);
984 // for "periodic" displacements the effective disp_d is the shortest of {..., disp_d-twon, disp_d, disp_d+twon, ...} ... see make_disp_periodic
985 const std::int64_t disp_d_eff = map_to_range_twon(disp_d);
986 disp_d_eff_abs = std::min(disp_d_eff,std::abs(disp_d_eff-twon));
987
988 // IMPORTANT for lattice-summed axes, if the destination is out of the simulation cell map the displacement back to the cell
989 // same logic as for disp_d: dest[d] -> dest[d] % twon
990 if (dest[d].has_value()) {
991 const Translation dest_d = dest[d].value();
992 const auto dest_d_in_cell = map_to_range_twon(dest_d);
993 MADNESS_ASSERT(!out_of_domain(
994 dest_d_in_cell));
995 // adjust displacement[d] so that it produces dest_d_cell, not dest_d
996 auto t = (*displacement).translation();
997 t[d] += (dest_d_in_cell - dest_d);
998 displacement.emplace(displacement->level(), t);
999 }
1000
1001 // N.B. bmax in make_disp_periodic is clipped in the same way
1002 if (Displacements<NDIM>::bmax_default() >= twon) bmax_standard = twon-1;
1003 }
1004
1005 if (disp_d_eff_abs > bmax_standard) {
1006 among_standard_displacements = false;
1007 // Do not break - this loop needs not only to determine among_standard_displacements but to shift the displacement if domain_is_periodic_
1008 // Therefore, looping over all dim is strictly necessary.
1009 }
1010 }
1011 if (among_standard_displacements) {
1012 const auto distsq = default_distance_squared_(*displacement);
1013 if (distsq > max_distsq_reached_) { // among standard displacements => keep if longer than the longest standard displacement considered
1014 return true;
1015 } {
1016 return false;
1017 }
1018 }
1019 else // not among standard displacements => keep it
1020 return true;
1021 }
1022 else // skip the displacement-based filter if not given
1023 return true;
1024 }
1025 else
1026 return false;
1027 }
1028
1029 private:
1030 std::array<ExtraDomainPolicy, NDIM> domain_policies_;
1031 std::array<KernelRange, NDIM> range_;
1034 };
1035
1036} // namespace madness
1037#endif // MADNESS_MRA_DISPLACEMENTS_H__INCLUDED
Iterator class for lazy generation of surface points.
Definition displacements.h:368
std::optional< Displacement > disp
Memoized displacement from parent->center_ to point, computed by displacement(), reset by advance()
Definition displacements.h:374
Iterator(const BoxSurfaceDisplacementRange *p, Type type)
Constructs an iterator.
Definition displacements.h:603
@ End
Definition displacements.h:370
@ Begin
Definition displacements.h:370
size_t fixed_dim
Current fixed dimension (i.e. faces perpendicular to this axis are being iterated over)
Definition displacements.h:375
const std::optional< Displacement > & displacement() const
Definition displacements.h:582
friend bool operator!=(const Iterator &a, const Iterator &b)
Inequality comparison operator.
Definition displacements.h:673
bool done
Flag indicating iteration completion.
Definition displacements.h:384
const Point * pointer
Definition displacements.h:594
pointer operator->() const
Arrow operator for member access.
Definition displacements.h:633
Box unprocessed_bounds
Definition displacements.h:376
Iterator operator++(int)
Post-increment operator.
Definition displacements.h:648
std::ptrdiff_t difference_type
Definition displacements.h:593
std::input_iterator_tag iterator_category
Definition displacements.h:591
const BoxSurfaceDisplacementRange * parent
Pointer to parent surface.
Definition displacements.h:372
bool next_surface_layer()
Definition displacements.h:388
reference operator*() const
Dereferences the iterator.
Definition displacements.h:627
Point value_type
Definition displacements.h:592
void reset_along_dim(size_t dim)
Definition displacements.h:516
void advance_till_valid()
Perform advance, repeating if you are at a filtered point.
Definition displacements.h:496
Point point
Current point / box. This is always free to leave the simulation cell.
Definition displacements.h:373
friend bool operator==(const Iterator &a, const Iterator &b)
Equality comparison operator.
Definition displacements.h:660
Iterator & operator++()
Pre-increment operator.
Definition displacements.h:639
const Point & reference
Definition displacements.h:595
void advance()
Advances the iterator to the next surface point.
Definition displacements.h:421
Definition displacements.h:333
Displacement probing_displacement_
displacement to a nearby point on the surface; it may not be able to pass the filter,...
Definition displacements.h:360
std::function< bool(Level, const PointPattern &, std::optional< Displacement > &)> Validator
Definition displacements.h:341
BoxSurfaceDisplacementRange(const Key< NDIM > &center, const std::array< std::optional< std::int64_t >, NDIM > &box_radius, const std::array< std::optional< std::int64_t >, NDIM > &surface_thickness, const array_of_bools< NDIM > &is_lattice_summed, Validator validator={}, std::optional< Translation > probe_offset_radius={})
Constructs a box with different radii and thicknesses for each dimension.
Definition displacements.h:693
Key< NDIM > Point
Definition displacements.h:335
const Key< NDIM > & center() const
Definition displacements.h:752
std::array< std::optional< Translation >, NDIM > SurfaceThickness
Definition displacements.h:345
const std::array< std::optional< int64_t >, NDIM > & box_radius() const
Definition displacements.h:757
const array_of_bools< NDIM > & is_lattice_summed() const
Definition displacements.h:767
Periodicity is_lattice_summed_
which dimensions are lattice summed?
Definition displacements.h:356
Validator validator_
optional validator function
Definition displacements.h:357
SurfaceThickness surface_thickness_
surface thickness in each dimension, measured in boxes. Real-space surface size is thus n-dependent.
Definition displacements.h:353
std::array< std::optional< Translation >, NDIM > BoxRadius
Definition displacements.h:344
const Displacement compute_probing_displacement()
Definition displacements.h:777
const std::array< std::optional< int64_t >, NDIM > & surface_thickness() const
Definition displacements.h:762
Hollowness hollowness_
does box contain non-surface points along each dimension?
Definition displacements.h:355
std::optional< Translation > probe_offset_radius_
Definition displacements.h:358
auto begin() const
Returns an iterator to the beginning of the surface points.
Definition displacements.h:731
Box box_
box bounds in each dimension.
Definition displacements.h:354
const Displacement & probing_displacement() const
Definition displacements.h:772
Point center_
Center point of the box.
Definition displacements.h:350
Key< NDIM > Displacement
Definition displacements.h:337
std::array< bool, NDIM > Hollowness
Definition displacements.h:347
auto end() const
Returns an iterator to the end of the surface points.
Definition displacements.h:737
Vector< std::optional< Translation >, NDIM > PointPattern
Definition displacements.h:336
std::array< std::pair< Translation, Translation >, NDIM > Box
Definition displacements.h:346
BoxRadius box_radius_
halved size of the box in each dimension, in half-SimulationCells.
Definition displacements.h:351
Definition displacements.h:892
std::function< double(const Displacement &)> DistanceSquaredFunc
Definition displacements.h:898
Vector< std::optional< Translation >, NDIM > PointPattern
Definition displacements.h:895
DistanceSquaredFunc default_distance_squared_
Definition displacements.h:1032
Key< NDIM > Displacement
Definition displacements.h:896
std::array< ExtraDomainPolicy, NDIM > domain_policies_
Definition displacements.h:1030
Key< NDIM > Point
Definition displacements.h:894
double max_distsq_reached_
Definition displacements.h:1033
bool operator()(const Level level, const PointPattern &dest, std::optional< Displacement > &displacement) const
Apply filter to a displacement ending up at a point or a group of points (point pattern)
Definition displacements.h:940
std::array< KernelRange, NDIM > range_
Definition displacements.h:1031
BoxSurfaceDisplacementValidator(const array_of_bools< NDIM > &is_infinite_domain, const array_of_bools< NDIM > &is_lattice_summed, const std::array< KernelRange, NDIM > &range, DistanceSquaredFunc default_distance_squared, double max_distsq_reached)
Definition displacements.h:905
Holds displacements for applying operators to avoid replicating for all operators.
Definition displacements.h:66
static std::array< std::vector< Key< NDIM > >, 64 > disp_periodic
displacements to be used with lattice-summed kernels
Definition displacements.h:70
static bool cmp_keys(const Key< NDIM > &a, const Key< NDIM > &b)
Definition displacements.h:89
const std::vector< Key< NDIM > > & get_disp()
return the standard displacements appropriate for operators w/o lattice summation
Definition displacements.h:261
const std::vector< Key< NDIM > > & get_disp(Level n, const array_of_bools< NDIM > &kernel_lattice_sum_axes)
Definition displacements.h:237
static std::vector< Key< NDIM > > disp
standard displacements to be used with standard kernels (range-unrestricted, no lattice sum)
Definition displacements.h:68
static Tensor< double > widths
cell width, used to order displacements from least to most real space distance
Definition displacements.h:71
static void reset_periodic_axes(const array_of_bools< NDIM > &new_periodic_axes)
rebuilds periodic displacements so that they are optimal for the given set of periodic axes
Definition displacements.h:275
static void make_disp(int bmax)
Definition displacements.h:103
static void make_disp_periodic(int bmax, Level n)
Definition displacements.h:159
static int bmax_default()
Definition displacements.h:74
static bool cmp_keys_periodic(const Key< NDIM > &a, const Key< NDIM > &b)
Definition displacements.h:96
static array_of_bools< NDIM > periodic_axes
along which axes lattice summation is performed?
Definition displacements.h:69
static void set_width(const Tensor< double > &width)
Definition displacements.h:290
Displacements()
Definition displacements.h:214
FunctionDefaults holds default paramaters as static class members.
Definition funcdefaults.h:100
Definition indexit.h:55
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:70
A tensor is a multidimensional array.
Definition tensor.h:318
float_scalar_type normf() const
Returns the Frobenius norm of the tensor.
Definition tensor.h:1727
A simple, fixed dimension vector.
Definition vector.h:64
syntactic sugar for std::array<bool, N>
Definition array_of_bools.h:19
bool any() const
Definition array_of_bools.h:38
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
real_function_3d mask
Definition dirac-hatom.cc:27
Provides FunctionDefaults and utilities for coordinate transformation.
Provides IndexIterator.
#define MADNESS_PRAGMA_CLANG(x)
Definition madness_config.h:200
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:13
ExtraDomainPolicy
Definition displacements.h:53
int64_t Translation
Definition key.h:58
Key< NDIM > displacement(const Key< NDIM > &source, const Key< NDIM > &target)
given a source and a target, return the displacement in translation
Definition key.h:533
int Level
Definition key.h:59
static double pop(std::vector< double > &v)
Definition SCF.cc:117
constexpr std::array< std::size_t, N-M > iota_array(std::array< std::size_t, M > values_to_skip_sorted)
Definition displacements.h:304
std::string type(const PairType &n)
Definition PNOParameters.h:18
Definition mraimpl.h:51
static long abs(long a)
Definition tensor.h:219
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
Defines and implements most of Tensor.
void e()
Definition test_sig.cc:75
#define N
Definition testconv.cc:37
const double offset
Definition testfuns.cc:143
constexpr std::size_t NDIM
Definition testgconv.cc:54