HILA
Loading...
Searching...
No Matches
field.h
Go to the documentation of this file.
1#ifndef HILA_FIELD_H
2#define HILA_FIELD_H
3
4/**
5 * @file field.h
6 * @brief This files containts definitions for the Field class and the classes required to define
7 * it such as field_struct.
8 */
9
10#include <sstream>
11#include <iostream>
12#include <string>
13#include <cstring> //Memcpy is here...
14#include <math.h>
15#include <type_traits>
16
17#include "plumbing/defs.h"
19#include "plumbing/lattice.h"
21
22#include "plumbing/backend_vector/vector_types.h"
23
24#include "plumbing/com_mpi.h"
25
26
27// This is a marker for hilapp -- will be removed by it
28#define onsites(p) for (Parity par_dummy__(p); par_dummy__ == EVEN; par_dummy__ = ODD)
29
30template <typename T>
31class Field;
32
33template <typename T>
34void ensure_field_operators_exist();
35
36#include "plumbing/ensure_loop_functions.h"
37
38/**
39 * @class Field
40 * @brief The field class implements the standard methods for accessing Fields. Hilapp replaces the
41 * parity access patterns, Field[par] with a loop over the appropriate sites. Since the Field class
42 * is one of the more important functionalities of HILA, extensive general instructions on the Field
43 * class can be read at [HILA Functionality](@ref field_documentation) documentation.
44 *
45 * @details The Field class contains member functions used by hilapp, which are marked internal, as
46 * well as members that are useful for application developers.
47 *
48 * The Field mainly implements the interface to the Field and not the content.
49 *
50 * The Field contains a pointer to Field::field_struct, which implements MPI communication of the
51 * Field boundaries. Though generally the application developer does not need to worry about the
52 * Field::field_struct class, hence it is marked as internal and it's documentation cannot be viewed
53 * in the Web documentation.
54 *
55 * The Field::field_struct points to a field_storage, which is defined by each backend. It
56 * implements storing and accessing the Field data, including buffers for storing haloes returned
57 * from MPI communication.
58 *
59 * @tparam T Field content type
60 */
61template <typename T>
62class Field {
63
64 public:
65 enum class gather_status_t : unsigned { NOT_DONE, STARTED, DONE };
66
67 private:
68 /**
69 * @internal
70 * @class field_struct
71 * @brief Stores Field class data and communication parameters for said data
72 *
73 * Generally the user need not worry about this class.
74 * @todo field-specific boundary conditions
75 */
76 class field_struct {
77 public:
78 field_storage<T> payload; // TODO: must be maximally aligned, modifiers - never null
79 Lattice mylattice;
80#ifdef VECTORIZED
81 // get a direct ptr from here too, ease access
83#endif
84 unsigned assigned_to; // keeps track of first assignment to parities
85 gather_status_t gather_status_arr[3][NDIRS]; // is communication done
86
87 // neighbour pointers - because of boundary conditions, can be different for
88 // diff. fields
89 const unsigned *RESTRICT neighbours[NDIRS];
90 hila::bc boundary_condition[NDIRS];
91
92 MPI_Request receive_request[3][NDIRS];
93 MPI_Request send_request[3][NDIRS];
94#ifndef VANILLA
95 // vanilla needs no special receive buffers
96 T *receive_buffer[NDIRS];
97#endif
98 T *send_buffer[NDIRS];
99 /**
100 * @internal
101 * @brief Initialize communication
102 */
103 void initialize_communication() {
104 for (int d = 0; d < NDIRS; d++) {
105 for (int p = 0; p < 3; p++)
106 gather_status_arr[p][d] = gather_status_t::NOT_DONE;
107 send_buffer[d] = nullptr;
108#ifndef VANILLA
109 receive_buffer[d] = nullptr;
110#endif
111 }
112 }
113
114 /**
115 * @internal
116 * @brief Free communication
117 *
118 */
119 void free_communication() {
120 for (int d = 0; d < NDIRS; d++) {
121 if (send_buffer[d] != nullptr)
122 payload.free_mpi_buffer(send_buffer[d]);
123#ifndef VANILLA
124 if (receive_buffer[d] != nullptr)
125 payload.free_mpi_buffer(receive_buffer[d]);
126#endif
127 }
128 }
129
130 /**
131 * @internal
132 * @brief Allocate payload for lattice
133 */
134 void allocate_payload() {
135 payload.allocate_field(lattice);
136 }
137
138 /**
139 * @internal
140 * @brief Deallocate payload for lattice
141 */
142 void free_payload() {
143 payload.free_field();
144 }
145
146#ifndef VECTORIZED
147 /**
148 * @internal
149 * @brief Getter for individual element inside loop
150 *
151 * @param i
152 * @return auto
153 */
154 inline auto get(const unsigned i) const {
155 return payload.get(i, lattice->mynode.field_alloc_size);
156 }
157
158 template <typename A>
159 inline void set(const A &value, const unsigned i) {
160 payload.set(value, i, lattice->mynode.field_alloc_size);
161 }
162
163 /**
164 * @internal
165 * @brief Getter for an element outside a loop. Used to manipulate the field directly
166 * outside loops.
167 */
168 inline auto get_element(const unsigned i) const {
169 return payload.get_element(i, lattice);
170 }
171
172 template <typename A>
173 inline void set_element(const A &value, const unsigned i) {
174 payload.set_element(value, i, lattice);
175 }
176#else
177 template <typename vecT>
178 inline vecT get_vector(const unsigned i) const {
179 return payload.template get_vector<vecT>(i);
180 }
181 inline T get_element(const unsigned i) const {
182 return payload.get_element(i);
183 }
184
185 template <typename vecT>
186 inline void set_vector(const vecT &val, const unsigned i) {
187 return payload.set_vector(val, i);
188 }
189 inline void set_element(const T &val, const unsigned i) {
190 return payload.set_element(val, i);
191 }
192#endif
193
194 /**
195 * @internal
196 * @brief Gather boundary elements for communication
197 */
198 void gather_comm_elements(Direction d, Parity par, T *RESTRICT buffer,
199 const lattice_struct::comm_node_struct &to_node) const;
200
201 /**
202 * @internal
203 * @brief Place boundary elements from neighbour
204 */
205 void place_comm_elements(Direction d, Parity par, T *RESTRICT buffer,
206 const lattice_struct::comm_node_struct &from_node);
207
208 /**
209 * @internal
210 * @brief Place boundary elements from local lattice (used in vectorized version)
211 */
212 void set_local_boundary_elements(Direction dir, Parity par);
213
214 /**
215 * @internal
216 * @brief Gather a list of elements to a single node
217 * @param buffer
218 * @param coord_list
219 * @param root
220 */
221 void gather_elements(T *buffer, const std::vector<CoordinateVector> &coord_list,
222 int root = 0) const;
223 void scatter_elements(T *buffer, const std::vector<CoordinateVector> &coord_list,
224 int root = 0);
225
226
227 /// get the receive buffer pointer for the communication.
228 T *get_receive_buffer(Direction d, Parity par,
229 const lattice_struct::comm_node_struct &from_node);
230 };
231
232 // static_assert( std::is_pod<T>::value, "Field expects only pod-type elements
233 // (plain data): default constructor, copy and delete");
234 static_assert(std::is_trivial<T>::value && std::is_standard_layout<T>::value,
235 "Field expects only pod-type elements (plain data): default "
236 "constructor, copy and delete");
237
238 public:
239 /**
240 * @brief Field::fs holds all of the field content in Field::field_struct.
241 * @details The field_struct class is nonetheless marked as internal so the user need not worry
242 * about it.
243 */
244 field_struct *RESTRICT fs;
245
246 /**
247 * @brief Field constructor
248 * @details The following ways of constructing a Field object are:
249 *
250 * __Default constructor__:
251 *
252 * Assigns Field::fs to nullptr.
253 *
254 * \code {.cpp}
255 * Field<MyType> f;
256 * \endcode
257 *
258 * __Copy constructor__:
259 *
260 * Copy from already existing field of similar type if conversion exists. For example for float
261 * to double.
262 *
263 * \code {.cpp}
264 * Field<MyType> f = g;
265 * \endcode
266 *
267 * __Assignment from constant constructor__:
268 *
269 * Assign constant to field if conversion exists.
270 *
271 * \code
272 * .
273 * . Let a be a constant of type MyType1
274 * .
275 * Field<MyType2> f = a;
276 * \endcode
277 */
279
280 // put here some implementation checks for field vars
281#ifdef VECTORIZED
282 static_assert(sizeof(hila::arithmetic_type<T>) == 4 ||
283 sizeof(hila::arithmetic_type<T>) == 8,
284 "In vectorized arch (e.g. AVX2), only 4 or 8 byte (32 or 64 bit) numbers for "
285 "Field<> implemented, sorry!");
286#endif
287#if defined(CUDA) || defined(HIP)
288 static_assert(!std::is_same<hila::arithmetic_type<T>, long double>::value,
289 "Type 'long double' numbers in Field<> not supported by cuda/hip");
290#endif
291
292 fs = nullptr; // lazy allocation on 1st use
293 }
294 /**
295 * @internal
296 * @brief Copy constructor with already initialised Field
297 * @param other
298 */
299 Field(const Field &other) : Field() {
300 assert(other.is_initialized(ALL) && "Initializer Field value not set");
301
302 (*this)[ALL] = other[X];
303 }
304 /**
305 * @internal
306 * @brief Copy constructor form Field of type A to field of type F if the conversion is defined
307 * @tparam A
308 * @param other
309 */
310 template <typename A, std::enable_if_t<std::is_convertible<A, T>::value, int> = 0>
311 Field(const Field<A> &other) : Field() {
312 assert(other.is_initialized(ALL) && "Initializer Field value not set");
313
314 (*this)[ALL] = other[X];
315 }
316 /**
317 * @internal
318 * @brief Construct a new Field object with scalar (val) of type A to a field of type F type if
319 * the conversion is defined
320 * @tparam A
321 * @param val
322 */
323 template <typename A,
324 std::enable_if_t<
325 hila::is_assignable<T &, A>::value || std::is_convertible<A, T>::value, int> = 0>
326 Field(const A &val) : Field() {
327 (*this)[ALL] = val;
328 }
329 /**
330 * @internal
331 * @brief Construct a new Field object with scalar 0 with nullpointer trick
332 * @param z
333 */
334 Field(const std::nullptr_t z) : Field() {
335 (*this)[ALL] = 0;
336 }
337
338 /**
339 * @internal
340 * @brief Construct a new Field object by stealing content from previous field (rhs) which will
341 * be set to null happens in the case of std::move for example ` Field<MyType> f = g+h` where
342 * `g+h` is generated momenterally and then destroyed.
343 * @param rhs
344 */
345 Field(Field &&rhs) {
346 assert(rhs.fs->mylattice.ptr() == lattice.ptr());
347 fs = rhs.fs;
348 rhs.fs = nullptr;
349 }
350
351 ~Field() {
352 clear();
353
354#ifdef HILAPP
355 // Because destructor is instantiated for all fields,
356 // use this to put in a hook for generating call to this function
357 // in preprocessing stage
358 ensure_field_operators_exist<T>();
359#endif
360 }
361
362 /**
363 * @internal
364 * @brief Sets up memory for field content and communication.
365 */
366 void allocate() {
367 assert(lattice.is_initialized() && "Fields cannot be used before lattice.setup()");
368 assert(fs == nullptr);
369 fs = (field_struct *)memalloc(sizeof(field_struct));
370 fs->mylattice = lattice;
371 fs->allocate_payload();
372 fs->initialize_communication();
373 mark_changed(ALL); // guarantees communications will be done
374 fs->assigned_to = 0; // and this means that it is not assigned
375
376 for (Direction d = (Direction)0; d < NDIRS; ++d) {
377
378#if !defined(CUDA) && !defined(HIP)
379 fs->neighbours[d] = lattice->neighb[d];
380#else
381 fs->payload.neighbours[d] = lattice->backend_lattice->d_neighb[d];
382#endif
383 }
384
385#ifdef SPECIAL_BOUNDARY_CONDITIONS
386 foralldir(dir) {
387 fs->boundary_condition[dir] = hila::bc::PERIODIC;
388 fs->boundary_condition[-dir] = hila::bc::PERIODIC;
389 }
390#endif
391
392#ifdef VECTORIZED
394 fs->vector_lattice = lattice->backend_lattice
396 } else {
397 fs->vector_lattice = nullptr;
398 }
399#endif
400 }
401
402 /**
403 * @brief Destroys field data
404 * @details don't call destructors when exiting - either MPI or cuda can already be off.
405 */
406 void clear() {
407 if (fs != nullptr && !hila::about_to_finish) {
408 for (Direction d = (Direction)0; d < NDIRS; ++d)
409 drop_comms(d, ALL);
410 fs->free_payload();
411 fs->free_communication();
412 std::free(fs);
413 fs = nullptr;
414 }
415 }
416
417 /**
418 * @brief Returns true if the Field data has been allocated
419 * @return bool
420 */
421 bool is_allocated() const {
422 return (fs != nullptr);
423 }
424
425 /**
426 * @brief Returns true if the Field has been assigned to.
427 * @param p Field parity
428 * @return bool
429 */
430 bool is_initialized(Parity p) const {
431 return fs != nullptr && ((fs->assigned_to & parity_bits(p)) != 0);
432 }
433
434 /**
435 * @internal
436 * @brief Returns current gather_status_t
437 * @param p Field partiy
438 * @param d Direction
439 * @return gather_status_t
440 */
441 gather_status_t gather_status(Parity p, int d) const {
442 assert(parity_bits(p) && d >= 0 && d < NDIRS);
443 return fs->gather_status_arr[(int)p - 1][d];
444 }
445 void set_gather_status(Parity p, int d, gather_status_t stat) const {
446 assert(parity_bits(p) && d >= 0 && d < NDIRS);
447 fs->gather_status_arr[(int)p - 1][d] = stat;
448 }
449
450 /**
451 * @brief Allocate Field if it is not already allocated
452 * @details Check that Field is allocated, and if not do it (if not const). Must be called
453 * before the var is actually used. "hilapp" will generate these calls as needed!
454 *
455 * If Field is const assert that the Field is allocated.
456 *
457 */
458 void check_alloc() {
459 if (!is_allocated())
460 allocate();
461 }
462
463 /**
464 * @internal
465 * @brief If Field is const assert that the Field is allocated
466 */
467 void check_alloc() const {
468 assert(is_allocated());
469 }
470
471
472 /**
473 * @internal
474 * @brief Bookkeeping for field communication
475 * @details If ALL changes, both parities invalid; if p != ALL, then p and ALL.
476 * @param p Field parity
477 */
478 void mark_changed(const Parity p) const {
479
480 for (Direction i = (Direction)0; i < NDIRS; ++i) {
481 // check if there's ongoing comms, invalidate it!
482 drop_comms(i, opp_parity(p));
483
484 set_gather_status(opp_parity(p), i, gather_status_t::NOT_DONE);
485 if (p != ALL) {
486 set_gather_status(ALL, i, gather_status_t::NOT_DONE);
487 } else {
488 set_gather_status(EVEN, i, gather_status_t::NOT_DONE);
489 set_gather_status(ODD, i, gather_status_t::NOT_DONE);
490 }
491 }
492 fs->assigned_to |= parity_bits(p);
493 }
494
495 /**
496 * @internal
497 * @brief Mark the Field already gathered, no need to communicate
498 * @details Mark the field parity gathered from Direction
499 * In case p=ALL we could mark everything gathered, but we'll be conservative here
500 * and mark only this parity, because there might be other parities on the fly and
501 * corresponding waits should be done, This should never happen in automatically
502 * generated loops. In any case start_gather, is_gathered, get_gather_parity has
503 * intelligence to figure out the right thing to do
504 *
505 * @param dir
506 * @param p
507 */
508 void mark_gathered(int dir, const Parity p) const {
509 set_gather_status(p, dir, gather_status_t::DONE);
510 }
511
512 /**
513 * @internal
514 * @brief Check if the field has been gathered since the previous communication
515 * @details par = ALL: ALL or (EVEN+ODD) are OK\n
516 * par != ALL: ALL or par are OK
517 * @param dir
518 * @param par
519 * @return true
520 * @return false
521 */
522 bool is_gathered(int dir, Parity par) const {
523 if (par != ALL) {
524 return gather_status(par, dir) == gather_status_t::DONE ||
525 gather_status(ALL, dir) == gather_status_t::DONE;
526 } else {
527 return gather_status(ALL, dir) == gather_status_t::DONE ||
528 (gather_status(EVEN, dir) == gather_status_t::DONE &&
529 gather_status(ODD, dir) == gather_status_t::DONE);
530 }
531 }
532
533 /**
534 * @internal
535 * @brief Mark communication has started
536 * @param dir
537 * @param p
538 */
539 void mark_gather_started(int dir, Parity p) const {
540 set_gather_status(p, dir, gather_status_t::STARTED);
541 }
542
543 /**
544 * @internal
545 * @brief Check if communication has started
546 * @param dir
547 * @param par
548 * @return bool
549 */
550 bool is_gather_started(int dir, Parity par) const {
551 return gather_status(par, dir) == gather_status_t::STARTED;
552 }
553
554 bool gather_not_done(int dir, Parity par) const {
555 return gather_status(par, dir) == gather_status_t::NOT_DONE;
556 }
557
558 /**
559 * @internal
560 * @brief function boundary_need_to_communicate(dir) returns false if there's special B.C. which
561 * does not need comms here, otherwise true
562 */
563 bool boundary_need_to_communicate(const Direction dir) const {
564#ifdef SPECIAL_BOUNDARY_CONDITIONS
565
566 return hila::bc_need_communication(fs->boundary_condition[dir]) ||
567 !fs->mylattice->mynode.is_on_edge(dir);
568#else
569 return true;
570#endif
571 }
572
573
574 /**
575 * @brief Set the boundary condition in a given Direction (periodic or antiperiodic)
576 * @param dir Direction of boundary condition
577 * @param bc Field boundary condition
578 */
580
581#ifdef SPECIAL_BOUNDARY_CONDITIONS
582
583
584#ifdef HILAPP
585 // this section is just to generate loop function calls for unary-. Will removed by hilapp
586 // from final code. If type T does not have -, give error
587
589 "BC possible only for types which implement unary "
590 "minus (-) -operator and are not unsigned");
591
592 ensure_unary_minus_is_loop_function<T>();
593#endif
594
595 // TODO: This works as intended only for periodic/antiperiodic b.c.
596 check_alloc();
597
598 lattice_struct *mylat = fs->mylattice.ptr();
599 fs->boundary_condition[dir] = bc;
600 fs->boundary_condition[-dir] = bc;
601#if !defined(CUDA) && !defined(HIP)
602 fs->neighbours[dir] = mylat->get_neighbour_array(dir, bc);
603 fs->neighbours[-dir] = mylat->get_neighbour_array(-dir, bc);
604#else
605 if (bc == hila::bc::PERIODIC) {
606 fs->payload.neighbours[dir] = mylat->backend_lattice->d_neighb[dir];
607 fs->payload.neighbours[-dir] = mylat->backend_lattice->d_neighb[-dir];
608 } else {
609 fs->payload.neighbours[dir] = mylat->backend_lattice->d_neighb_special[dir];
610 fs->payload.neighbours[-dir] = mylat->backend_lattice->d_neighb_special[-dir];
611 }
612#endif
613
614 // Make sure boundaries get refreshed
615 mark_changed(ALL);
616#else
617 assert(bc == hila::bc::PERIODIC &&
618 "Only periodic bondary conditions when SPECIAL_BOUNDARY_CONDITIONS is undefined");
619#endif
620 }
621
622 /**
623 * @brief Get the boundary condition of the Field
624 * @param dir Boundary condition in certain direction
625 * @return hila::bc The boundary condition of the Field
626 */
628#ifdef SPECIAL_BOUNDARY_CONDITIONS
629 return fs->boundary_condition[dir];
630#else
631 return hila::bc::PERIODIC;
632#endif
633 }
634
635 void print_boundary_condition() {
636 check_alloc();
637 hila::out0 << " ( ";
638 for (int dir = 0; dir < NDIRS; dir++) {
639 hila::out0 << (int)fs->boundary_condition[dir] << " ";
640 }
641 hila::out0 << ")\n";
642 }
643
644 /**
645 * @brief Copy the boundary condition from another field
646 *
647 * @tparam A the type of the field which we are copying from
648 * @param rhs the ohter Field
649 */
650 template <typename A>
652 foralldir(dir) {
654 }
655 }
656
657 // Overloading []
658 // declarations -- WILL BE implemented by hilapp, not written here
659 // let there be const and non-const protos
660 T operator[](const Parity p) const; // f[EVEN]
661 T operator[](const X_index_type) const; // f[X]
662 T operator[](const X_plus_direction p) const; // f[X+dir]
663 T operator[](const X_plus_offset p) const; // f[X+dir1+dir2] and others
664
665 T &operator[](const Parity p); // f[EVEN]
666 T &operator[](const X_index_type); // f[X]
667
668 T &operator[](const CoordinateVector &v); // f[CoordinateVector]
669 T &operator[](const CoordinateVector &v) const; // f[CoordinateVector]
670
671 // TEMPORARY HACK: return ptr to bare array
672 inline auto field_buffer() const {
673 return fs->payload.get_buffer();
674 }
675
676#ifndef VECTORIZED
677
678 /**
679 * @brief Get an individual element outside a loop. This is also used as a getter in the vanilla
680 * code.
681 *
682 * @param i
683 * @return auto
684 */
685 inline auto get_value_at(const unsigned i) const {
686 return fs->get_element(i);
687 }
688#else
689 inline auto get_value_at(const unsigned i) const {
690 return fs->get_element(i);
691 }
692 template <typename vecT>
693 inline auto get_vector_at(unsigned i) const {
694 return fs->template get_vector<vecT>(i);
695 }
696 inline auto get_value_at_nb_site(Direction d, unsigned i) const {
697 return fs->get_element(fs->vector_lattice->site_neighbour(d, i));
698 }
699#endif
700
701#ifndef VECTORIZED
702 /**
703 * @brief Set an individual element outside a loop. This is also used as a getter in the vanilla
704 * code.
705 *
706 * @param i
707 * @return auto
708 */
709 template <typename A>
710 inline void set_value_at(const A &value, unsigned i) {
711 fs->set_element(value, i);
712 }
713
714#else
715 template <typename vecT>
716 inline void set_vector_at(const vecT &value, unsigned i) {
717 fs->set_vector(value, i);
718 }
719
720 template <typename A>
721 inline void set_value_at(const A &value, unsigned i) {
722 fs->set_element(value, i);
723 }
724#endif
725
726 /**
727 * @brief Assignment operator.
728 *
729 * @details Assignment can be performed in the following ways:
730 *
731 * __Assignment from field:__
732 *
733 * Assignment from field is possible if types are the same or convertible
734 *
735 * \code {.cpp}
736 * f = g;
737 * \endcode
738 *
739 * __Assignment from scalar:__
740 *
741 * Assignment from scalar is possible if scalar is convertible to Field type
742 *
743 * \code {.cpp}
744 * f = a; // a is a scalar
745 * \endcode
746 *
747 * @param rhs
748 * @return Field<T>& Assigned field
749 */
751 (*this)[ALL] = rhs[X];
752
753 // // This is direct memcpy version of the routine, slightly faster on cpus
754 // if (this->fs == rhs.fs)
755 // return *this;
756 //
757 // assert(this->fs->mylattice == rhs.fs->mylattice &&
758 // "Cannot assign fields which belong to different lattices!");
759 //
760 // #if !defined(CUDA) && !defined(HIP)
761 // memcpy(this->field_buffer(), rhs.field_buffer(), sizeof(T) *
762 // lattice->mynode.volume);
763 // #else
764 // gpuMemcpy(this->field_buffer(), rhs.field_buffer(), sizeof(T) *
765 // lattice->mynode.volume,
766 // gpuMemcpyDeviceToDevice);
767 // #endif
768 // this->mark_changed(ALL);
769
770 return *this;
771 }
772
773 /**
774 * @internal
775 * @brief More general assignment operation if A can be casted into T
776 *
777 * @tparam A Type of element to be assigned
778 * @param rhs Field to assign from
779 * @return Field<T>& Assigned field
780 */
781 template <typename A,
782 std::enable_if_t<
783 hila::is_assignable<T &, A>::value || std::is_convertible<A, T>::value, int> = 0>
784 Field<T> &operator=(const Field<A> &rhs) {
785 (*this)[ALL] = rhs[X];
786 return *this;
787 }
788
789 /**
790 * @internal
791 * @brief Assginment from element
792 *
793 * @tparam A Type of element to be assigned
794 * @param d element to assign to Field
795 * @return Field<T>& Assigned Field
796 */
797 template <typename A,
798 std::enable_if_t<
799 hila::is_assignable<T &, A>::value || std::is_convertible<A, T>::value, int> = 0>
800 Field<T> &operator=(const A &d) {
801 (*this)[ALL] = d;
802 return *this;
803 }
804
805 /**
806 * @internal
807 * @brief assignment of 0 - nullptr, zero field
808 *
809 * @param z nullptr
810 * @return Field<T>& Zero Field
811 */
812 Field<T> &operator=(const std::nullptr_t &z) {
813 (*this)[ALL] = 0;
814 return *this;
815 }
816
817 /**
818 * @brief Move Assignment
819 *
820 * @param rhs
821 * @return Field<T>&
822 */
824 if (this != &rhs) {
825 assert((fs == nullptr || (rhs.fs->mylattice.ptr() == fs->mylattice.ptr())) &&
826 "Field = Field assignment possible only if fields belong to the same lattice");
827 clear();
828 fs = rhs.fs;
829 rhs.fs = nullptr;
830 }
831 return *this;
832 }
833
834 /**
835 * @brief Addition assignment operator
836 * @details Addition assignmen operator can be called in the following ways as long as types are
837 * convertible.
838 *
839 * __Addition assignment with field:__
840 *
841 * \code{.cpp}
842 * Field<MyType> f,g;
843 * . . .
844 * f += g;
845 * \endcode
846 *
847 * __Addition assignment with scalar:__
848 *
849 * \code{.cpp}
850 * Field<MyType> f;
851 * MyType a;
852 * . . .
853 * f += a;
854 * \endcode
855 *
856 * @tparam A Type of r.h.s element
857 * @param rhs Field to sum
858 * @return Field<T>&
859 */
860 template <typename A,
861 std::enable_if_t<std::is_convertible<hila::type_plus<T, A>, T>::value, int> = 0>
863 (*this)[ALL] += rhs[X];
864 return *this;
865 }
866
867 /**
868 * @brief Subtraction assignment operator
869 * @details Subtraction assignment operator can be called in the following ways as long as types
870 * are convertible.
871 *
872 * __Subtraction assignment with field:__
873 *
874 * \code{.cpp}
875 * Field<MyType> f,g;
876 * . . .
877 * f -= g;
878 * \endcode
879 *
880 * __Subtraction assignment with scalar:__
881 *
882 * \code{.cpp}
883 * Field<MyType> f;
884 * MyType a;
885 * . . .
886 * f -= a;
887 * \endcode
888 *
889 * @tparam A Type of r.h.s element
890 * @param rhs Field to subtract
891 * @return Field<T>&
892 */
893 template <typename A,
894 std::enable_if_t<std::is_convertible<hila::type_minus<T, A>, T>::value, int> = 0>
896 (*this)[ALL] -= rhs[X];
897 return *this;
898 }
899
900 /**
901 * @brief Product assignment operator
902 * @details Product assignment operator can be called in the following ways as long as types
903 * are convertible.
904 *
905 * __Product assignment with field:__
906 *
907 * \code{.cpp}
908 * Field<MyType> f,g;
909 * . . .
910 * f *= g;
911 * \endcode
912 *
913 * __Product assignment with scalar:__
914 *
915 * \code{.cpp}
916 * Field<MyType> f;
917 * MyType a;
918 * . . .
919 * f *= a;
920 * \endcode
921 *
922 * @tparam A Type of r.h.s element
923 * @param rhs Field to compute product with
924 * @return Field<T>&
925 */
926 template <typename A,
927 std::enable_if_t<std::is_convertible<hila::type_mul<T, A>, T>::value, int> = 0>
929 (*this)[ALL] *= rhs[X];
930 return *this;
931 }
932
933 /**
934 * @brief Division assignment operator
935 * @details Division assignment operator can be called in the following ways as long as types
936 * are convertible.
937 *
938 * __Division assignment with field:__
939 *
940 * \code{.cpp}
941 * Field<MyType> f,g;
942 * . . .
943 * f /= g;
944 * \endcode
945 *
946 * __Division assignment with scalar:__
947 *
948 * \code{.cpp}
949 * Field<MyType> f;
950 * MyType a;
951 * . . .
952 * f /= a;
953 * \endcode
954 *
955 * @tparam A Type of r.h.s element
956 * @param rhs Field to divide with
957 * @return Field<T>&
958 */
959 template <typename A,
960 std::enable_if_t<std::is_convertible<hila::type_div<T, A>, T>::value, int> = 0>
962 (*this)[ALL] /= rhs[X];
963 return *this;
964 }
965
966 /**
967 * @internal
968 * @brief += Operator between element and field if type of rhs and Field type T are compatible
969 *
970 * @tparam A Type of r.h.s element
971 * @param rhs Element to sum
972 * @return Field<T>&
973
974 */
975 template <typename A,
976 std::enable_if_t<std::is_convertible<hila::type_plus<T, A>, T>::value, int> = 0>
977 Field<T> &operator+=(const A &rhs) {
978 (*this)[ALL] += rhs;
979 return *this;
980 }
981
982 /**
983 * @internal
984 * @brief -= Operator between scalar and field if type of rhs and Field type T are compatible
985 *
986 * @tparam A Type of r.h.s element
987 * @param rhs Element to subtract
988 * @return Field<T>&
989 */
990 template <typename A,
991 std::enable_if_t<std::is_convertible<hila::type_minus<T, A>, T>::value, int> = 0>
992 Field<T> &operator-=(const A &rhs) {
993 (*this)[ALL] -= rhs;
994 return *this;
995 }
996
997 /**
998 * @internal
999 * @brief *= Operator between element and field if type of rhs and Field type T are compatible
1000 *
1001 * @tparam A Type of r.h.s element
1002 * @param rhs Element to multiply with
1003 * @return Field<T>&
1004 */
1005 template <typename A,
1006 std::enable_if_t<std::is_convertible<hila::type_mul<T, A>, T>::value, int> = 0>
1007 Field<T> &operator*=(const A &rhs) {
1008 (*this)[ALL] *= rhs;
1009 return *this;
1010 }
1011
1012 /**
1013 * @internal
1014 * @brief /= Operator between element and field if type of rhs and Field type T are compatible
1015 *
1016 * @tparam A Type of r.h.s element
1017 * @param rhs Element to divide with
1018 * @return Field<T>&
1019 */
1020 template <typename A,
1021 std::enable_if_t<std::is_convertible<hila::type_div<T, A>, T>::value, int> = 0>
1022 Field<T> &operator/=(const A &rhs) {
1023 (*this)[ALL] /= rhs;
1024 return *this;
1025 }
1026
1027 // Unary + and -
1028 /**
1029 * @memberof Field
1030 * @brief Summation operator
1031 * @details Summation operator can be called in the following ways as long as types are
1032 * convertible.
1033 *
1034 * __unary operator:__
1035 *
1036 * Acts as identity of field
1037 *
1038 * \code{.cpp}
1039 * Field<MyType> f,g;
1040 * . . .
1041 * f = +f;
1042 * \endcode
1043 *
1044 * __Summation with field:__
1045 *
1046 * \code{.cpp}
1047 * Field<MyType> f,g;
1048 * . . .
1049 * f = f + g;
1050 * \endcode
1051 *
1052 * __Summation with scalar:__
1053 *
1054 * \code{.cpp}
1055 * Field<MyType> f;
1056 * MyType a;
1057 * . . .
1058 * f = f + a;
1059 * \endcode
1060 *
1061 * @tparam A Type of lhs Field
1062 * @tparam B Type of rhs field
1063 * @param lhs First (left) Field
1064 * @param rhs Second (right) Field
1065 * @return Field<hila::type_plus<A, B>> Summed Field
1066 */
1068 return *this;
1069 }
1070
1071 /**
1072 * @memberof Field
1073 * @brief Subtraction operator
1074 * @details Subtraction operator can be called in the following ways as long as types are
1075 * convertible.
1076 *
1077 * __unary operator:__
1078 *
1079 * Acts as negation of field
1080 *
1081 * * \code{.cpp}
1082 * Field<MyType> f,g;
1083 * . . .
1084 * f = -f;
1085 * \endcode
1086 *
1087 * __Subtraction with field:__
1088 *
1089 * \code{.cpp}
1090 * Field<MyType> f,g;
1091 * . . .
1092 * f = f - g;
1093 * \endcode
1094 *
1095 * __Subtraction with scalar:__
1096 *
1097 * \code{.cpp}
1098 * Field<MyType> f;
1099 * MyType a;
1100 * . . .
1101 * f = f - a;
1102 * f = a - f;
1103 * \endcode
1104 *
1105 * @tparam A Type of lhs Field
1106 * @tparam B Type of rhs field
1107 * @param lhs First (left) Field
1108 * @param rhs Second (right) Field
1109 * @return Field<hila::type_plus<A, B>> Subtracted Field
1110 */
1112 Field<T> f;
1113 f[ALL] = -(*this)[X];
1114 return f;
1115 }
1116
1117 /**
1118 * @brief Field comparison operator.
1119 * @details Fields are equal if their content is element-by-element equal
1120 *
1121 * @param rhs Field to compare Field with
1122 * @return true
1123 * @return false
1124 */
1125 template <typename S>
1126 bool operator==(const Field<S> &rhs) const {
1127 double s = 0;
1128 onsites(ALL) s += !((*this)[X] == rhs[X]);
1129 return (s == 0);
1130 }
1131
1132 template <typename S>
1133 bool operator!=(const Field<S> &rhs) const {
1134 return !(*this == rhs);
1135 }
1136
1137
1138 /**
1139 * @brief Squarenorm
1140 * @details Squarenorm of Field \f$f\f$ is dependent on how it is defined for Field type T.
1141 *
1142 * \f$|f|^2 = \sum_{\forall x \in f} |x|^2\f$
1143 *
1144 * If squarenorm is defined for type T then the definition can be seen on the types
1145 * documentation page.
1146 * @return double
1147 */
1148 double squarenorm() const {
1149 double n = 0;
1150 onsites(ALL) {
1151 n += ::squarenorm((*this)[X]);
1152 }
1153 return n;
1154 }
1155
1156 /**
1157 * @brief Norm
1158 * @details Norm of Field depending on how it is defined for Field type T
1159 *
1160 * \f$|f| = \sqrt{\sum_{\forall x \in f} |x|^2}\f$
1161 *
1162 * If norm is defined for type T then the definition can be seen on the types
1163 * documentation page.
1164 * @return double
1165 */
1166 double norm() {
1167 return sqrt(squarenorm());
1168 }
1169
1170 /**
1171 * @brief Returns field with all elements conjugated depending how conjugate is defined for type
1172 *
1173 * @return Field<T>
1174 */
1175 Field<T> conj() const {
1176 Field<T> f;
1177 f[ALL] = ::conj((*this)[X]);
1178 return f;
1179 }
1180
1181 /**
1182 * @brief Returns dagger or Hermitian conjugate of Field depending on how it is
1183 * defined for Field type T
1184 *
1185 * @return Field<T>
1186 */
1187 template <typename R = T, typename A = decltype(::dagger(std::declval<R>()))>
1189 Field<A> f;
1190 f[ALL] = ::dagger((*this)[X]);
1191 return f;
1192 }
1193
1194 /**
1195 * @brief Returns real part of Field
1196 *
1197 * @tparam R Field current type
1198 * @tparam A Field real part type
1199 * @return Field<A>
1200 */
1201 template <typename R = T, typename A = decltype(::real(std::declval<R>()))>
1202 Field<A> real() const {
1203 Field<A> f;
1204 f[ALL] = ::real((*this)[X]);
1205 return f;
1206 }
1207
1208 /**
1209 * @brief Returns imaginary part of Field
1210 *
1211 * @tparam R Field current type
1212 * @tparam A Field imaginary part type
1213 * @return Field<A>
1214 */
1215 template <typename R = T, typename A = decltype(::imag(std::declval<R>()))>
1216 Field<A> imag() const {
1217 Field<A> f;
1218 f[ALL] = ::imag((*this)[X]);
1219 return f;
1220 }
1221
1222 // Communication routines. These are all internal.
1224 void wait_gather(Direction d, Parity p) const;
1225 void gather(Direction d, Parity p = ALL) const;
1226 void drop_comms(Direction d, Parity p) const;
1227
1228 /**
1229 * @brief Create a periodically shifted copy of the field
1230 * @details this is currently OK only for short moves, very inefficient for longer moves
1231 *
1232 * @code{.cpp}
1233 * .
1234 * . //Field<MyType> f is defined
1235 * .
1236 * CoordinateVector v = {1,1,0}
1237 * f.shift(v) // Shift all elements of field once in the x direction and once in the y direction
1238 * f.shift(v,EVEN) // Shift all EVEN elements of field once in the x direction and once in the y
1239 * direction
1240 * @endcode
1241 * @param v
1242 * @param par
1243 * @param r
1244 * @return Field<T>& returns a reference to res
1245 */
1246 Field<T> &shift(const CoordinateVector &v, Field<T> &r, Parity par) const;
1247
1248 Field<T> &shift(const CoordinateVector &v, Field<T> &r) const {
1249 return shift(v, r, ALL);
1250 }
1251
1252 Field<T> shift(const CoordinateVector &v) const;
1253
1254 // General getters and setters
1255
1256 /// Set a single element. Assuming that each node calls this with the same value, it
1257 /// is sufficient to set the element locally
1258
1259 /**
1260 * @brief Get singular element which will be broadcast to all nodes
1261 * @details Works as long as `value` type A and field type T match
1262 * @tparam A type
1263 * @param coord Coordinate of element to be set
1264 * @param value Value to be set
1265 * @return const T
1266 */
1267 template <typename A, std::enable_if_t<std::is_assignable<T &, A>::value, int> = 0>
1268 const T set_element(const CoordinateVector &coord, const A &value) {
1269 T element;
1270 element = value;
1271 assert(is_initialized(ALL) && "Field not initialized yet");
1272 if (fs->mylattice->is_on_mynode(coord)) {
1273 set_value_at(element, fs->mylattice->site_index(coord));
1274 }
1275 mark_changed(coord.parity());
1276 return element;
1277 }
1278
1279 /**
1280 * @brief Get singular element which will be broadcast to all nodes
1281 *
1282 * @param coord coordinate of fetched element
1283 * @return const T
1284 */
1285 const T get_element(const CoordinateVector &coord) const {
1286 T element;
1287
1288 assert(is_initialized(ALL) && "Field not initialized yet");
1289 int owner = fs->mylattice->node_rank(coord);
1290
1291 if (hila::myrank() == owner) {
1292 element = get_value_at(fs->mylattice->site_index(coord));
1293 }
1294
1295 hila::broadcast(element, owner);
1296
1297 return element;
1298 }
1299
1300 /**
1301 * @internal
1302 * @brief Set an array of elements in the field
1303 * @remark Assuming that each node calls this with the same value,
1304 * it is sufficient to set the elements locally
1305 *
1306 * @param elements @typedef vector<T> of elements to set
1307 * @param coord_list @typedef vector<CoordinateVector> of coordinates to set
1308 */
1309 void set_elements(const std::vector<T> &elements,
1310 const std::vector<CoordinateVector> &coord_list);
1311 /**
1312 * @internal
1313 * @brief Retrieves list of elements to all nodes.
1314 * @param coord_list vector of coordinates which will be fetched
1315 * @param broadcast if true then elements retrieved to root node will be broadcast to all nodes
1316 * @return std::vector<T> list of all elements
1317 */
1318 std::vector<T> get_elements(const std::vector<CoordinateVector> &coord_list,
1319 bool broadcast = false) const;
1320
1321 std::vector<T> get_subvolume(const CoordinateVector &cmin, const CoordinateVector &cmax,
1322 bool broadcast = false) const;
1323
1324 std::vector<T> get_slice(const CoordinateVector &c, bool broadcast = false) const;
1325
1326 void copy_local_data(std::vector<T> &buffer) const;
1327 void set_local_data(const std::vector<T> &buffer);
1328
1329 void copy_local_data_with_halo(std::vector<T> &buffer) const;
1330
1331
1332 // inline void set_element_at(const CoordinateVector &coord, const A &elem) {
1333 // T e;
1334 // e = elem;
1335 // set_element(e, coord);
1336 // }
1337
1338 // inline void set_element_at(const CoordinateVector &coord, std::nullptr_t elem) {
1339 // T e;
1340 // e = 0;
1341 // set_element(e, coord);
1342 // }
1343
1344 /// @internal
1345 /// Compound element ops - used in field element operations like F[cv] += smth;
1346 /// These are optimized so that do NOT return a value, thus cannot be chained. This avoids MPI.
1347 template <typename A,
1348 std::enable_if_t<std::is_assignable<T &, hila::type_plus<T, A>>::value, int> = 0>
1349 inline void compound_add_element(const CoordinateVector &coord, const A &av) {
1350 assert(is_initialized(ALL));
1351 if (fs->mylattice->is_on_mynode(coord)) {
1352 auto i = fs->mylattice->site_index(coord);
1353 auto v = get_value_at(i);
1354 v += av;
1355 set_value_at(v, i);
1356 }
1357 mark_changed(coord.parity());
1358 }
1359
1360 template <typename A,
1361 std::enable_if_t<std::is_assignable<T &, hila::type_minus<T, A>>::value, int> = 0>
1362 inline void compound_sub_element(const CoordinateVector &coord, const A &av) {
1363 assert(is_initialized(ALL));
1364 if (fs->mylattice->is_on_mynode(coord)) {
1365 auto i = fs->mylattice->site_index(coord);
1366 auto v = get_value_at(i);
1367 v -= av;
1368 set_value_at(v, i);
1369 }
1370 mark_changed(coord.parity());
1371 }
1372
1373 template <typename A,
1374 std::enable_if_t<std::is_assignable<T &, hila::type_mul<T, A>>::value, int> = 0>
1375 inline void compound_mul_element(const CoordinateVector &coord, const A &av) {
1376 assert(is_initialized(ALL));
1377 if (fs->mylattice->is_on_mynode(coord)) {
1378 auto i = fs->mylattice->site_index(coord);
1379 auto v = get_value_at(i);
1380 v *= av;
1381 set_value_at(v, i);
1382 }
1383 mark_changed(coord.parity());
1384 }
1385
1386 template <typename A,
1387 std::enable_if_t<std::is_assignable<T &, hila::type_div<T, A>>::value, int> = 0>
1388 inline void compound_div_element(const CoordinateVector &coord, const A &av) {
1389 assert(is_initialized(ALL));
1390 if (fs->mylattice->is_on_mynode(coord)) {
1391 auto i = fs->mylattice->site_index(coord);
1392 auto v = get_value_at(i);
1393 v /= av;
1394 set_value_at(v, i);
1395 }
1396 mark_changed(coord.parity());
1397 }
1398
1399
1400 // pre- and postfix ++ -- return value
1401 ///@internal
1402 inline const T increment_postfix_element(const CoordinateVector &coord) {
1403 T r, v;
1404 v = get_element(coord);
1405 r = v;
1406 v++;
1407 set_element(coord, v);
1408 return r;
1409 }
1410
1411 ///@internal
1412 inline const T increment_prefix_element(const CoordinateVector &coord) {
1413 T v = get_element(coord);
1414 ++v;
1415 set_element(coord, v);
1416 return v;
1417 }
1418
1419 ///@internal
1420 inline const T decrement_postfix_element(const CoordinateVector &coord) {
1421 T r, v;
1422 v = get_element(coord);
1423 r = v;
1424 v--;
1425 set_element(coord, v);
1426 return r;
1427 }
1428
1429 ///@internal
1430 inline const T decrement_prefix_element(const CoordinateVector &coord) {
1431 T v = get_element(coord);
1432 --v;
1433 set_element(coord, v);
1434 return v;
1435 }
1436
1437
1438 // Fourier transform declaration
1439 Field<T> FFT(fft_direction fdir = fft_direction::forward) const;
1440 Field<T> FFT(const CoordinateVector &dirs, fft_direction fdir = fft_direction::forward) const;
1441
1443 FFT_real_to_complex(fft_direction fdir = fft_direction::forward) const;
1445 FFT_complex_to_real(fft_direction fdir = fft_direction::forward) const;
1446
1447
1448 // Reflect the field along all or 1 coordinate
1449 Field<T> reflect() const;
1450 Field<T> reflect(Direction dir) const;
1451 Field<T> reflect(const CoordinateVector &dirs) const;
1452
1453 // Writes the Field to disk
1454 void write(std::ofstream &outputfile, bool binary = true, int precision = 8) const;
1455 void write(const std::string &filename, bool binary = true, int precision = 8) const;
1456
1457 void read(std::ifstream &inputfile);
1458 void read(std::ifstream &inputfile, const CoordinateVector &insize);
1459 void read(const std::string &filename);
1460
1461 void write_subvolume(std::ofstream &outputfile, const CoordinateVector &cmin,
1462 const CoordinateVector &cmax, int precision = 6) const;
1463 void write_subvolume(const std::string &filenname, const CoordinateVector &cmin,
1464 const CoordinateVector &cmax, int precision = 6) const;
1465
1466 template <typename Out>
1467 void write_slice(Out &outputfile, const CoordinateVector &slice, int precision = 6) const;
1468
1469 /**
1470 * @brief Sum reduction of Field
1471 * @details The sum in the reduction is defined by the Field type T
1472 * @param par Parity
1473 * @param allreduce Switch to turn on or off MPI allreduce
1474 * @return T Field element type reduced to one element
1475 */
1476 T sum(Parity par = Parity::all, bool allreduce = true) const;
1477
1478 /**
1479 * @brief Product reduction of Field
1480 * @details The product in the reduction is defined by the Field type T.
1481 * @param par Parity
1482 * @param allreduce Switch to turn on or off MPI allreduce
1483 * @return T Field element type reduced to one element
1484 */
1485 T product(Parity par = Parity::all, bool allreduce = true) const;
1486
1487 /**
1488 * @brief Declare gpu_reduce here, defined only for GPU targets
1489 * @internal
1490 * @param min_or_max
1491 * @param par
1492 * @param loc
1493 * @return T
1494 */
1495 T gpu_minmax(bool min_or_max, Parity par, CoordinateVector &loc) const;
1496
1497 /**
1498 * @brief Minimum value of Field. If CoordinateVector is passed to function
1499 * then location of minimum value will be stored in said CoordinateVector
1500 * @name Min functions
1501 * @param par ::Parity
1502 * @return T Minimum value of Field
1503 */
1504 /** @{ */
1505 T min(Parity par = ALL) const;
1506 T min(CoordinateVector &loc) const;
1507 T min(Parity par, CoordinateVector &loc) const;
1508 /** @} */
1509
1510 /**
1511 * @brief Maximum value of Field. If CoordinateVector is passed to function
1512 * then location of maximum value will be stored in said CoordinateVector
1513 * @name Max functions
1514 * @param par ::Parity
1515 * @return T Minimum value of Field
1516 */
1517 /** @{ */
1518 T max(Parity par = ALL) const;
1519 T max(CoordinateVector &loc) const;
1520 T max(Parity par, CoordinateVector &loc) const;
1521 /** @} */
1522
1523 /**
1524 * @brief Function to perform min or max operations
1525 * @internal
1526 *
1527 * @param is_min if true we compute min
1528 * @param par Parity
1529 * @param loc Location of min or max
1530 * @return T
1531 */
1532 T minmax(bool is_min, Parity par, CoordinateVector &loc) const;
1533
1534 void random();
1535 void gaussian_random(double width = 1.0);
1536
1537
1538 /**
1539 * @brief Fill blocked lattice Field from parent lattice Field (see lattice.block())
1540 *
1541 * @details
1542 * If Field<T> a belongs to original (parent) lattice and Field<T> belongs to blocked
1543 * lattice, the operation
1544 * @code{.cpp}
1545 * b.block_from(a);
1546 * @endcode
1547 * copies data from the sparse (blocked) sites of a to b.
1548 * @example
1549 * @code{.cpp}
1550 * ...
1551 *
1552 * Field<Complex<double>> df;
1553 * df.gaussian_random();
1554 * smear(df,n); // do n times semaring op (assuming smear() exists)
1555 *
1556 * lattice.block({2,2,2});
1557 * Field<Complex<double>> dfb1;
1558 * dfb1.block_from(df);
1559 * smear(dfb1,n);
1560 *
1561 * lattice.block({2,2,2});
1562 * Field<Complex<double>> dfb2;
1563 * dfb2.block_from(dfb1);
1564 * smear(dfb2,n);
1565 *
1566 * // analyse dfb2 here ...
1567 *
1568 * lattice.switch_to_base(); // undo all blocking
1569 * @endcode
1570 *
1571 * The sites which are blocked can be accessed with the CoordinateVector function is_divisible:
1572 * @code{.cpp}
1573 * CoordinateVector factor = {2,2,2};
1574 * onsites(ALL) {
1575 * if (X.coordinates().is_divisible(factor)) {
1576 * // now we're on site which will be copied by .block_from()
1577 * ...
1578 * }
1579 * }
1580 * @endcode
1581 *
1582 */
1583 void block_from(Field<T> &orig);
1584
1585 /**
1586 * @brief Copy content to the argument Field on blocked (sparse) sites.
1587 * a.unblock_to(b) is the inverse of a.block_from(b)
1588 *
1589 * Leaves other sites of the argument Field unmodified.
1590 */
1591 void unblock_to(Field<T> &target) const;
1592
1593 /**
1594 * @brief Return the lattice to which this field instance belongs to.
1595 * @details Useful for switching lattices,for example
1596 * lattice.switch_to(a.mylattice());
1597 * changes active lattice to the one to which Field variable a belongs to.
1598 */
1600 return fs->mylattice;
1601 }
1602
1603
1604}; // End of class Field<>
1605
1606///////////////////////////////
1607// operators +-*/
1608// these operators rely on SFINAE, OK if field_hila::type_plus<A,B> exists i.e. A+B is
1609// OK
1610// There are several versions of the operators, depending if one of the arguments is
1611// Field or scalar, and if the return type is the same as the Field argument. This can
1612// enable the compiler to avoid extra copies of the args
1613
1614// operator + (Field + Field) -generic
1615
1616template <typename A, typename B>
1617auto operator+(const Field<A> &lhs, const Field<B> &rhs) -> Field<hila::type_plus<A, B>> {
1619 tmp[ALL] = lhs[X] + rhs[X];
1620 return tmp;
1621}
1622
1623//////////////////////////////
1624// operator + (Field + scalar)
1625
1626template <typename A, typename B>
1627auto operator+(const Field<A> &lhs, const B &rhs) -> Field<hila::type_plus<A, B>> {
1629 tmp[ALL] = lhs[X] + rhs;
1630 return tmp;
1631}
1632
1633//////////////////////////////
1634/// operator + (scalar + Field)
1635
1636template <typename A, typename B>
1637auto operator+(const A &lhs, const Field<B> &rhs) -> Field<hila::type_plus<A, B>> {
1638 return rhs + lhs;
1639}
1640
1641//////////////////////////////
1642// operator - Field - Field -generic
1643
1644template <typename A, typename B>
1645auto operator-(const Field<A> &lhs, const Field<B> &rhs) -> Field<hila::type_minus<A, B>> {
1647 tmp[ALL] = lhs[X] - rhs[X];
1648 return tmp;
1649}
1650
1651//////////////////////////////
1652/// operator - (Field - scalar)
1653
1654template <typename A, typename B>
1655auto operator-(const Field<A> &lhs, const B &rhs) -> Field<hila::type_minus<A, B>> {
1657 tmp[ALL] = lhs[X] - rhs;
1658 return tmp;
1659}
1660
1661//////////////////////////////
1662// operator - (scalar - Field)
1663
1664template <typename A, typename B>
1665auto operator-(const A &lhs, const Field<B> &rhs) -> Field<hila::type_minus<A, B>> {
1667 tmp[ALL] = lhs - rhs[X];
1668 return tmp;
1669}
1670
1671///////////////////////////////
1672// operator * (Field * Field)
1673// generic
1674/**
1675 * @memberof Field
1676 * @brief Multiplication operator
1677 * @details Multiplication operator can be called in the following ways as long as types are
1678 * convertible.
1679 *
1680 * __Multiplication with field:__
1681 *
1682 * \code{.cpp}
1683 * Field<MyType> f,g;
1684 * . . .
1685 * f = f * g;
1686 * \endcode
1687 *
1688 * __Multiplication with scalar:__
1689 *
1690 * \code{.cpp}
1691 * Field<MyType> f;
1692 * MyType a;
1693 * . . .
1694 * f = f * a;
1695 * f = a * f;
1696 * \endcode
1697 *
1698 * @tparam A Type of lhs Field
1699 * @tparam B Type of rhs field
1700 * @param lhs First (left) Field
1701 * @param rhs Second (right) Field
1702 * @return Field<hila::type_plus<A, B>> Multiplied Field
1703 */
1704template <typename A, typename B>
1705auto operator*(const Field<A> &lhs, const Field<B> &rhs) -> Field<hila::type_mul<A, B>> {
1707 tmp[ALL] = lhs[X] * rhs[X];
1708 return tmp;
1709}
1710
1711/////////////////////////////////
1712// operator * (scalar * field)
1713
1714template <typename A, typename B>
1715auto operator*(const A &lhs, const Field<B> &rhs) -> Field<hila::type_mul<A, B>> {
1717 tmp[ALL] = lhs * rhs[X];
1718 return tmp;
1719}
1720
1721/////////////////////////////////
1722// operator * (field * scalar)
1723
1724template <typename A, typename B>
1725auto operator*(const Field<A> &lhs, const B &rhs) -> Field<hila::type_mul<A, B>> {
1727 tmp[ALL] = lhs[X] * rhs;
1728 return tmp;
1729}
1730
1731///////////////////////////////
1732// operator / (Field / Field)
1733// generic
1734/**
1735 * @memberof Field
1736 * @brief Division operator
1737 * @details Division operator can be called in the following ways as long as types are
1738 * convertible.
1739 *
1740 * __Division with field:__
1741 *
1742 * \code{.cpp}
1743 * Field<MyType> f,g;
1744 * . . .
1745 * f = f / g;
1746 * \endcode
1747 *
1748 * __Division with scalar:__
1749 *
1750 * \code{.cpp}
1751 * Field<MyType> f;
1752 * MyType a;
1753 * . . .
1754 * f = f / a;
1755 * f = a / f;
1756 * \endcode
1757 *
1758 * @tparam A Type of lhs Field
1759 * @tparam B Type of rhs field
1760 * @param lhs First (left) Field
1761 * @param rhs Second (right) Field
1762 * @return Field<hila::type_plus<A, B>> Subtracted Field
1763 */
1764template <typename A, typename B>
1767 tmp[ALL] = l[X] / r[X];
1768 return tmp;
1769}
1770
1771
1772//////////////////////////////////
1773// operator / (scalar/Field)
1774template <typename A, typename B>
1775auto operator/(const A &lhs, const Field<B> &rhs) -> Field<hila::type_div<A, B>> {
1777 tmp[ALL] = lhs / rhs[X];
1778 return tmp;
1779}
1780
1781//////////////////////////////////
1782// operator / (Field/scalar)
1783template <typename A, typename B>
1784auto operator/(const Field<A> &lhs, const B &rhs) -> Field<hila::type_div<A, B>> {
1786 tmp[ALL] = lhs[X] / rhs;
1787 return tmp;
1788}
1789
1790namespace hila {
1791
1792/**
1793 * @brief Implement hila::swap() for Fields too, equivalent to std::swap()
1794 */
1795template <typename T>
1796void swap(Field<T> &A, Field<T> &B) {
1797 std::swap(A.fs, B.fs);
1798}
1799} // namespace hila
1800
1801
1802///////////////////////////////////////////////////////////////////////
1803// Allow some arithmetic functions if implemented
1804
1805
1806/**
1807 * @name Mathematical methods.
1808 * @tparam T Field element type of arg
1809 * @tparam R Field return type
1810 * @param arg input Field
1811 * @return Field<R>
1812 * @memberof Field
1813 * @details See [field documentation](@ref mathematical_methods_field)
1814 *
1815 * @{
1816 */
1817
1818/**
1819 * @brief Exponential
1820 */
1821template <typename T, typename R = decltype(exp(std::declval<T>()))>
1823 Field<R> res;
1824 onsites(ALL) {
1825 res[X] = exp(arg[X]);
1826 }
1827 return res;
1828}
1829
1830/**
1831 * @brief Logarithm
1832 */
1833template <typename T, typename R = decltype(log(std::declval<T>()))>
1835 Field<R> res;
1836 onsites(ALL) {
1837 res[X] = log(arg[X]);
1838 }
1839 return res;
1840}
1841
1842/**
1843 * @brief Sine
1844 */
1845template <typename T, typename R = decltype(sin(std::declval<T>()))>
1847 Field<R> res;
1848 onsites(ALL) {
1849 res[X] = sin(arg[X]);
1850 }
1851 return res;
1852}
1853
1854/**
1855 * @brief Cosine
1856 */
1857template <typename T, typename R = decltype(cos(std::declval<T>()))>
1859 Field<R> res;
1860 onsites(ALL) {
1861 res[X] = cos(arg[X]);
1862 }
1863 return res;
1864}
1865
1866/**
1867 * @brief Tangent
1868 */
1869template <typename T, typename R = decltype(tan(std::declval<T>()))>
1871 Field<R> res;
1872 onsites(ALL) {
1873 res[X] = tan(arg[X]);
1874 }
1875 return res;
1876}
1877
1878/**
1879 * @brief Arcsine
1880 */
1881template <typename T, typename R = decltype(asin(std::declval<T>()))>
1883 Field<R> res;
1884 onsites(ALL) {
1885 res[X] = asin(arg[X]);
1886 }
1887 return res;
1888}
1889
1890/**
1891 * @brief Arccosine
1892 */
1893template <typename T, typename R = decltype(acos(std::declval<T>()))>
1895 Field<R> res;
1896 onsites(ALL) {
1897 res[X] = acos(arg[X]);
1898 }
1899 return res;
1900}
1901
1902/**
1903 * @brief Arctangent
1904 */
1905template <typename T, typename R = decltype(atan(std::declval<T>()))>
1907 Field<R> res;
1908 onsites(ALL) {
1909 res[X] = atan(arg[X]);
1910 }
1911 return res;
1912}
1913
1914/**
1915 * @brief Absolute value
1916 */
1917template <typename T, typename R = decltype(abs(std::declval<T>()))>
1919 Field<R> res;
1920 onsites(ALL) {
1921 res[X] = abs(arg[X]);
1922 }
1923 return res;
1924}
1925
1926/**
1927 * @brief Power
1928 * @param p exponent to which Field element is raised to.
1929 */
1930template <typename T, typename P, typename R = decltype(pow(std::declval<T>()), std::declval<P>())>
1931Field<R> pow(const Field<T> &arg, const P p) {
1932 Field<R> res;
1933 onsites(ALL) {
1934 res[X] = pow(arg[X], p);
1935 }
1936 return res;
1937}
1938
1939/**
1940 * @brief Squared norm \f$|f|^2\f$
1941 */
1942template <typename T>
1943double squarenorm(const Field<T> &arg) {
1944 double r = 0;
1945 onsites(ALL) {
1946 r += squarenorm(arg[X]);
1947 }
1948 return r;
1949}
1950
1951
1952template <typename T>
1953double norm(const Field<T> &arg) {
1954 return sqrt(squarenorm(arg));
1955}
1956
1957template <typename T>
1958Field<T> conj(const Field<T> &arg) {
1959 return arg.conj();
1960}
1961
1962template <typename T, typename A = decltype(::dagger(std::declval<T>()))>
1963Field<A> dagger(const Field<T> &arg) {
1964 return arg.dagger();
1965}
1966
1967template <typename T, typename A = decltype(::real(std::declval<T>()))>
1968Field<A> real(const Field<T> &arg) {
1969 return arg.real();
1970}
1971
1972template <typename T, typename A = decltype(::imag(std::declval<T>()))>
1973Field<A> imag(const Field<T> &arg) {
1974 return arg.imag();
1975}
1976
1977/**
1978 * @brief Squarenorm relative \f$|a-b|^2\f$
1979 *
1980 * @tparam A Element type of Field a
1981 * @tparam B Element type of Field b
1982 * @param a Field a
1983 * @param b Field b
1984 * @return double
1985 */
1986template <typename A, typename B, typename R = decltype(std::declval<A>() - std::declval<B>())>
1987double squarenorm_relative(const Field<A> &a, const Field<B> &b) {
1988 double res = 0;
1989 onsites(ALL) {
1990 res += squarenorm(a[X] - b[X]);
1991 }
1992 return res;
1993}
1994
1995/// @}
1996
1997template <typename T>
1999 Field<T> res;
2000 shift(v, res, ALL);
2001 return res;
2002}
2003
2004
2005/// @internal
2006/// drop_comms(): if field is changed or deleted, 'cancel' ongoing communications. Now just wait
2007/// for the communications to finish don't actually cancel them. Still separate this from using
2008/// only wait_gather since this needs to be called in ~Field() and we need to check if there are
2009/// ongoing communications. User gets nottified if there was redundant communications if
2010/// drop_comms_timer is in the run print out.
2011///
2012/// This should happen very seldom, only if there are "by-hand" start_gather operations and these
2013/// are not needed.
2014template <typename T>
2015void Field<T>::drop_comms(Direction d, Parity p) const {
2016
2017 if (hila::is_comm_initialized()) {
2018 if (is_gather_started(d, ALL)) {
2019 drop_comms_timer.start();
2020 wait_gather(d, ALL);
2021 drop_comms_timer.stop();
2022 }
2023 if (p != ALL) {
2024 if (is_gather_started(d, p)) {
2025 drop_comms_timer.start();
2026 wait_gather(d, p);
2027 drop_comms_timer.stop();
2028 }
2029 } else {
2030 if (is_gather_started(d, EVEN)) {
2031 drop_comms_timer.start();
2032 wait_gather(d, EVEN);
2033 drop_comms_timer.stop();
2034 }
2035 if (is_gather_started(d, ODD)) {
2036 drop_comms_timer.start();
2037 wait_gather(d, ODD);
2038 drop_comms_timer.stop();
2039 }
2040 }
2041 }
2042}
2043
2044
2045/// @internal And a convenience combi function
2046template <typename T>
2047void Field<T>::gather(Direction d, Parity p) const {
2048 start_gather(d, p);
2049 wait_gather(d, p);
2050}
2051
2052
2053// Read in the "technical" communication bits
2054
2055#include "field_comm.h"
2056
2057
2058template <typename T>
2059void Field<T>::random() {
2060
2061#if defined(CUDA) || defined(HIP)
2062
2063 if (!hila::is_device_rng_on()) {
2064
2065 std::vector<T> rng_buffer(lattice->mynode.volume);
2066 for (auto &element : rng_buffer)
2067 hila::random(element);
2068 (*this).set_local_data(rng_buffer);
2069
2070 } else {
2071 onsites(ALL) {
2072 hila::random((*this)[X]);
2073 }
2074 }
2075#else
2076
2077 onsites(ALL) {
2078 hila::random((*this)[X]);
2079 }
2080
2081#endif
2082}
2083
2084template <typename T>
2085void Field<T>::gaussian_random(double width) {
2086
2087#if defined(CUDA) || defined(HIP)
2088
2089 if (!hila::is_device_rng_on()) {
2090
2091 std::vector<T> rng_buffer(lattice->mynode.volume);
2092 for (auto &element : rng_buffer)
2093 hila::gaussian_random(element, width);
2094 (*this).set_local_data(rng_buffer);
2095
2096 } else {
2097 onsites(ALL) {
2098 hila::gaussian_random((*this)[X], width);
2099 }
2100 }
2101#else
2102
2103 onsites(ALL) {
2104 hila::gaussian_random((*this)[X], width);
2105 }
2106
2107#endif
2108}
2109
2110
2111#ifdef HILAPP
2112
2113////////////////////////////////////////////////////////////////////////////////
2114// A couple of placeholder functions, not included in produced code.
2115// These are here in order for hilapp to generate explicitly
2116// some Direction and CoordinateVector operations, which may not exist in
2117// original code as such. It is easiest to let the general hilapp
2118// code generation to do it using this hack, instead of hard-coding these to
2119// hilapp.
2120//
2121// These are needed because hilapp changes X+d-d -> +d-d, which may involve
2122// an operator not met before
2123
2124inline void dummy_X_f() {
2125 Direction d1 = e_x;
2126 CoordinateVector v1(0);
2127 onsites(ALL) {
2128 Direction d;
2129 d = +d1;
2130 d = -d1; // unaryops
2131 CoordinateVector vec;
2132 vec = +v1;
2133 vec = -v1;
2134
2135 // Direction + vector combos
2136 vec = d + d1;
2137 vec = d - d1;
2138 vec = v1 + d1;
2139 vec = v1 - d1;
2140 vec = d1 + v1;
2141 vec = d1 - v1;
2142 vec = vec + v1;
2143 vec = vec - v1;
2144
2145 // and Direction index func
2146 vec[e_x] = vec[0] + vec[e_y] + vec.e(e_y);
2147 }
2148}
2149
2150/**
2151 * @internal
2152 * @brief Dummy function including Field<T> functions and methods which
2153 * need to be explicitly seen by hilapp during 1st pass in order to
2154 * generater necessary functions. Add here ops as needed
2155 */
2156template <typename T>
2157inline void ensure_field_operators_exist() {
2158
2159 ensure_unary_minus_is_loop_function<T>();
2160 ensure_assign_zero_is_loop_function<T>();
2161
2162 // make shift also explicit
2163 CoordinateVector v = 0;
2164 Field<T> f;
2165 f = f.shift(v);
2166}
2167
2168#endif
2169
2170#endif // FIELD_H
Array< n, m, T > conj(const Array< n, m, T > &arg)
Return conjugate Array.
Definition array.h:675
Array< n, m, hila::arithmetic_type< T > > imag(const Array< n, m, T > &arg)
Return imaginary part of Array.
Definition array.h:703
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:1019
Array< n, m, hila::arithmetic_type< T > > real(const Array< n, m, T > &arg)
Return real part of Array.
Definition array.h:689
The field class implements the standard methods for accessing Fields. Hilapp replaces the parity acce...
Definition field.h:62
Field< T > & operator=(const Field< T > &rhs)
Assignment operator.
Definition field.h:750
Field()
Field constructor.
Definition field.h:278
Field< A > real() const
Returns real part of Field.
Definition field.h:1202
Field< R > acos(const Field< T > &arg)
Arccosine.
Definition field.h:1894
std::vector< T > get_subvolume(const CoordinateVector &cmin, const CoordinateVector &cmax, bool broadcast=false) const
Get a subvolume of the field elements to all nodes.
Definition field_comm.h:675
Field< T > & shift(const CoordinateVector &v, Field< T > &r, Parity par) const
Create a periodically shifted copy of the field.
Definition field_comm.h:162
void set_boundary_condition(Direction dir, hila::bc bc)
Set the boundary condition in a given Direction (periodic or antiperiodic)
Definition field.h:579
void write_subvolume(std::ofstream &outputfile, const CoordinateVector &cmin, const CoordinateVector &cmax, int precision=6) const
Definition field_io.h:280
const T get_element(const CoordinateVector &coord) const
Get singular element which will be broadcast to all nodes.
Definition field.h:1285
hila::bc get_boundary_condition(Direction dir) const
Get the boundary condition of the Field.
Definition field.h:627
Field< A > imag() const
Returns imaginary part of Field.
Definition field.h:1216
Lattice mylattice() const
Return the lattice to which this field instance belongs to.
Definition field.h:1599
T gpu_minmax(bool min_or_max, Parity par, CoordinateVector &loc) const
Declare gpu_reduce here, defined only for GPU targets.
Definition gpu_minmax.h:29
Field< A > dagger() const
Returns dagger or Hermitian conjugate of Field depending on how it is defined for Field type T.
Definition field.h:1188
const T set_element(const CoordinateVector &coord, const A &value)
Get singular element which will be broadcast to all nodes.
Definition field.h:1268
T max(Parity par=ALL) const
Find maximum value from Field.
Definition reduction.h:413
Field< T > operator+() const
Summation operator.
Definition field.h:1067
auto operator*(const Field< A > &lhs, const Field< B > &rhs) -> Field< hila::type_mul< A, B > >
Multiplication operator.
Definition field.h:1705
double norm()
Norm.
Definition field.h:1166
bool is_initialized(Parity p) const
Returns true if the Field has been assigned to.
Definition field.h:430
Field< R > log(const Field< T > &arg)
Logarithm.
Definition field.h:1834
auto get_value_at(const unsigned i) const
Get an individual element outside a loop. This is also used as a getter in the vanilla code.
Definition field.h:685
Field< R > pow(const Field< T > &arg, const P p)
Power.
Definition field.h:1931
field_struct *__restrict__ fs
Field::fs holds all of the field content in Field::field_struct.
Definition field.h:244
void clear()
Destroys field data.
Definition field.h:406
Field< R > tan(const Field< T > &arg)
Tangent.
Definition field.h:1870
void set_value_at(const A &value, unsigned i)
Set an individual element outside a loop. This is also used as a getter in the vanilla code.
Definition field.h:710
double squarenorm_relative(const Field< A > &a, const Field< B > &b)
Squarenorm relative .
Definition field.h:1987
Field< T > operator-() const
Subtraction operator.
Definition field.h:1111
Field< R > abs(const Field< T > &arg)
Absolute value.
Definition field.h:1918
auto operator/(const Field< A > &l, const Field< B > &r) -> Field< hila::type_div< A, B > >
Division operator.
Definition field.h:1765
Field< R > asin(const Field< T > &arg)
Arcsine.
Definition field.h:1882
double squarenorm(const Field< T > &arg)
Squared norm .
Definition field.h:1943
Field< Complex< hila::arithmetic_type< T > > > FFT_real_to_complex(fft_direction fdir=fft_direction::forward) const
Definition fft.h:474
Field< R > sin(const Field< T > &arg)
Sine.
Definition field.h:1846
Field< R > atan(const Field< T > &arg)
Arctangent.
Definition field.h:1906
Field< T > & operator=(Field< T > &&rhs)
Move Assignment.
Definition field.h:823
Field< T > & operator*=(const Field< A > &rhs)
Product assignment operator.
Definition field.h:928
Field< R > cos(const Field< T > &arg)
Cosine.
Definition field.h:1858
bool is_allocated() const
Returns true if the Field data has been allocated.
Definition field.h:421
void copy_boundary_condition(const Field< A > &rhs)
Copy the boundary condition from another field.
Definition field.h:651
Field< T > conj() const
Returns field with all elements conjugated depending how conjugate is defined for type.
Definition field.h:1175
T product(Parity par=Parity::all, bool allreduce=true) const
Product reduction of Field.
Definition reduction.h:296
void check_alloc()
Allocate Field if it is not already allocated.
Definition field.h:458
Field< R > exp(const Field< T > &arg)
Exponential.
Definition field.h:1822
bool operator==(const Field< S > &rhs) const
Field comparison operator.
Definition field.h:1126
double squarenorm() const
Squarenorm.
Definition field.h:1148
dir_mask_t start_gather(Direction d, Parity p=ALL) const
Definition field_comm.h:262
Field< T > & operator-=(const Field< A > &rhs)
Subtraction assignment operator.
Definition field.h:895
void write(std::ofstream &outputfile, bool binary=true, int precision=8) const
Write the field to a file stream.
Definition field_io.h:79
std::vector< T > get_elements(const std::vector< CoordinateVector > &coord_list, bool broadcast=false) const
Get a list of elements and store them into a vector.
Definition field_comm.h:658
T minmax(bool is_min, Parity par, CoordinateVector &loc) const
Function to perform min or max operations.
Definition reduction.h:319
T min(Parity par=ALL) const
Find minimum value from Field.
Definition reduction.h:393
T sum(Parity par=Parity::all, bool allreduce=true) const
Sum reduction of Field.
Definition reduction.h:287
void read(std::ifstream &inputfile)
Read the Field from a stream.
Definition field_io.h:148
std::vector< T > get_slice(const CoordinateVector &c, bool broadcast=false) const
Get a slice (subvolume)
Definition field_comm.h:696
void unblock_to(Field< T > &target) const
Copy content to the argument Field on blocked (sparse) sites. a.unblock_to(b) is the inverse of a....
Definition field_comm.h:971
Field< T > & operator/=(const Field< A > &rhs)
Division assignment operator.
Definition field.h:961
Field< T > & operator+=(const Field< A > &rhs)
Addition assignment operator.
Definition field.h:862
main interface class to lattices.
Definition lattice.h:396
lattice_struct * ptr() const
get non-const pointer to lattice_struct (cf. operator ->)
Definition lattice.h:474
T e(const int i, const int j) const
Standard array indexing operation for matrices.
Definition matrix.h:279
X-coordinate type - "dummy" class.
The field_storage struct contains minimal information for using the field in a loop....
auto get_element(const unsigned i, const Lattice lattice) const
Conditionally reture bool type false if type T does't have unary - operator.
unsigned *__restrict__ neighb[NDIRS]
Main neighbour index array.
Definition lattice.h:213
Complex< T > dagger(const Complex< T > &val)
Return dagger of Complex number.
Definition cmplx.h:1302
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1266
T arg(const Complex< T > &a)
Return argument of Complex number.
Definition cmplx.h:1278
This header file defines:
Parity
Parity enum with values EVEN, ODD, ALL; refers to parity of the site. Parity of site (x,...
constexpr Parity EVEN
bit pattern: 001
#define foralldir(d)
Macro to loop over (all) Direction(s)
Definition coordinates.h:80
constexpr unsigned NDIRS
Number of directions.
Definition coordinates.h:57
constexpr Parity ODD
bit pattern: 010
unsigned char dir_mask_t
Direction
Enumerator for direction that assigns integer to direction to be interpreted as unit vector.
Definition coordinates.h:34
constexpr Parity ALL
bit pattern: 011
This file defines all includes for HILA.
fft_direction
define a class for FFT direction
Definition defs.h:159
#define RESTRICT
Definition defs.h:52
Implement hila::swap for gauge fields.
Definition array.h:982
T gaussian_random()
Template function T hila::gaussian_random<T>(),generates gaussian random value of type T,...
Definition random.h:179
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:121
int myrank()
rank of this node
Definition com_mpi.cpp:237
bool is_device_rng_on()
Check if the RNG on GPU is allocated and ready to use.
Definition hila_gpu.cpp:58
std::ostream out0
This writes output only from main process (node 0)
bc
list of field boundary conditions - used only if SPECIAL_BOUNDARY_CONDITIONS defined
Definition lattice.h:31
T broadcast(T &var, int rank=0)
Broadcast the value of var to all MPI ranks from rank (default=0).
Definition com_mpi.h:170
bool bc_need_communication(hila::bc bc)
False if we have b.c. which does not require communication.
Definition lattice.h:34
T gaussian_random(out_only T &val, double w=1.0)
Template function const T & hila::gaussian_random(T & variable,double width=1)
Definition random.h:153
X + coordinate offset, used in f[X+CoordinateVector] or f[X+dir1+dir2] etc.
vectorized_lattice_struct< vector_size > * get_vectorized_lattice()
Returns a vectorized lattice with given vector size.
unsigned * d_neighb[NDIRS]
Storage for the neighbour indexes. Stored on device.
is_vectorizable_type<T>::value is always false if the target is not vectorizable
Definition vector_types.h:8
Information necessary to communicate with a node.
Definition lattice.h:137