3131#include < bout/globals.hxx>
3232
3333#include < cmath>
34+ #include < cpptrace/cpptrace.hpp>
35+ #include < memory>
36+ #include < utility>
3437
3538#include " bout/parallel_boundary_op.hxx"
3639#include " bout/parallel_boundary_region.hxx"
4649#include < bout/output.hxx>
4750#include < bout/utils.hxx>
4851
52+ #include " fmt/format.h"
53+
4954// / Constructor
5055Field3D::Field3D (Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in)
5156 : Field(localmesh, location_in, directions_in) {
@@ -235,6 +240,8 @@ Field3D& Field3D::operator=(const Field3D& rhs) {
235240 return (*this ); // skip this assignment
236241 }
237242
243+ track (rhs, " operator=" );
244+
238245 // Copy base slice
239246 Field::operator =(rhs);
240247
@@ -254,6 +261,7 @@ Field3D& Field3D::operator=(const Field3D& rhs) {
254261}
255262
256263Field3D& Field3D::operator =(Field3D&& rhs) {
264+ track (rhs, " operator=" );
257265
258266 // Move parallel slices or delete existing ones.
259267 yup_fields = std::move (rhs.yup_fields );
@@ -274,6 +282,7 @@ Field3D& Field3D::operator=(Field3D&& rhs) {
274282}
275283
276284Field3D& Field3D::operator =(const Field2D& rhs) {
285+ track (rhs, " operator=" );
277286
278287 // / Check that the data is allocated
279288 ASSERT1 (rhs.isAllocated ());
@@ -318,6 +327,7 @@ void Field3D::operator=(const FieldPerp& rhs) {
318327}
319328
320329Field3D& Field3D::operator =(const BoutReal val) {
330+ track (val, " operator=" );
321331
322332 // Delete existing parallel slices. We don't copy parallel slices, so any
323333 // that currently exist will be incorrect.
@@ -831,3 +841,63 @@ Field3D::getValidRegionWithDefault(const std::string& region_name) const {
831841void Field3D::setRegion (const std::string& region_name) {
832842 regionID = fieldmesh->getRegionID (region_name);
833843}
844+
845+ Field3D& Field3D::enableTracking (const std::string& name,
846+ std::weak_ptr<Options> _tracking) {
847+ tracking = std::move (_tracking);
848+ tracking_state = 1 ;
849+ selfname = name;
850+ return *this ;
851+ }
852+
853+ template <typename T, typename >
854+ void Field3D::_track (const T& change, std::string operation) {
855+ if (tracking_state == 0 ) {
856+ return ;
857+ }
858+ auto locked = tracking.lock ();
859+ if (locked == nullptr ) {
860+ return ;
861+ }
862+ const std::string outname{fmt::format (" track_{:s}_{:d}" , selfname, tracking_state++)};
863+
864+ locked->set (outname, change, " tracking" );
865+
866+ const std::string trace = cpptrace::generate_trace ().to_string ();
867+
868+ // Workaround for bug in gcc9.4
869+ #if BOUT_USE_TRACK
870+ const std::string changename = change.name ;
871+ #endif
872+ (*locked)[outname].setAttributes ({
873+ {" operation" , operation},
874+ #if BOUT_USE_TRACK
875+ {" rhs.name" , changename},
876+ #endif
877+ {" trace" , trace},
878+ });
879+ }
880+
881+ template void
882+ Field3D::_track<Field3D, bout::utils::EnableIfField<Field3D>>(const Field3D&,
883+ std::string);
884+ template void Field3D::_track<Field2D>(const Field2D&, std::string);
885+ template void Field3D::_track<>(const FieldPerp&, std::string);
886+
887+ void Field3D::_track (const BoutReal& change, std::string operation) {
888+ if (tracking_state == 0 ) {
889+ return ;
890+ }
891+ auto locked = tracking.lock ();
892+ if (locked == nullptr ) {
893+ return ;
894+ }
895+ const std::string trace = cpptrace::generate_trace ().to_string ();
896+ const std::string outname{fmt::format (" track_{:s}_{:d}" , selfname, tracking_state++)};
897+ locked->set (outname, change, " tracking" );
898+ (*locked)[outname].setAttributes ({
899+ {" operation" , operation},
900+ {" rhs.name" , " BoutReal" },
901+ {" trace" , trace},
902+ });
903+ }
0 commit comments