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>
38
39#include <algorithm>
40#include <array>
41#include <functional>
42#include <iterator>
43#include <optional>
44#include <tuple>
45#include <utility>
46#include <vector>
47
48namespace madness {
49 /// Holds displacements for applying operators to avoid replicating for all operators
50 template <std::size_t NDIM>
52
53 static std::vector< Key<NDIM> > disp; ///< standard displacements to be used with standard kernels (range-unrestricted, no lattice sum)
54 static array_of_bools<NDIM> periodic_axes; ///< along which axes lattice summation is performed?
55 static std::vector< Key<NDIM> > disp_periodic[64]; ///< displacements to be used with lattice-summed kernels
56
57 public:
58 static int bmax_default() {
59 int bmax;
60 if (NDIM == 1) bmax = 7;
61 else if (NDIM == 2) bmax = 5;
62 else if (NDIM == 3) bmax = 3;
63 else if (NDIM == 4) bmax = 3;
64 else if (NDIM == 5) bmax = 3;
65 else if (NDIM == 6) bmax = 3;
66 else bmax = 2;
67 return bmax;
68 }
69
70 private:
71 static bool cmp_keys(const Key<NDIM>& a, const Key<NDIM>& b) {
72 return a.distsq() < b.distsq();
73 }
74
75 static bool cmp_keys_periodic(const Key<NDIM>& a, const Key<NDIM>& b) {
76 return a.distsq_bc(periodic_axes) < b.distsq_bc(periodic_axes);
77 }
78
79 static void make_disp(int bmax) {
80 // Note newer loop structure in make_disp_periodic_sum
82
83 int num = 1;
84 for (std::size_t i=0; i<NDIM; ++i) num *= (2*bmax + 1);
85 disp.resize(num,Key<NDIM>(0));
86
87 num = 0;
88 if (NDIM == 1) {
89 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
90 disp[num++] = Key<NDIM>(0,d);
91 }
92 else if (NDIM == 2) {
93 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
94 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
95 disp[num++] = Key<NDIM>(0,d);
96 }
97 else if (NDIM == 3) {
98 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
99 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
100 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
101 disp[num++] = Key<NDIM>(0,d);
102 }
103 else if (NDIM == 4) {
104 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
105 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
106 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
107 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
108 disp[num++] = Key<NDIM>(0,d);
109 }
110 else if (NDIM == 5) {
111 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
112 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
113 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
114 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
115 for (d[4]=-bmax; d[4]<=bmax; ++d[4])
116
117 disp[num++] = Key<NDIM>(0,d);
118 }
119 else if (NDIM == 6) {
120 for (d[0]=-bmax; d[0]<=bmax; ++d[0])
121 for (d[1]=-bmax; d[1]<=bmax; ++d[1])
122 for (d[2]=-bmax; d[2]<=bmax; ++d[2])
123 for (d[3]=-bmax; d[3]<=bmax; ++d[3])
124 for (d[4]=-bmax; d[4]<=bmax; ++d[4])
125 for (d[5]=-bmax; d[5]<=bmax; ++d[5])
126 disp[num++] = Key<NDIM>(0,d);
127 }
128 else {
129 MADNESS_EXCEPTION("make_disp: hard dimension loop",NDIM);
130 }
131
132 std::sort(disp.begin(), disp.end(), cmp_keys);
133 }
134
135 static void make_disp_periodic(int bmax, Level n) {
136 MADNESS_ASSERT(periodic_axes.any()); // else use make_disp
137 Translation twon = Translation(1)<<n;
138
139 if (bmax > (twon-1)) bmax=twon-1;
140
141 // Make permissible 1D translations, periodic and nonperiodic (for mixed BC)
142 std::vector<Translation> bp(4*bmax+1);
143 std::vector<Translation> bnp(2*bmax+1);
144 int ip=0;
145 int inp=0;
146 for (Translation lx=-bmax; lx<=bmax; ++lx) {
147 bp[ip++] = lx;
148 if ((lx < 0) && (lx+twon > bmax)) bp[ip++] = lx + twon;
149 if ((lx > 0) && (lx-twon <-bmax)) bp[ip++] = lx - twon;
150 bnp[inp++] = lx;
151 }
152 MADNESS_ASSERT(ip <= 4*bmax+1);
153 MADNESS_ASSERT(inp <= 2*bmax+1);
154 const int nbp = ip;
155 const int nbnp = inp;
156
158 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
159
160 disp_periodic[n] = std::vector< Key<NDIM> >();
162 for(size_t i=0; i!=NDIM; ++i) {
163 lim[i] = periodic_axes[i] ? nbp : nbnp;
164 }
165 for (IndexIterator index(lim); index; ++index) {
167 for (std::size_t i=0; i<NDIM; ++i) {
168 d[i] = periodic_axes[i] ? bp[index[i]] : bnp[index[i]];
169 }
170 disp_periodic[n].push_back(Key<NDIM>(n,d));
171 }
172
173 std::sort(disp_periodic[n].begin(), disp_periodic[n].end(), cmp_keys_periodic);
174// print("KEYS AT LEVEL", n);
175// print(disp_periodic[n]);
176
178
179 }
180
181
182 public:
183 /// first time this is called displacements are generated.
184 /// if boundary conditions are not periodic, the periodic displacements
185 /// are generated for all axes. This allows to support application of
186 /// operators with boundary conditions periodic along any axis (including all).
187 /// If need to use periodic boundary conditions
188 /// for some axes only, make sure to set the boundary conditions appropriately
189 /// before the first call to this
192 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
193
194 if (disp.empty()) {
196 }
197
198 if constexpr (NDIM <= 3) {
199 if (disp_periodic[0].empty()) { // if not initialized yet
200 if (FunctionDefaults<NDIM>::get_bc().is_periodic().any())
202 FunctionDefaults<NDIM>::get_bc().is_periodic());
203 else
205 }
206 }
207
209 }
210
211 const std::vector< Key<NDIM> >& get_disp(Level n,
214 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
215
216 if (kernel_lattice_sum_axes.any()) {
217 MADNESS_ASSERT(NDIM <= 3);
218 MADNESS_ASSERT((std::size_t)n < std::extent_v<decltype(disp_periodic)>);
220 std::string msg =
221 "Displacements<" + std::to_string(NDIM) +
222 ">::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";
223 MADNESS_EXCEPTION(msg.c_str(), 1);
224 }
225 return disp_periodic[n];
226 }
227 else {
228 return disp;
229 }
230
232 }
233
234 /// return the standard displacements appropriate for operators w/o lattice summation
235 const std::vector< Key<NDIM> >& get_disp() {
237 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
238
239 return disp;
240
242 }
243
244 /// rebuilds periodic displacements so that they are optimal for the given set of periodic axes
245
246 /// this must be done while no references to prior periodic displacements are outstanding (i.e. no operator application
247 /// tasks in flight)
248 /// \param new_periodic_axes the new periodic axes
251 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wundefined-var-template")
252
253 MADNESS_ASSERT(new_periodic_axes.any()); // else why call this?
255
257 Level nmax = 8 * sizeof(Translation) - 2;
258 for (Level n = 0; n < nmax; ++n)
260 }
262 }
263 };
264
265 template <std::size_t N, std::size_t M>
266 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) {
267 std::array<std::size_t, N - M> result;
268 if constexpr (N != M) {
269 std::size_t nadded = 0;
273 for (std::size_t i = 0; i < N; ++i) {
274 if (i < value_to_skip) {
275 result[nadded++] = i;
276 } else if (value_to_skip_it != values_to_skip_sorted.end()) {
280 } else
282 }
283 }
284 }
285 return result;
286 }
287
288 /**
289 * 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.
290 * 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.
291 * For dimensions with unlimited size the point coordinates are limited to [0,2^n], with n being the level of the box.
292 */
293 template<std::size_t NDIM>
295 public:
299 /// this callable filters out points and/or displacements; note that the displacement is optional (this use case supports filtering based on point pattern onlu) and non-const to make it possible for the filter function to update the displacement (e.g. to map it back to the simulation cell)
300 using Filter = std::function<bool(Level, const PointPattern&, std::optional<Displacement>&)>;
301
302 private:
303 using BoxRadius = std::array<std::optional<Translation>, NDIM>; // null radius = unlimited size
304 using SurfaceThickness = std::array<std::optional<Translation>, NDIM>; // null thickness for dimensions with null radius
305 using Box = std::array<std::pair<Translation, Translation>, NDIM>;
306 using Hollowness = std::array<bool, NDIM>; // this can be uninitialized, unlike array_of_bools
308
309 Point center_; ///< Center point of the box
310 BoxRadius box_radius_; ///< halved size of the box in each dimension
312 surface_thickness_; ///< surface thickness in each dimension
313 Box box_; ///< box bounds in each dimension
314 Hollowness hollowness_; ///< does box contain non-surface points along each dimension?
315 Periodicity is_periodic_; ///< which dimensions are periodic?
316 Filter filter_; ///< optional filter function
317 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
318
319 /**
320 * @brief Iterator class for lazy generation of surface points
321 *
322 * This iterator generates surface points on-demand by tracking the current fixed
323 * dimension and positions in each dimension. It implements the InputIterator concept.
324 */
325 class Iterator {
326 public:
327 enum Type {Begin, End};
328 private:
329 const BoxSurfaceDisplacementRange* parent; ///< Pointer to parent box
330 Point point; ///< Current point
331 mutable std::optional<Displacement> disp; ///< Memoized displacement from parent->center_ to point, computed by displacement(), reset by advance()
332 size_t fixed_dim; ///< Current fixed dimension (i.e. faces perpendicular to this axis are being iterated over)
333 Box box; ///< updated box bounds in each dimension, used to avoid duplicate displacements by excluding the surface displacements for each processed fixed dim
334 bool done; ///< Flag indicating iteration completion
335
336 // return true if we have another surface layer
339 if (l[fixed_dim] !=
340 parent->box_[fixed_dim].second +
341 parent->surface_thickness_[fixed_dim].value_or(0)) {
342 // if box is hollow along this dim (has 2 surface layers) and exhausted all layers on the "negative" side of the fixed dimension, move to the first layer on the "positive" side
344 l[fixed_dim] ==
345 parent->box_[fixed_dim].first +
346 parent->surface_thickness_[fixed_dim].value_or(0)) {
347 l[fixed_dim] =
348 parent->box_[fixed_dim].second -
349 parent->surface_thickness_[fixed_dim].value_or(0);
350 } else
351 ++l[fixed_dim];
352 point = Point(point.level(), l);
353 return true;
354 } else
355 return false;
356 };
357
358 /**
359 * @brief Advances the iterator to the next surface point
360 *
361 * This function implements the logic for traversing the box surface by:
362 * 1. Incrementing displacement in non-fixed dimensions
363 * 2. Switching sides in the fixed dimension when needed
364 * 3. Moving to the next fixed dimension when current one is exhausted
365 */
366 void advance() {
367 disp.reset();
368
369 auto increment_along_dim = [this](size_t dim) {
373 };
374
375 for (size_t i = NDIM; i > 0; --i) {
376 const size_t cur_dim = i - 1;
377 if (cur_dim == fixed_dim) continue;
378
379 if (point[cur_dim] < box[cur_dim].second) {
381 return;
382 }
384 }
385
386 // move to the next surface layer normal to the fixed dimension
388 const auto filtered_out = [&,this]() {
389 bool result = false;
390 const auto& filter = this->parent->filter_;
391 if (filter) {
394 std::optional<Displacement> nulldisp;
395 result = !filter(point.level(), point_pattern, nulldisp);
396 }
397 return result;
398 };
399
400 if (!filtered_out())
401 return;
402 }
403
404 // ready to switch to next fixed dimension with finite radius
405 // but first update box bounds to exclude the surface displacements for the current fixed dimension
406 // WARNING if box along this dimension is not hollow we are done!
408 box[fixed_dim] = {
409 parent->box_[fixed_dim].first +
410 parent->surface_thickness_[fixed_dim].value_or(0) + 1,
411 parent->box_[fixed_dim].second -
412 parent->surface_thickness_[fixed_dim].value_or(0) - 1};
413 }
414 else {
415 done = true;
416 return;
417 }
418 // onto next dimension
419 ++fixed_dim;
420 while (!parent->box_radius_[fixed_dim] && fixed_dim < NDIM) {
421 ++fixed_dim;
422 }
423
424
425 if (fixed_dim >= NDIM) {
426 done = true;
427 return;
428 }
429
430 // reset upon moving to the next fixed dimension
431 for (size_t i = 0; i < NDIM; ++i) {
433 }
434 }
435
437 if (parent->filter_) {
438 const auto filtered_out = [&]() -> bool {
439 this->displacement(); // ensure disp is up to date
440 return !parent->filter_(point.level(), point.translation(), disp);
441 };
442
443 // if displacement has value, filter has already been applied to it, just advance it
444 if (!done && disp) this->advance();
445
446 while (!done && filtered_out()) {
447 this->advance();
448 }
449 }
450 else
451 this->advance();
452 }
453
454 void reset_along_dim(size_t dim) {
455 const auto is_fixed_dim = dim == fixed_dim;
457 // for fixed dimension start with first surface layer, else use box lower bound (N.B. it's updated as fixed dimensions change)
460 ? parent->box_[dim].first -
461 parent->surface_thickness_[dim].value_or(0)
462 : box[dim].first;
463 // if dimension is periodic, only include *unique* displacements (modulo period)
464 if (parent->is_periodic_[dim]) {
465 const auto period = 1 << parent->center_.level();
466 const Translation l_dim_max =
467 is_fixed_dim ? parent->box_[dim].second +
468 parent->surface_thickness_[dim].value_or(0) :
469 box[dim].second;
470 l_dim_min = std::max(l_dim_min, l_dim_max - period + 1);
471 // fixed dim only: l_dim_min may not correspond to a surface layer
472 // this can only happen if l_dim_min is in the gap between the surface layers
473 if (is_fixed_dim && parent->hollowness_[dim]) {
474 if (l_dim_min > parent->box_[dim].first +
475 parent->surface_thickness_[dim].value_or(0)) {
476 l_dim_min = std::max(
477 l_dim_min, parent->box_[dim].second -
478 parent->surface_thickness_[dim].value_or(0));
479 }
480 }
481 }
482 l[dim] = l_dim_min;
483
484 point = Point(point.level(), l);
485
486 // if the entire surface layer is filtered out, pick the next one
487 if (dim == fixed_dim) {
488
489 const auto filtered_out = [&,this]() {
490 bool result = false;
491 const auto& filter = this->parent->filter_;
492 if (filter) {
495 std::optional<Displacement> nulldisp;
496 result = !filter(point.level(), point_pattern, nulldisp);
497 }
498 return result;
499 };
500
501 if (filtered_out()) {
504 if (!filtered_out())
505 break;
506 }
508 }
509
510 }
511 };
512
513 /**
514 * @return displacement from the center to the current point
515 */
516 const std::optional<Displacement>& displacement() const {
517 if (!disp) {
519 }
520 return disp;
521 }
522
523 public:
524 // Iterator type definitions for STL compatibility
525 using iterator_category = std::input_iterator_tag;
527 using difference_type = std::ptrdiff_t;
528 using pointer = const Point*;
529 using reference = const Point&;
530
531 /**
532 * @brief Constructs an iterator
533 *
534 * @param p Pointer to the parent BoxSurfaceDisplacementRange
535 * @param type the type of iterator (Begin or End)
536 */
538 : parent(p), point(parent->center_.level()), fixed_dim(type == End ? NDIM : 0), done(type == End) {
539 if (type != End) {
540 // skip to first dimensions with limited range
541 while (!parent->box_radius_[fixed_dim] && fixed_dim < NDIM) {
542 ++fixed_dim;
543 }
544
545 for (size_t d = 0; d != NDIM; ++d) {
546 // min/max displacements along this axis ... N.B. take into account surface thickness!
547 box[d] = parent->box_radius_[d] ? std::pair{parent->box_[d].first -
548 parent->surface_thickness_[d].value_or(0),
549 parent->box_[d].second +
550 parent->surface_thickness_[d].value_or(0)} : parent->box_[d];
552 }
554 }
555 }
556
557 /**
558 * @brief Dereferences the iterator
559 * @return A const reference to the current displacement
560 */
561 reference operator*() const { return *displacement(); }
562
563 /**
564 * @brief Arrow operator for member access
565 * @return A const pointer to the current displacement
566 */
567 pointer operator->() const { return &(*(*this)); }
568
569 /**
570 * @brief Pre-increment operator
571 * @return Reference to this iterator after advancement
572 */
575 return *this;
576 }
577
578 /**
579 * @brief Post-increment operator
580 * @return Copy of the iterator before advancement
581 */
583 Iterator tmp = *this;
584 ++(*this);
585 return tmp;
586 }
587
588 /**
589 * @brief Equality comparison operator
590 * @param a First iterator
591 * @param b Second iterator
592 * @return true if iterators are equivalent
593 */
594 friend bool operator==(const Iterator& a, const Iterator& b) {
595 if (a.done && b.done) return true;
596 if (a.done || b.done) return false;
597 return a.fixed_dim == b.fixed_dim &&
598 a.point == b.point;
599 }
600
601 /**
602 * @brief Inequality comparison operator
603 * @param a First iterator
604 * @param b Second iterator
605 * @return true if iterators are not equivalent
606 */
607 friend bool operator!=(const Iterator& a, const Iterator& b) {
608 return !(a == b);
609 }
610 };
611
612 friend class Iterator;
613
614 public:
615 /**
616 * @brief Constructs a box with different sizes for each dimension
617 *
618 * @param center Center of the box
619 * @param box_radius Box radius in each dimension
620 * @param surface_thickness Surface thickness in each dimension
621 * @param is_periodic whether each dimension is periodic; along periodic range-restricted dimensions only one side of the box is iterated over.
622 * @param filter 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)
623 * @pre `box_radius[d]>0 && surface_thickness[d]<=box_radius[d]`
624 *
625 */
627 const std::array<std::optional<std::int64_t>, NDIM>& box_radius,
628 const std::array<std::optional<std::int64_t>, NDIM>& surface_thickness,
630 Filter filter = {})
633 // initialize box bounds
634 bool has_finite_dimensions = false;
635 const auto n = center_.level();
637 for (size_t d=0; d!= NDIM; ++d) {
638 if (box_radius_[d]) {
639 auto r = *box_radius_[d]; // in units of 2^{n-1}
640 r = (n == 0) ? (r+1)/2 : (r * Translation(1) << (n-1));
641 MADNESS_ASSERT(r > 0);
642 box_[d] = {center_[d] - r, center_[d] + r};
643 if (!has_finite_dimensions) // first finite dimension? probing displacement will be nonzero along it, zero along all others
646 } else {
647 box_[d] = {0, (1 << center_.level()) - 1};
648 }
649 }
652 for (size_t d=0; d!= NDIM; ++d) {
653 // surface thickness should be only given for finite-radius dimensions
656 hollowness_[d] = surface_thickness_[d] ? (box_[d].first + surface_thickness_[d].value() < box_[d].second - surface_thickness_[d].value()) : false;
657 }
658 }
659
660 /**
661 * @brief Returns an iterator to the beginning of the surface points
662 * @return Iterator pointing to the first surface point
663 */
664 auto begin() const { return Iterator(this, Iterator::Begin); }
665
666 /**
667 * @brief Returns an iterator to the end of the surface points
668 * @return Iterator indicating the end of iteration
669 */
670 auto end() const { return Iterator(this, Iterator::End); }
671
672 // /**
673 // * @brief Returns a view over the surface points
674 // *
675 // * This operator allows the class to be used with C++20 ranges.
676 // *
677 // * @return A view over the surface points
678 // */
679 // auto operator()() const {
680 // return std::ranges::subrange(begin(), end());
681 // }
682
683 /* @return the center of the box
684 */
685 const Key<NDIM>& center() const { return center_; }
686
687 /**
688 * @return the radius of the box in each dimension
689 */
690 const std::array<std::optional<int64_t>, NDIM>& box_radius() const { return box_radius_; }
691
692 /**
693 * @return the surface thickness in each dimension
694 */
695 const std::array<std::optional<int64_t>, NDIM>& surface_thickness() const { return surface_thickness_; }
696
697 /**
698 * @return flags indicating whether each dimension is periodic
699 */
701
702 /**
703 * @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
704 */
707 }
708 }; // BoxSurfaceDisplacementRange
709
710
711 /// This is used to filter out box surface displacements that
712 /// - take us outside of the target domain, or
713 /// - were already utilized as part of the the standard displacements list.
714 /// For dealing with the lattice-summed operators the filter
715 /// can adjusts the displacement to make sure that we end up in
716 /// the simulation cell.
717 template <size_t NDIM>
719 public:
724 using DistanceSquaredFunc = std::function<Translation(const Displacement&)>;
725
726 /// \param domain_is_infinite 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
727 /// \param domain_is_periodic 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.
728 /// \param range the kernel range for each axis
729 /// \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)
730 /// \param max_distsq_reached max effective distance squared reached by standard displacements
744
745 /// Apply filter to a displacement ending up at a point or a group of points (point pattern)
746
747 /// @param level the tree level
748 /// @param dest the target point (when all elements are nonnull) or point pattern (when only some are).
749 /// The latter is useful to skip the entire surface layer. The point coordinates are
750 /// only used to determine whether we end up in or out of the domain.
751 /// @param displacement the optional displacement; if given then will check if it's among
752 /// the standard displacement and whether it was used as part of the standard displacement
753 /// set; if it has not been used and the operator is lattice summed, the displacement
754 /// will be adjusted to end up in the simulation cell.
755 /// @return true if the displacement is to be used
757 const Level level,
758 const PointPattern& dest,
759 std::optional<Displacement>& displacement
760 ) const {
761 // preliminaries
762 const auto twon = (static_cast<Translation>(1) << level); // number of boxes along an axis
763 // map_to_range_twon(x) returns for x >= 0 ? x % 2^level : map_to_range_twon(x+2^level)
764 // idiv is generally slow, so instead use bit logic that relies on 2's complement representation of integers
765 const auto map_to_range_twon = [&, mask = ((~(static_cast<std::uint64_t>(0)) << (64-level)) >> (64-level))](std::int64_t x) -> std::int64_t {
766 const std::int64_t x_mapped = x & mask;
767 MADNESS_ASSERT(x_mapped >=0 && x_mapped < twon && (std::abs(x_mapped-x)%twon==0));
768 return x_mapped;
769 };
770
771 const auto out_of_domain = [&](const Translation& t) -> bool {
772 return t < 0 || t >= twon;
773 };
774
775 // check that dest is in the domain
776 const bool dest_is_in_domain = [&]() {
777 for(size_t d=0; d!=NDIM; ++d) {
778 // - if domain is periodic, all displacements will be mapped back to the simulation cell by for_each/neighbor
779 // - if kernel is lattice summed mapping back to the simulation cell for standard displacements
780 // is done during their construction, and for boundary displacements manually (see IMPORTANT below in this function)
781 // N.B. due to this mapping back into the cell only half of the surface displacements was generated by BoxSurfaceDisplacementRange
782 if (domain_is_infinite_[d] || domain_is_periodic_[d]) continue;
783 if (dest[d].has_value() && out_of_domain(*dest[d])) return false;
784 }
785 return true;
786 }();
787
788 if (dest_is_in_domain) {
789 if (displacement.has_value()) {
790
791 // N.B. avoid duplicates of standard displacements previously included:
792 // A displacement has been possibly considered if along EVERY axis the "effective" displacement size
793 // fits within the box explored by the standard displacement.
794 // If so, skip if <= max magnitude of standard displacements encountered
795 // Otherwise this is a new non-standard displacement, consider it
797 for(size_t d=0; d!=NDIM; ++d) {
798 const auto disp_d = (*displacement)[d];
800
801 // the effective displacement length depends on whether lattice summation is performed along it
802 // compare Displacements::make_disp vs Displacements::make_disp_periodic
804 if (domain_is_periodic_[d]) {
805 MADNESS_ASSERT(range_[d].N() <= 2); // displacements that exceed 1 whole cell need bit more complex logic
806
807 // for "periodic" displacements the effective disp_d is the shortest of {..., disp_d-twon, disp_d, disp_d+twon, ...} ... see make_disp_periodic
808 const std::int64_t disp_d_eff = map_to_range_twon(disp_d);
810
811 // IMPORTANT for lattice-summed axes, if the destination is out of the simulation cell map the displacement back to the cell
812 // same logic as for disp_d: dest[d] -> dest[d] % twon
813 if (dest[d].has_value()) {
814 const Translation dest_d = dest[d].value();
818 // adjust displacement[d] so that it produces dest_d_cell, not dest_d
819 auto t = (*displacement).translation();
820 t[d] += (dest_d_in_cell - dest_d);
821 displacement.emplace(displacement->level(), t);
822 }
823
824 // N.B. bmax in make_disp_periodic is wrapped around, adjust
825 if (Displacements<NDIM>::bmax_default() >= twon) bmax_standard = twon-1;
826 }
827
830 break;
831 }
832 }
834 const auto distsq = default_distance_squared_(*displacement);
835 if (distsq > max_distsq_reached_) { // among standard displacements => keep if longer than the longest standard displacement considered
836 return true;
837 } {
838 return false;
839 }
840 }
841 else // not among standard displacements => keep it
842 return true;
843 }
844 else // skip the displacement-based filter if not given
845 return true;
846 }
847 else
848 return false;
849 }
850
851 private:
854 std::array<KernelRange, NDIM> range_;
857 };
858
859} // namespace madness
860#endif // MADNESS_MRA_DISPLACEMENTS_H__INCLUDED
Definition displacements.h:718
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:756
array_of_bools< NDIM > domain_is_periodic_
Definition displacements.h:853
array_of_bools< NDIM > domain_is_infinite_
Definition displacements.h:852
std::array< KernelRange, NDIM > range_
Definition displacements.h:854
std::function< Translation(const Displacement &)> DistanceSquaredFunc
Definition displacements.h:724
Translation max_distsq_reached_
Definition displacements.h:856
DistanceSquaredFunc default_distance_squared_
Definition displacements.h:855
BoxSurfaceDisplacementFilter(const array_of_bools< NDIM > &domain_is_infinite, const array_of_bools< NDIM > &domain_is_periodic, const std::array< KernelRange, NDIM > &range, DistanceSquaredFunc default_distance_squared, Translation max_distsq_reached)
Definition displacements.h:731
Iterator class for lazy generation of surface points.
Definition displacements.h:325
std::optional< Displacement > disp
Memoized displacement from parent->center_ to point, computed by displacement(), reset by advance()
Definition displacements.h:331
Iterator(const BoxSurfaceDisplacementRange *p, Type type)
Constructs an iterator.
Definition displacements.h:537
@ End
Definition displacements.h:327
@ Begin
Definition displacements.h:327
size_t fixed_dim
Current fixed dimension (i.e. faces perpendicular to this axis are being iterated over)
Definition displacements.h:332
const std::optional< Displacement > & displacement() const
Definition displacements.h:516
friend bool operator!=(const Iterator &a, const Iterator &b)
Inequality comparison operator.
Definition displacements.h:607
bool done
Flag indicating iteration completion.
Definition displacements.h:334
pointer operator->() const
Arrow operator for member access.
Definition displacements.h:567
Iterator operator++(int)
Post-increment operator.
Definition displacements.h:582
std::ptrdiff_t difference_type
Definition displacements.h:527
std::input_iterator_tag iterator_category
Definition displacements.h:525
Box box
updated box bounds in each dimension, used to avoid duplicate displacements by excluding the surface ...
Definition displacements.h:333
const BoxSurfaceDisplacementRange * parent
Pointer to parent box.
Definition displacements.h:329
bool next_surface_layer()
Definition displacements.h:337
reference operator*() const
Dereferences the iterator.
Definition displacements.h:561
void reset_along_dim(size_t dim)
Definition displacements.h:454
void advance_till_valid()
Definition displacements.h:436
Point point
Current point.
Definition displacements.h:330
friend bool operator==(const Iterator &a, const Iterator &b)
Equality comparison operator.
Definition displacements.h:594
Iterator & operator++()
Pre-increment operator.
Definition displacements.h:573
void advance()
Advances the iterator to the next surface point.
Definition displacements.h:366
Definition displacements.h:294
Displacement probing_displacement_
displacement to a nearby point on the surface; it may not be able to pass the filter,...
Definition displacements.h:317
Key< NDIM > Point
Definition displacements.h:296
const Key< NDIM > & center() const
Definition displacements.h:685
std::array< std::optional< Translation >, NDIM > SurfaceThickness
Definition displacements.h:304
const std::array< std::optional< int64_t >, NDIM > & box_radius() const
Definition displacements.h:690
SurfaceThickness surface_thickness_
surface thickness in each dimension
Definition displacements.h:312
std::array< std::optional< Translation >, NDIM > BoxRadius
Definition displacements.h:303
const std::array< std::optional< int64_t >, NDIM > & surface_thickness() const
Definition displacements.h:695
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_periodic, Filter filter={})
Constructs a box with different sizes for each dimension.
Definition displacements.h:626
Hollowness hollowness_
does box contain non-surface points along each dimension?
Definition displacements.h:314
auto begin() const
Returns an iterator to the beginning of the surface points.
Definition displacements.h:664
Box box_
box bounds in each dimension
Definition displacements.h:313
Filter filter_
optional filter function
Definition displacements.h:316
const Displacement & probing_displacement() const
Definition displacements.h:705
Point center_
Center point of the box.
Definition displacements.h:309
Periodicity is_periodic_
which dimensions are periodic?
Definition displacements.h:315
std::function< bool(Level, const PointPattern &, std::optional< Displacement > &)> Filter
this callable filters out points and/or displacements; note that the displacement is optional (this u...
Definition displacements.h:300
Key< NDIM > Displacement
Definition displacements.h:298
std::array< bool, NDIM > Hollowness
Definition displacements.h:306
const array_of_bools< NDIM > & is_periodic() const
Definition displacements.h:700
auto end() const
Returns an iterator to the end of the surface points.
Definition displacements.h:670
std::array< std::pair< Translation, Translation >, NDIM > Box
Definition displacements.h:305
BoxRadius box_radius_
halved size of the box in each dimension
Definition displacements.h:310
Holds displacements for applying operators to avoid replicating for all operators.
Definition displacements.h:51
static std::vector< Key< NDIM > > disp
standard displacements to be used with standard kernels (range-unrestricted, no lattice sum)
Definition displacements.h:53
static bool cmp_keys(const Key< NDIM > &a, const Key< NDIM > &b)
Definition displacements.h:71
const std::vector< Key< NDIM > > & get_disp()
return the standard displacements appropriate for operators w/o lattice summation
Definition displacements.h:235
const std::vector< Key< NDIM > > & get_disp(Level n, const array_of_bools< NDIM > &kernel_lattice_sum_axes)
Definition displacements.h:211
static array_of_bools< NDIM > periodic_axes
along which axes lattice summation is performed?
Definition displacements.h:54
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:249
static void make_disp(int bmax)
Definition displacements.h:79
static std::vector< Key< NDIM > > disp_periodic[64]
displacements to be used with lattice-summed kernels
Definition displacements.h:55
static void make_disp_periodic(int bmax, Level n)
Definition displacements.h:135
static int bmax_default()
Definition displacements.h:58
static bool cmp_keys_periodic(const Key< NDIM > &a, const Key< NDIM > &b)
Definition displacements.h:75
Displacements()
Definition displacements.h:190
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:69
Level level() const
Definition key.h:168
const Vector< Translation, NDIM > & translation() const
Definition key.h:173
Key neighbor(const Key< NDIM > &disp) const
given a displacement, generate a neighbor key; ignore boundary conditions and disp's level
Definition key.h:303
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:10
int64_t Translation
Definition key.h:57
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:452
int Level
Definition key.h:58
static double pop(std::vector< double > &v)
Definition SCF.cc:115
constexpr std::array< std::size_t, N-M > iota_array(std::array< std::size_t, M > values_to_skip_sorted)
Definition displacements.h:266
std::string type(const PairType &n)
Definition PNOParameters.h:18
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
Definition mraimpl.h:51
static long abs(long a)
Definition tensor.h:218
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
#define N
Definition testconv.cc:37
constexpr std::size_t NDIM
Definition testgconv.cc:54