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 the following:
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
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
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 * @name Standard arithmetic operations
727 * @brief Not all are always callable, e.g. division may not be implemented by all field types
728 * since division is not defined for all @p Field::T types.
729 * @{
730 */
731 /**
732 * @brief Assignment operator.
733 *
734 * @details Assignment can be performed in the following ways:
735 *
736 * __Assignment from field:__
737 *
738 * Assignment from field is possible if types are the same or convertible
739 *
740 * \code {.cpp}
741 * f = g;
742 * \endcode
743 *
744 * __Assignment from scalar:__
745 *
746 * Assignment from scalar is possible if scalar is convertible to Field type
747 *
748 * \code {.cpp}
749 * f = a; // a is a scalar
750 * \endcode
751 *
752 * @param rhs
753 * @return Field<T>& Assigned field
754 */
756 (*this)[ALL] = rhs[X];
757
758 // // This is direct memcpy version of the routine, slightly faster on cpus
759 // if (this->fs == rhs.fs)
760 // return *this;
761 //
762 // assert(this->fs->lattice_id == rhs.fs->lattice_id &&
763 // "Cannot assign fields which belong to different lattices!");
764 //
765 // #if !defined(CUDA) && !defined(HIP)
766 // memcpy(this->field_buffer(), rhs.field_buffer(), sizeof(T) *
767 // lattice.mynode.volume());
768 // #else
769 // gpuMemcpy(this->field_buffer(), rhs.field_buffer(), sizeof(T) *
770 // lattice.mynode.volume(),
771 // gpuMemcpyDeviceToDevice);
772 // #endif
773 // this->mark_changed(ALL);
774
775 return *this;
776 }
777
778 /**
779 * @internal
780 * @brief More general assignment operation if A can be casted into T
781 *
782 * @tparam A Type of element to be assigned
783 * @param rhs Field to assign from
784 * @return Field<T>& Assigned field
785 */
786 template <typename A,
787 std::enable_if_t<
788 hila::is_assignable<T &, A>::value || std::is_convertible<A, T>::value, int> = 0>
789 Field<T> &operator=(const Field<A> &rhs) {
790 (*this)[ALL] = rhs[X];
791 return *this;
792 }
793
794 /**
795 * @internal
796 * @brief Assginment from element
797 *
798 * @tparam A Type of element to be assigned
799 * @param d element to assign to Field
800 * @return Field<T>& Assigned Field
801 */
802 template <typename A,
803 std::enable_if_t<
804 hila::is_assignable<T &, A>::value || std::is_convertible<A, T>::value, int> = 0>
805 Field<T> &operator=(const A &d) {
806 (*this)[ALL] = d;
807 return *this;
808 }
809
810 /**
811 * @internal
812 * @brief assignment of 0 - nullptr, zero field
813 *
814 * @param z nullptr
815 * @return Field<T>& Zero Field
816 */
817 Field<T> &operator=(const std::nullptr_t &z) {
818 (*this)[ALL] = 0;
819 return *this;
820 }
821
822 /**
823 * @brief Move Assignment
824 *
825 * @param rhs
826 * @return Field<T>&
827 */
829 if (this != &rhs) {
830 free();
831 fs = rhs.fs;
832 rhs.fs = nullptr;
833 }
834 return *this;
835 }
836
837 /**
838 * @brief Addition assignment operator
839 * @details Addition assignmen operator can be called in the following ways as long as types are
840 * convertible.
841 *
842 * __Addition assignment with field:__
843 *
844 * \code{.cpp}
845 * Field<MyType> f,g;
846 * . . .
847 * f += g;
848 * \endcode
849 *
850 * __Addition assignment with scalar:__
851 *
852 * \code{.cpp}
853 * Field<MyType> f;
854 * MyType a;
855 * . . .
856 * f += a;
857 * \endcode
858 *
859 * @tparam A Type of r.h.s element
860 * @param rhs Field to sum
861 * @return Field<T>&
862 */
863 template <typename A,
864 std::enable_if_t<std::is_convertible<hila::type_plus<T, A>, T>::value, int> = 0>
866 (*this)[ALL] += rhs[X];
867 return *this;
868 }
869
870 /**
871 * @brief Subtraction assignment operator
872 * @details Subtraction assignment operator can be called in the following ways as long as types
873 * are convertible.
874 *
875 * __Subtraction assignment with field:__
876 *
877 * \code{.cpp}
878 * Field<MyType> f,g;
879 * . . .
880 * f -= g;
881 * \endcode
882 *
883 * __Subtraction assignment with scalar:__
884 *
885 * \code{.cpp}
886 * Field<MyType> f;
887 * MyType a;
888 * . . .
889 * f -= a;
890 * \endcode
891 *
892 * @tparam A Type of r.h.s element
893 * @param rhs Field to subtract
894 * @return Field<T>&
895 */
896 template <typename A,
897 std::enable_if_t<std::is_convertible<hila::type_minus<T, A>, T>::value, int> = 0>
899 (*this)[ALL] -= rhs[X];
900 return *this;
901 }
902
903 /**
904 * @brief Product assignment operator
905 * @details Product assignment operator can be called in the following ways as long as types
906 * are convertible.
907 *
908 * __Product assignment with field:__
909 *
910 * \code{.cpp}
911 * Field<MyType> f,g;
912 * . . .
913 * f *= g;
914 * \endcode
915 *
916 * __Product assignment with scalar:__
917 *
918 * \code{.cpp}
919 * Field<MyType> f;
920 * MyType a;
921 * . . .
922 * f *= a;
923 * \endcode
924 *
925 * @tparam A Type of r.h.s element
926 * @param rhs Field to compute product with
927 * @return Field<T>&
928 */
929 template <typename A,
930 std::enable_if_t<std::is_convertible<hila::type_mul<T, A>, T>::value, int> = 0>
932 (*this)[ALL] *= rhs[X];
933 return *this;
934 }
935
936 /**
937 * @brief Division assignment operator
938 * @details Division assignment operator can be called in the following ways as long as types
939 * are convertible.
940 *
941 * __Division assignment with field:__
942 *
943 * \code{.cpp}
944 * Field<MyType> f,g;
945 * . . .
946 * f /= g;
947 * \endcode
948 *
949 * __Division assignment with scalar:__
950 *
951 * \code{.cpp}
952 * Field<MyType> f;
953 * MyType a;
954 * . . .
955 * f /= a;
956 * \endcode
957 *
958 * @tparam A Type of r.h.s element
959 * @param rhs Field to divide with
960 * @return Field<T>&
961 */
962 template <typename A,
963 std::enable_if_t<std::is_convertible<hila::type_div<T, A>, T>::value, int> = 0>
965 (*this)[ALL] /= rhs[X];
966 return *this;
967 }
968
969 /**
970 * @internal
971 * @brief += Operator between element and field if type of rhs and Field type T are compatible
972 *
973 * @tparam A Type of r.h.s element
974 * @param rhs Element to sum
975 * @return Field<T>&
976
977 */
978 template <typename A,
979 std::enable_if_t<std::is_convertible<hila::type_plus<T, A>, T>::value, int> = 0>
980 Field<T> &operator+=(const A &rhs) {
981 (*this)[ALL] += rhs;
982 return *this;
983 }
984
985 /**
986 * @internal
987 * @brief -= Operator between scalar and field if type of rhs and Field type T are compatible
988 *
989 * @tparam A Type of r.h.s element
990 * @param rhs Element to subtract
991 * @return Field<T>&
992 */
993 template <typename A,
994 std::enable_if_t<std::is_convertible<hila::type_minus<T, A>, T>::value, int> = 0>
995 Field<T> &operator-=(const A &rhs) {
996 (*this)[ALL] -= rhs;
997 return *this;
998 }
999
1000 /**
1001 * @internal
1002 * @brief *= Operator between element and field if type of rhs and Field type T are compatible
1003 *
1004 * @tparam A Type of r.h.s element
1005 * @param rhs Element to multiply with
1006 * @return Field<T>&
1007 */
1008 template <typename A,
1009 std::enable_if_t<std::is_convertible<hila::type_mul<T, A>, T>::value, int> = 0>
1010 Field<T> &operator*=(const A &rhs) {
1011 (*this)[ALL] *= rhs;
1012 return *this;
1013 }
1014
1015 /**
1016 * @internal
1017 * @brief /= Operator between element and field if type of rhs and Field type T are compatible
1018 *
1019 * @tparam A Type of r.h.s element
1020 * @param rhs Element to divide with
1021 * @return Field<T>&
1022 */
1023 template <typename A,
1024 std::enable_if_t<std::is_convertible<hila::type_div<T, A>, T>::value, int> = 0>
1025 Field<T> &operator/=(const A &rhs) {
1026 (*this)[ALL] /= rhs;
1027 return *this;
1028 }
1029
1030 // Unary + and -
1031
1032 /**
1033 * @brief Unary + operator, acts as Identity
1034 *
1035 * @return Field<T> Returns itself
1036 */
1038 return *this;
1039 }
1040
1041 /**
1042 * @brief Unary - operator, acts as negation to all field elements
1043 *
1044 * @return Field<T> Returns negated version of itself
1045 */
1047 Field<T> f;
1048 f[ALL] = -(*this)[X];
1049 return f;
1050 }
1051
1052 /**
1053 * @brief Field comparison operator.
1054 * @details Fields are equal if their content is element-by-element equal
1055 *
1056 * @param rhs Field to compare Field with
1057 * @return true
1058 * @return false
1059 */
1060 template <typename S>
1061 bool operator==(const Field<S> &rhs) const {
1062 double s = 0;
1063 onsites(ALL) s += !((*this)[X] == rhs[X]);
1064 return (s == 0);
1065 }
1066
1067 template <typename S>
1068 bool operator!=(const Field<S> &rhs) const {
1069 return !(*this == rhs);
1070 }
1071
1072
1073 /**
1074 * @brief Squarenorm
1075 * @details Squarenorm of Field \f$f\f$ is dependent on how it is defined for Field type T.
1076 *
1077 * \f$|f|^2 = \sum_{\forall x \in f} |x|^2\f$
1078 *
1079 * If squarenorm is defined for type T then the definition can be seen on the types
1080 * documentation page.
1081 * @return double
1082 */
1083 double squarenorm() const {
1084 double n = 0;
1085 onsites(ALL) {
1086 n += ::squarenorm((*this)[X]);
1087 }
1088 return n;
1089 }
1090
1091 /**
1092 * @brief Norm
1093 * @details Norm of Field depending on how it is defined for Field type T
1094 *
1095 * \f$|f| = \sqrt{\sum_{\forall x \in f} |x|^2}\f$
1096 *
1097 * If norm is defined for type T then the definition can be seen on the types
1098 * documentation page.
1099 * @return double
1100 */
1101 double norm() {
1102 return sqrt(squarenorm());
1103 }
1104
1105 /**
1106 * @brief Returns field with all elements conjugated depending how conjugate is defined for type
1107 *
1108 * @return Field<T>
1109 */
1110 Field<T> conj() const {
1111 Field<T> f;
1112 f[ALL] = ::conj((*this)[X]);
1113 return f;
1114 }
1115
1116 /**
1117 * @brief Returns dagger or Hermitian conjugate of Field depending on how it is
1118 * defined for Field type T
1119 *
1120 * @return Field<T>
1121 */
1122 template <typename R = T, typename A = decltype(::dagger(std::declval<R>()))>
1124 Field<A> f;
1125 f[ALL] = ::dagger((*this)[X]);
1126 return f;
1127 }
1128
1129 /**
1130 * @brief Returns real part of Field
1131 *
1132 * @tparam R Field current type
1133 * @tparam A Field real part type
1134 * @return Field<A>
1135 */
1136 template <typename R = T, typename A = decltype(::real(std::declval<R>()))>
1137 Field<A> real() const {
1138 Field<A> f;
1139 f[ALL] = ::real((*this)[X]);
1140 return f;
1141 }
1142
1143 /**
1144 * @brief Returns imaginary part of Field
1145 *
1146 * @tparam R Field current type
1147 * @tparam A Field imaginary part type
1148 * @return Field<A>
1149 */
1150 template <typename R = T, typename A = decltype(::imag(std::declval<R>()))>
1151 Field<A> imag() const {
1152 Field<A> f;
1153 f[ALL] = ::imag((*this)[X]);
1154 return f;
1155 }
1156 /** @} */
1157
1158 // Communication routines. These are all internal.
1160 void wait_gather(Direction d, Parity p) const;
1161 void gather(Direction d, Parity p = ALL) const;
1162 void drop_comms(Direction d, Parity p) const;
1163
1164 /**
1165 * @brief Create a periodically shifted copy of the field
1166 * @details this is currently OK only for short moves, very inefficient for longer moves
1167 *
1168 * @code{.cpp}
1169 * .
1170 * . //Field<MyType> f is defined
1171 * .
1172 * CoordinateVector v = {1,1,0}
1173 * f.shift(v) // Shift all elements of field once in the x direction and once in the y direction
1174 * f.shift(v,EVEN) // Shift all EVEN elements of field once in the x direction and once in the y
1175 * direction
1176 * @endcode
1177 * @param v
1178 * @param par
1179 * @param r
1180 * @return Field<T>& returns a reference to res
1181 */
1182 Field<T> &shift(const CoordinateVector &v, Field<T> &r, Parity par) const;
1183
1184 Field<T> &shift(const CoordinateVector &v, Field<T> &r) const {
1185 return shift(v, r, ALL);
1186 }
1187
1188 Field<T> shift(const CoordinateVector &v) const;
1189
1190 // General getters and setters
1191
1192 /// Set a single element. Assuming that each node calls this with the same value, it
1193 /// is sufficient to set the element locally
1194
1195 /**
1196 * @brief Get singular element which will be broadcast to all nodes
1197 * @details Works as long as `value` type A and field type T match
1198 * @tparam A type
1199 * @param coord Coordinate of element to be set
1200 * @param value Value to be set
1201 * @return const T
1202 */
1203 template <typename A, std::enable_if_t<std::is_assignable<T &, A>::value, int> = 0>
1204 const T set_element(const CoordinateVector &coord, const A &value) {
1205 T element;
1206 element = value;
1207 assert(is_initialized(ALL) && "Field not initialized yet");
1208 if (lattice.is_on_mynode(coord)) {
1209 set_value_at(element, lattice.site_index(coord));
1210 }
1211 mark_changed(coord.parity());
1212 return element;
1213 }
1214
1215 /**
1216 * @brief Get singular element which will be broadcast to all nodes
1217 *
1218 * @param coord coordinate of fetched element
1219 * @return const T
1220 */
1221 const T get_element(const CoordinateVector &coord) const {
1222 T element;
1223
1224 assert(is_initialized(ALL) && "Field not initialized yet");
1225 int owner = lattice.node_rank(coord);
1226
1227 if (hila::myrank() == owner) {
1228 element = get_value_at(lattice.site_index(coord));
1229 }
1230
1231 hila::broadcast(element, owner);
1232
1233 return element;
1234 }
1235
1236 /**
1237 * @internal
1238 * @brief Set an array of elements in the field
1239 * @remark Assuming that each node calls this with the same value,
1240 * it is sufficient to set the elements locally
1241 *
1242 * @param elements @typedef vector<T> of elements to set
1243 * @param coord_list @typedef vector<CoordinateVector> of coordinates to set
1244 */
1245 void set_elements(const std::vector<T> &elements,
1246 const std::vector<CoordinateVector> &coord_list);
1247 /**
1248 * @internal
1249 * @brief Retrieves list of elements to all nodes.
1250 * @param coord_list vector of coordinates which will be fetched
1251 * @param broadcast if true then elements retrieved to root node will be broadcast to all nodes
1252 * @return std::vector<T> list of all elements
1253 */
1254 std::vector<T> get_elements(const std::vector<CoordinateVector> &coord_list,
1255 bool broadcast = false) const;
1256
1257 std::vector<T> get_subvolume(const CoordinateVector &cmin, const CoordinateVector &cmax,
1258 bool broadcast = false) const;
1259
1260 std::vector<T> get_slice(const CoordinateVector &c, bool broadcast = false) const;
1261
1262 void copy_local_data(std::vector<T> &buffer) const;
1263 void set_local_data(const std::vector<T> &buffer);
1264
1265 void copy_local_data_with_halo(std::vector<T> &buffer) const;
1266
1267
1268 // inline void set_element_at(const CoordinateVector &coord, const A &elem) {
1269 // T e;
1270 // e = elem;
1271 // set_element(e, coord);
1272 // }
1273
1274 // inline void set_element_at(const CoordinateVector &coord, std::nullptr_t elem) {
1275 // T e;
1276 // e = 0;
1277 // set_element(e, coord);
1278 // }
1279
1280 /// @internal
1281 /// Compound element ops - used in field element operations like F[cv] += smth;
1282 /// These are optimized so that do NOT return a value, thus cannot be chained. This avoids MPI.
1283 template <typename A,
1284 std::enable_if_t<std::is_assignable<T &, hila::type_plus<T, A>>::value, int> = 0>
1285 inline void compound_add_element(const CoordinateVector &coord, const A &av) {
1286 assert(is_initialized(ALL));
1287 if (lattice.is_on_mynode(coord)) {
1288 auto i = lattice.site_index(coord);
1289 auto v = get_value_at(i);
1290 v += av;
1291 set_value_at(v, i);
1292 }
1293 mark_changed(coord.parity());
1294 }
1295
1296 template <typename A,
1297 std::enable_if_t<std::is_assignable<T &, hila::type_minus<T, A>>::value, int> = 0>
1298 inline void compound_sub_element(const CoordinateVector &coord, const A &av) {
1299 assert(is_initialized(ALL));
1300 if (lattice.is_on_mynode(coord)) {
1301 auto i = lattice.site_index(coord);
1302 auto v = get_value_at(i);
1303 v -= av;
1304 set_value_at(v, i);
1305 }
1306 mark_changed(coord.parity());
1307 }
1308
1309 template <typename A,
1310 std::enable_if_t<std::is_assignable<T &, hila::type_mul<T, A>>::value, int> = 0>
1311 inline void compound_mul_element(const CoordinateVector &coord, const A &av) {
1312 assert(is_initialized(ALL));
1313 if (lattice.is_on_mynode(coord)) {
1314 auto i = lattice.site_index(coord);
1315 auto v = get_value_at(i);
1316 v *= av;
1317 set_value_at(v, i);
1318 }
1319 mark_changed(coord.parity());
1320 }
1321
1322 template <typename A,
1323 std::enable_if_t<std::is_assignable<T &, hila::type_div<T, A>>::value, int> = 0>
1324 inline void compound_div_element(const CoordinateVector &coord, const A &av) {
1325 assert(is_initialized(ALL));
1326 if (lattice.is_on_mynode(coord)) {
1327 auto i = lattice.site_index(coord);
1328 auto v = get_value_at(i);
1329 v /= av;
1330 set_value_at(v, i);
1331 }
1332 mark_changed(coord.parity());
1333 }
1334
1335
1336 // pre- and postfix ++ -- return value
1337 ///@internal
1338 inline const T increment_postfix_element(const CoordinateVector &coord) {
1339 T r, v;
1340 v = get_element(coord);
1341 r = v;
1342 v++;
1343 set_element(coord, v);
1344 return r;
1345 }
1346
1347 ///@internal
1348 inline const T increment_prefix_element(const CoordinateVector &coord) {
1349 T v = get_element(coord);
1350 ++v;
1351 set_element(coord, v);
1352 return v;
1353 }
1354
1355 ///@internal
1356 inline const T decrement_postfix_element(const CoordinateVector &coord) {
1357 T r, v;
1358 v = get_element(coord);
1359 r = v;
1360 v--;
1361 set_element(coord, v);
1362 return r;
1363 }
1364
1365 ///@internal
1366 inline const T decrement_prefix_element(const CoordinateVector &coord) {
1367 T v = get_element(coord);
1368 --v;
1369 set_element(coord, v);
1370 return v;
1371 }
1372
1373
1374 // Fourier transform declaration
1375 Field<T> FFT(fft_direction fdir = fft_direction::forward) const;
1376 Field<T> FFT(const CoordinateVector &dirs, fft_direction fdir = fft_direction::forward) const;
1377
1379 FFT_real_to_complex(fft_direction fdir = fft_direction::forward) const;
1381 FFT_complex_to_real(fft_direction fdir = fft_direction::forward) const;
1382
1383
1384 // Reflect the field along all or 1 coordinate
1385 Field<T> reflect() const;
1386 Field<T> reflect(Direction dir) const;
1387 Field<T> reflect(const CoordinateVector &dirs) const;
1388
1389 // Writes the Field to disk
1390 void write(std::ofstream &outputfile, bool binary = true, int precision = 8) const;
1391 void write(const std::string &filename, bool binary = true, int precision = 8) const;
1392
1393 void read(std::ifstream &inputfile);
1394 void read(const std::string &filename);
1395
1396 void write_subvolume(std::ofstream &outputfile, const CoordinateVector &cmin,
1397 const CoordinateVector &cmax, int precision = 6) const;
1398 void write_subvolume(const std::string &filenname, const CoordinateVector &cmin,
1399 const CoordinateVector &cmax, int precision = 6) const;
1400
1401 template <typename Out>
1402 void write_slice(Out &outputfile, const CoordinateVector &slice, int precision = 6) const;
1403
1404 /**
1405 * @brief Sum reduction of Field
1406 * @details The sum in the reduction is defined by the Field type T
1407 * @param par Parity
1408 * @param allreduce Switch to turn on or off MPI allreduce
1409 * @return T Field element type reduced to one element
1410 */
1411 T sum(Parity par = Parity::all, bool allreduce = true) const;
1412
1413 /**
1414 * @brief Product reduction of Field
1415 * @details The product in the reduction is defined by the Field type T.
1416 * @param par Parity
1417 * @param allreduce Switch to turn on or off MPI allreduce
1418 * @return T Field element type reduced to one element
1419 */
1420 T product(Parity par = Parity::all, bool allreduce = true) const;
1421
1422 /**
1423 * @brief Declare gpu_reduce here, defined only for GPU targets
1424 * @internal
1425 * @param min_or_max
1426 * @param par
1427 * @param loc
1428 * @return T
1429 */
1430 T gpu_minmax(bool min_or_max, Parity par, CoordinateVector &loc) const;
1431
1432 /**
1433 * @brief Minimum value of Field. If CoordinateVector is passed to function
1434 * then location of minimum value will be stored in said CoordinateVector
1435 * @name Min functions
1436 * @param par ::Parity
1437 * @return T Minimum value of Field
1438 */
1439 /** @{ */
1440 T min(Parity par = ALL) const;
1441 T min(CoordinateVector &loc) const;
1442 T min(Parity par, CoordinateVector &loc) const;
1443 /** @} */
1444
1445 /**
1446 * @brief Maximum value of Field. If CoordinateVector is passed to function
1447 * then location of maximum value will be stored in said CoordinateVector
1448 * @name Max functions
1449 * @param par ::Parity
1450 * @return T Minimum value of Field
1451 */
1452 /** @{ */
1453 T max(Parity par = ALL) const;
1454 T max(CoordinateVector &loc) const;
1455 T max(Parity par, CoordinateVector &loc) const;
1456 /** @} */
1457
1458 /**
1459 * @brief Function to perform min or max operations
1460 * @internal
1461 *
1462 * @param is_min if true we compute min
1463 * @param par Parity
1464 * @param loc Location of min or max
1465 * @return T
1466 */
1467 T minmax(bool is_min, Parity par, CoordinateVector &loc) const;
1468
1469 void random();
1470 void gaussian_random(double width = 1.0);
1471
1472
1473}; // End of class Field<>
1474
1475///////////////////////////////
1476// operators +-*/
1477// these operators rely on SFINAE, OK if field_hila::type_plus<A,B> exists i.e. A+B is
1478// OK
1479// There are several versions of the operators, depending if one of the arguments is
1480// Field or scalar, and if the return type is the same as the Field argument. This can
1481// enable the compiler to avoid extra copies of the args
1482
1483///////////////////////////////
1484/// operator + (Field + Field) -generic
1485template <typename A, typename B,
1486 std::enable_if_t<!std::is_same<hila::type_plus<A, B>, A>::value &&
1487 !std::is_same<hila::type_plus<A, B>, B>::value,
1488 int> = 0>
1489auto operator+(const Field<A> &lhs, const Field<B> &rhs) -> Field<hila::type_plus<A, B>> {
1491 tmp[ALL] = lhs[X] + rhs[X];
1492 return tmp;
1493}
1494
1495// (Possibly) optimzed version where the 1st argument can be reused
1496template <typename A, typename B,
1497 std::enable_if_t<std::is_same<hila::type_plus<A, B>, A>::value, int> = 0>
1498auto operator+(Field<A> lhs, const Field<B> &rhs) {
1499 lhs[ALL] += rhs[X];
1500 return lhs;
1501}
1502
1503// Optimzed version where the 2nd argument can be reused
1504template <typename A, typename B,
1505 std::enable_if_t<!std::is_same<hila::type_plus<A, B>, A>::value &&
1506 std::is_same<hila::type_plus<A, B>, B>::value,
1507 int> = 0>
1508auto operator+(const Field<A> &lhs, Field<B> rhs) {
1509 rhs[ALL] += lhs[X];
1510 return rhs;
1511}
1512
1513//////////////////////////////
1514/// operator + (Field + scalar)
1515
1516template <typename A, typename B,
1517 std::enable_if_t<!std::is_same<hila::type_plus<A, B>, A>::value, int> = 0>
1518auto operator+(const Field<A> &lhs, const B &rhs) -> Field<hila::type_plus<A, B>> {
1520 tmp[ALL] = lhs[X] + rhs;
1521 return tmp;
1522}
1523
1524template <typename A, typename B,
1525 std::enable_if_t<std::is_same<hila::type_plus<A, B>, A>::value, int> = 0>
1526Field<A> operator+(Field<A> lhs, const B &rhs) {
1527 lhs[ALL] += rhs;
1528 return lhs;
1529}
1530
1531
1532//////////////////////////////
1533/// operator + (scalar + Field)
1534
1535template <typename A, typename B,
1536 std::enable_if_t<!std::is_same<hila::type_plus<A, B>, B>::value, int> = 0>
1537auto operator+(const A &lhs, const Field<B> &rhs) -> Field<hila::type_plus<A, B>> {
1538 return rhs + lhs;
1539}
1540
1541template <typename A, typename B,
1542 std::enable_if_t<std::is_same<hila::type_plus<A, B>, B>::value, int> = 0>
1543Field<B> operator+(const A &lhs, Field<B> rhs) {
1544 return rhs + lhs;
1545}
1546
1547
1548//////////////////////////////
1549/// operator - Field - Field -generic
1550template <typename A, typename B,
1551 std::enable_if_t<!std::is_same<hila::type_minus<A, B>, A>::value &&
1552 !std::is_same<hila::type_minus<A, B>, B>::value,
1553 int> = 0>
1556 tmp[ALL] = lhs[X] - rhs[X];
1557 return tmp;
1558}
1559
1560// Optimzed version where the 1st argument can be reused
1561template <typename A, typename B,
1562 std::enable_if_t<std::is_same<hila::type_minus<A, B>, A>::value, int> = 0>
1563auto operator-(Field<A> lhs, const Field<B> &rhs) {
1564 lhs[ALL] -= rhs[X];
1565 return lhs;
1566}
1567
1568// Optimzed version where the 2nd argument can be reused
1569template <typename A, typename B,
1570 std::enable_if_t<!std::is_same<hila::type_minus<A, B>, A>::value &&
1571 std::is_same<hila::type_minus<A, B>, B>::value,
1572 int> = 0>
1573auto operator-(const Field<A> &lhs, Field<B> rhs) {
1574 rhs[ALL] = lhs[X] - rhs[X];
1575 return rhs;
1576}
1577
1578//////////////////////////////
1579/// operator - (Field - scalar)
1580
1581template <typename A, typename B,
1582 std::enable_if_t<!std::is_same<hila::type_minus<A, B>, A>::value, int> = 0>
1583auto operator-(const Field<A> &lhs, const B &rhs) -> Field<hila::type_minus<A, B>> {
1585 tmp[ALL] = lhs[X] - rhs;
1586 return tmp;
1587}
1588
1589template <typename A, typename B,
1590 std::enable_if_t<std::is_same<hila::type_minus<A, B>, A>::value, int> = 0>
1591Field<A> operator-(Field<A> lhs, const B &rhs) {
1592 lhs[ALL] -= rhs;
1593 return lhs;
1594}
1595
1596//////////////////////////////
1597/// operator - (scalar - Field)
1598
1599template <typename A, typename B,
1600 std::enable_if_t<!std::is_same<hila::type_minus<A, B>, B>::value, int> = 0>
1601auto operator-(const A &lhs, const Field<B> &rhs) -> Field<hila::type_minus<A, B>> {
1603 tmp[ALL] = lhs - rhs[X];
1604 return tmp;
1605}
1606
1607template <typename A, typename B,
1608 std::enable_if_t<std::is_same<hila::type_minus<A, B>, B>::value, int> = 0>
1609Field<B> operator-(const A &lhs, Field<B> rhs) {
1610 rhs[ALL] = lhs - rhs[X];
1611 return rhs;
1612}
1613
1614///////////////////////////////
1615/// operator * (Field * Field)
1616/// generic
1617template <typename A, typename B,
1618 std::enable_if_t<!std::is_same<hila::type_mul<A, B>, A>::value &&
1619 !std::is_same<hila::type_mul<A, B>, B>::value,
1620 int> = 0>
1621auto operator*(const Field<A> &lhs, const Field<B> &rhs) -> Field<hila::type_mul<A, B>> {
1623 tmp[ALL] = lhs[X] * rhs[X];
1624 return tmp;
1625}
1626
1627/// reuse 1st
1628template <typename A, typename B,
1629 std::enable_if_t<std::is_same<hila::type_mul<A, B>, A>::value, int> = 0>
1631 lhs[ALL] = lhs[X] * rhs[X];
1632 return lhs;
1633}
1634
1635/// reuse 2nd
1636template <typename A, typename B,
1637 std::enable_if_t<!std::is_same<hila::type_mul<A, B>, A>::value &&
1638 std::is_same<hila::type_mul<A, B>, B>::value,
1639 int> = 0>
1641 rhs[ALL] = lhs[X] * rhs[X];
1642 return rhs;
1643}
1644
1645/////////////////////////////////
1646/// operator * (scalar * field)
1647
1648template <typename A, typename B,
1649 std::enable_if_t<!std::is_same<hila::type_mul<A, B>, B>::value, int> = 0>
1650auto operator*(const A &lhs, const Field<B> &rhs) -> Field<hila::type_mul<A, B>> {
1652 tmp[ALL] = lhs * rhs[X];
1653 return tmp;
1654}
1655
1656template <typename A, typename B,
1657 std::enable_if_t<std::is_same<hila::type_mul<A, B>, B>::value, int> = 0>
1658Field<B> operator*(const A &lhs, Field<B> rhs) {
1659 rhs[ALL] = lhs * rhs[X];
1660 return rhs;
1661}
1662
1663/////////////////////////////////
1664/// operator * (field * scalar)
1665
1666template <typename A, typename B,
1667 std::enable_if_t<!std::is_same<hila::type_mul<A, B>, A>::value, int> = 0>
1668auto operator*(const Field<A> &lhs, const B &rhs) -> Field<hila::type_mul<A, B>> {
1670 tmp[ALL] = lhs[X] * rhs;
1671 return tmp;
1672}
1673
1674template <typename A, typename B,
1675 std::enable_if_t<std::is_same<hila::type_mul<A, B>, A>::value, int> = 0>
1676Field<A> operator*(Field<A> lhs, const B &rhs) {
1677 lhs[ALL] = lhs[X] * rhs;
1678 return lhs;
1679}
1680
1681///////////////////////////////
1682/// operator / (Field / Field)
1683/// generic
1684template <typename A, typename B,
1685 std::enable_if_t<!std::is_same<hila::type_div<A, B>, A>::value &&
1686 !std::is_same<hila::type_div<A, B>, B>::value,
1687 int> = 0>
1690 tmp[ALL] = l[X] / r[X];
1691 return tmp;
1692}
1693
1694/// reuse 1st
1695template <typename A, typename B,
1696 std::enable_if_t<std::is_same<hila::type_div<A, B>, A>::value, int> = 0>
1698 l[ALL] = l[X] / r[X];
1699 return l;
1700}
1701
1702/// reuse 2nd
1703template <typename A, typename B,
1704 std::enable_if_t<!std::is_same<hila::type_div<A, B>, A>::value &&
1705 std::is_same<hila::type_div<A, B>, B>::value,
1706 int> = 0>
1708 r[ALL] = l[X] / r[X];
1709 return r;
1710}
1711
1712//////////////////////////////////
1713/// operator / (scalar/Field)
1714template <typename A, typename B,
1715 std::enable_if_t<!std::is_same<hila::type_div<A, B>, B>::value, int> = 0>
1716auto operator/(const A &lhs, const Field<B> &rhs) -> Field<hila::type_div<A, B>> {
1718 tmp[ALL] = lhs / rhs[X];
1719 return tmp;
1720}
1721
1722template <typename A, typename B,
1723 std::enable_if_t<std::is_same<hila::type_div<A, B>, B>::value, int> = 0>
1724Field<B> operator/(const A &lhs, Field<B> rhs) {
1725 rhs[ALL] = lhs / rhs[X];
1726 return rhs;
1727}
1728
1729//////////////////////////////////
1730/// operator / (Field/scalar)
1731template <typename A, typename B,
1732 std::enable_if_t<!std::is_same<hila::type_div<A, B>, A>::value, int> = 0>
1733auto operator/(const Field<A> &lhs, const B &rhs) -> Field<hila::type_div<A, B>> {
1735 tmp[ALL] = lhs[X] / rhs;
1736 return tmp;
1737}
1738
1739template <typename A, typename B,
1740 std::enable_if_t<std::is_same<hila::type_div<A, B>, A>::value, int> = 0>
1741auto operator/(Field<A> lhs, const B &rhs) {
1742 lhs[ALL] = lhs[X] / rhs;
1743 return lhs;
1744}
1745
1746namespace hila {
1747
1748/**
1749 * @brief Implement hila::swap() for Fields too, equivalent to std::swap()
1750 */
1751template <typename T>
1752void swap(Field<T> &A, Field<T> &B) {
1753 std::swap(A.fs, B.fs);
1754}
1755} // namespace hila
1756
1757
1758///////////////////////////////////////////////////////////////////////
1759// Allow some arithmetic functions if implemented
1760
1761template <typename T, typename R = decltype(exp(std::declval<T>()))>
1762Field<R> exp(const Field<T> &arg) {
1763 Field<R> res;
1764 onsites(ALL) {
1765 res[X] = exp(arg[X]);
1766 }
1767 return res;
1768}
1769
1770template <typename T, typename R = decltype(log(std::declval<T>()))>
1771Field<R> log(const Field<T> &arg) {
1772 Field<R> res;
1773 onsites(ALL) {
1774 res[X] = log(arg[X]);
1775 }
1776 return res;
1777}
1778
1779template <typename T, typename R = decltype(sin(std::declval<T>()))>
1780Field<R> sin(const Field<T> &arg) {
1781 Field<R> res;
1782 onsites(ALL) {
1783 res[X] = sin(arg[X]);
1784 }
1785 return res;
1786}
1787
1788template <typename T, typename R = decltype(cos(std::declval<T>()))>
1789Field<R> cos(const Field<T> &arg) {
1790 Field<R> res;
1791 onsites(ALL) {
1792 res[X] = cos(arg[X]);
1793 }
1794 return res;
1795}
1796
1797template <typename T, typename R = decltype(tan(std::declval<T>()))>
1798Field<R> tan(const Field<T> &arg) {
1799 Field<R> res;
1800 onsites(ALL) {
1801 res[X] = tan(arg[X]);
1802 }
1803 return res;
1804}
1805
1806template <typename T, typename R = decltype(asin(std::declval<T>()))>
1807Field<R> asin(const Field<T> &arg) {
1808 Field<R> res;
1809 onsites(ALL) {
1810 res[X] = asin(arg[X]);
1811 }
1812 return res;
1813}
1814
1815template <typename T, typename R = decltype(acos(std::declval<T>()))>
1816Field<R> acos(const Field<T> &arg) {
1817 Field<R> res;
1818 onsites(ALL) {
1819 res[X] = acos(arg[X]);
1820 }
1821 return res;
1822}
1823
1824template <typename T, typename R = decltype(atan(std::declval<T>()))>
1825Field<R> atan(const Field<T> &arg) {
1826 Field<R> res;
1827 onsites(ALL) {
1828 res[X] = atan(arg[X]);
1829 }
1830 return res;
1831}
1832
1833template <typename T, typename R = decltype(abs(std::declval<T>()))>
1834Field<R> abs(const Field<T> &arg) {
1835 Field<R> res;
1836 onsites(ALL) {
1837 res[X] = abs(arg[X]);
1838 }
1839 return res;
1840}
1841
1842template <typename T, typename P, typename R = decltype(pow(std::declval<T>()), std::declval<P>())>
1843Field<R> pow(const Field<T> &arg, const P p) {
1844 Field<R> res;
1845 onsites(ALL) {
1846 res[X] = pow(arg[X], p);
1847 }
1848 return res;
1849}
1850
1851template <typename T>
1852double squarenorm(const Field<T> &arg) {
1853 double r = 0;
1854 onsites(ALL) {
1855 r += squarenorm(arg[X]);
1856 }
1857 return r;
1858}
1859
1860template <typename T>
1861double norm(const Field<T> &arg) {
1862 return sqrt(squarenorm(arg));
1863}
1864
1865template <typename T>
1866Field<T> conj(const Field<T> &arg) {
1867 return arg.conj();
1868}
1869
1870template <typename T, typename A = decltype(::dagger(std::declval<T>()))>
1871Field<A> dagger(const Field<T> &arg) {
1872 return arg.dagger();
1873}
1874
1875template <typename T, typename A = decltype(::real(std::declval<T>()))>
1876Field<A> real(const Field<T> &arg) {
1877 return arg.real();
1878}
1879
1880template <typename T, typename A = decltype(::imag(std::declval<T>()))>
1881Field<A> imag(const Field<T> &arg) {
1882 return arg.imag();
1883}
1884
1885
1886template <typename A, typename B, typename R = decltype(std::declval<A>() - std::declval<B>())>
1887double squarenorm_relative(const Field<A> &a, const Field<B> &b) {
1888 double res = 0;
1889 onsites(ALL) {
1890 res += squarenorm(a[X] - b[X]);
1891 }
1892 return res;
1893}
1894
1895
1896template <typename T>
1898 Field<T> res;
1899 shift(v, res, ALL);
1900 return res;
1901}
1902
1903
1904/// @internal
1905/// drop_comms(): if field is changed or deleted, 'cancel' ongoing communications. Now just wait
1906/// for the communications to finish don't actually cancel them. Still separate this from using
1907/// only wait_gather since this needs to be called in ~Field() and we need to check if there are
1908/// ongoing communications. User gets nottified if there was redundant communications if
1909/// drop_comms_timer is in the run print out.
1910///
1911/// This should happen very seldom, only if there are "by-hand" start_gather operations and these
1912/// are not needed.
1913template <typename T>
1914void Field<T>::drop_comms(Direction d, Parity p) const {
1915
1916 if (is_comm_initialized()) {
1917 if (is_gather_started(d, ALL)) {
1918 drop_comms_timer.start();
1919 wait_gather(d, ALL);
1920 drop_comms_timer.stop();
1921 }
1922 if (p != ALL) {
1923 if (is_gather_started(d, p)) {
1924 drop_comms_timer.start();
1925 wait_gather(d, p);
1926 drop_comms_timer.stop();
1927 }
1928 } else {
1929 if (is_gather_started(d, EVEN)) {
1930 drop_comms_timer.start();
1931 wait_gather(d, EVEN);
1932 drop_comms_timer.stop();
1933 }
1934 if (is_gather_started(d, ODD)) {
1935 drop_comms_timer.start();
1936 wait_gather(d, ODD);
1937 drop_comms_timer.stop();
1938 }
1939 }
1940 }
1941}
1942
1943
1944/// @internal And a convenience combi function
1945template <typename T>
1946void Field<T>::gather(Direction d, Parity p) const {
1947 start_gather(d, p);
1948 wait_gather(d, p);
1949}
1950
1951
1952// Read in the "technical" communication bits
1953
1954#include "field_comm.h"
1955
1956
1957template <typename T>
1958void Field<T>::random() {
1959
1960#if defined(CUDA) || defined(HIP)
1961
1962 if (!hila::is_device_rng_on()) {
1963
1964 std::vector<T> rng_buffer(lattice.mynode.volume());
1965 for (auto &element : rng_buffer)
1966 hila::random(element);
1967 (*this).set_local_data(rng_buffer);
1968
1969 } else {
1970 onsites(ALL) {
1971 hila::random((*this)[X]);
1972 }
1973 }
1974#else
1975
1976 onsites(ALL) {
1977 hila::random((*this)[X]);
1978 }
1979
1980#endif
1981}
1982
1983template <typename T>
1984void Field<T>::gaussian_random(double width) {
1985
1986#if defined(CUDA) || defined(HIP)
1987
1988 if (!hila::is_device_rng_on()) {
1989
1990 std::vector<T> rng_buffer(lattice.mynode.volume());
1991 for (auto &element : rng_buffer)
1992 hila::gaussian_random(element, width);
1993 (*this).set_local_data(rng_buffer);
1994
1995 } else {
1996 onsites(ALL) {
1997 hila::gaussian_random((*this)[X], width);
1998 }
1999 }
2000#else
2001
2002 onsites(ALL) {
2003 hila::gaussian_random((*this)[X], width);
2004 }
2005
2006#endif
2007}
2008
2009
2010#ifdef HILAPP
2011
2012////////////////////////////////////////////////////////////////////////////////
2013// A couple of placeholder functions, not included in produced code.
2014// These are here in order for hilapp to generate explicitly
2015// some Direction and CoordinateVector operations, which may not exist in
2016// original code as such. It is easiest to let the general hilapp
2017// code generation to do it using this hack, instead of hard-coding these to
2018// hilapp.
2019//
2020// These are needed because hilapp changes X+d-d -> +d-d, which may involve
2021// an operator not met before
2022
2023inline void dummy_X_f() {
2024 Direction d1 = e_x;
2025 CoordinateVector v1(0);
2026 onsites(ALL) {
2027 Direction d;
2028 d = +d1;
2029 d = -d1; // unaryops
2030 CoordinateVector vec;
2031 vec = +v1;
2032 vec = -v1;
2033
2034 // Direction + vector combos
2035 vec = d + d1;
2036 vec = d - d1;
2037 vec = v1 + d1;
2038 vec = v1 - d1;
2039 vec = d1 + v1;
2040 vec = d1 - v1;
2041 vec = vec + v1;
2042 vec = vec - v1;
2043
2044 // and Direction index func
2045 vec[e_x] = vec[0] + vec[e_y] + vec.e(e_y);
2046 }
2047}
2048
2049/**
2050 * @internal
2051 * @brief Dummy function including Field<T> functions and methods which
2052 * need to be explicitly seen by hilapp during 1st pass in order to
2053 * generater necessary functions. Add here ops as needed
2054 */
2055template <typename T>
2056inline void ensure_field_operators_exist() {
2057
2058 ensure_unary_minus_is_loop_function<T>();
2059 ensure_assign_zero_is_loop_function<T>();
2060
2061 // make shift also explicit
2062 CoordinateVector v = 0;
2063 Field<T> f;
2064 f = f.shift(v);
2065}
2066
2067#endif
2068
2069#endif // FIELD_H
Array< n, m, T > conj(const Array< n, m, T > &arg)
Return conjugate Array.
Definition array.h:674
Array< n, m, T > asin(Array< n, m, T > a)
Inverse Sine.
Definition array.h:1109
Array< n, m, T > exp(Array< n, m, T > a)
Exponential.
Definition array.h:1059
Array< n, m, hila::arithmetic_type< T > > imag(const Array< n, m, T > &arg)
Return imaginary part of Array.
Definition array.h:702
hila::arithmetic_type< T > squarenorm(const Array< n, m, T > &rhs)
Return square norm of Array.
Definition array.h:1018
Array< n, m, T > tan(Array< n, m, T > a)
Tangent.
Definition array.h:1099
Array< n, m, T > cos(Array< n, m, T > a)
Cosine.
Definition array.h:1089
Array< n, m, T > acos(Array< n, m, T > a)
Inverse Cosine.
Definition array.h:1119
Array< n, m, T > sqrt(Array< n, m, T > a)
Square root.
Definition array.h:1039
Array< n, m, T > atan(Array< n, m, T > a)
Inverse Tangent.
Definition array.h:1129
Array< n, m, T > pow(Array< n, m, T > a, int b)
Power.
Definition array.h:1204
Array< n, m, T > sin(Array< n, m, T > a)
Sine.
Definition array.h:1079
Array< n, m, hila::arithmetic_type< T > > real(const Array< n, m, T > &arg)
Return real part of Array.
Definition array.h:688
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:755
Field()
Field constructor.
Definition field.h:277
Field< A > real() const
Returns real part of Field.
Definition field.h:1137
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:682
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:1221
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:1151
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:1123
const T set_element(const CoordinateVector &coord, const A &value)
Get singular element which will be broadcast to all nodes.
Definition field.h:1204
T max(Parity par=ALL) const
Find maximum value from Field.
Definition reduction.h:424
double norm()
Norm.
Definition field.h:1101
bool is_initialized(Parity p) const
Returns true if the Field has been assigned to.
Definition field.h:431
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_struct *__restrict__ fs
Field::fs holds all of the field content in Field::field_struct.
Definition field.h:243
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
Field< T > operator+() const
Unary + operator, acts as Identity.
Definition field.h:1037
Field< Complex< hila::arithmetic_type< T > > > FFT_real_to_complex(fft_direction fdir=fft_direction::forward) const
Definition fft.h:467
Field< T > & operator=(Field< T > &&rhs)
Move Assignment.
Definition field.h:828
Field< T > & operator*=(const Field< A > &rhs)
Product assignment operator.
Definition field.h:931
bool is_allocated() const
Returns true if the Field data has been allocated.
Definition field.h:422
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:1110
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
bool operator==(const Field< S > &rhs) const
Field comparison operator.
Definition field.h:1061
double squarenorm() const
Squarenorm.
Definition field.h:1083
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:898
Field< T > operator-() const
Unary - operator, acts as negation to all field elements.
Definition field.h:1046
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:665
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
and get a slice (subvolume)
Definition field_comm.h:703
Field< T > & operator/=(const Field< A > &rhs)
Division assignment operator.
Definition field.h:964
Field< T > & operator+=(const Field< A > &rhs)
Addition assignment operator.
Definition field.h:865
T e(const int i, const int j) const
Standard array indexing operation for matrices and vectors.
Definition matrix.h:286
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
Conditionally reture bool type false if type T does't have unary - operator.
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
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:1223
T abs(const Complex< T > &a)
Return absolute value of Complex number.
Definition cmplx.h:1187
T arg(const Complex< T > &a)
Return argument of Complex number.
Definition cmplx.h:1199
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:150
#define RESTRICT
Definition defs.h:51
auto operator/(const Field< A > &l, const Field< B > &r) -> Field< hila::type_div< A, B > >
Definition field.h:1688
auto operator+(const Field< A > &lhs, const Field< B > &rhs) -> Field< hila::type_plus< A, B > >
operator + (Field + Field) -generic
Definition field.h:1489
auto operator-(const Field< A > &lhs, const Field< B > &rhs) -> Field< hila::type_minus< A, B > >
operator - Field - Field -generic
Definition field.h:1554
auto operator*(const Field< A > &lhs, const Field< B > &rhs) -> Field< hila::type_mul< A, B > >
Definition field.h:1621
Implement hila::swap for gauge fields.
Definition array.h:981
T gaussian_random()
Template function T hila::gaussian_random<T>(),generates gaussian random value of type T,...
Definition random.h:183
logger_class log
Now declare the logger.
double random()
Real valued uniform random number generator.
Definition hila_gpu.cpp:121
int myrank()
rank of this node
Definition com_mpi.cpp:234
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:152
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
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: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