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