Task: Adding support to memory collection of Particles and MultiFabs within a grid (AMReX-Codes/amrex#4629)

org: AMReX-Codes
repo: amrex
pr_number: 4629
issue_title: Adding support to memory collection of Particles and MultiFabs within a grid
issue_body: ## Summary
This PR adds functions to return memory usage from `Particles` and `FabArray`/`MultiFabs` within a grid/box. The main goal is to use this information for load balancing. We aim to consider the memory allocated to a worker as a factor in the load-balancing algorithms for AMReX, so there is a need to capture specific grid/box memory information.

## Additional background

## Checklist

The proposed changes:
- [ ] fix a bug or incorrect behavior in AMReX
- [x] add new capabilities to AMReX
- [ ] changes answers in the test suite to more than roundoff level
- [ ] are likely to significantly affect the results of downstream AMReX users
- [ ] include documentation in the code and/or rst files, if appropriate

resolved_issues: number: 4629
title: Adding support to memory collection of Particles and MultiFabs within a grid
body: ## Summary
This PR adds functions to return memory usage from `Particles` and `FabArray`/`MultiFabs` within a grid/box. The main goal is to use this information for load balancing. We aim to consider the memory allocated to a worker as a factor in the load-balancing algorithms for AMReX, so there is a need to capture specific grid/box memory information.

## Additional background

## Checklist

The proposed changes:
- [ ] fix a bug or incorrect behavior in AMReX
- [x] add new capabilities to AMReX
- [ ] changes answers in the test suite to more than roundoff level
- [ ] are likely to significantly affect the results of downstream AMReX users
- [ ] include documentation in the code and/or rst files, if appropriate


Reference solution:
fix_patch: diff --git a/Src/Base/AMReX_FabArray.H b/Src/Base/AMReX_FabArray.H
index 3588a8f0714..a47a9278f34 100644
--- a/Src/Base/AMReX_FabArray.H
+++ b/Src/Base/AMReX_FabArray.H
@@ -817,6 +817,10 @@ public:
     template <class F=FAB, std::enable_if_t<IsBaseFab<F>::value,int> = 0>
     void setDomainBndry (value_type val, int strt_comp, int ncomp, const Geometry& geom);
 
+    //! Get capacity of the FabArray
+    template <typename I, class F=FAB, std::enable_if_t<IsBaseFab<F>::value && std::is_integral_v<I> && (sizeof(I) >= sizeof(Long)), int> = 0>
+    void capacityOfFabs (LayoutData<I>& mem) const;
+
     /**
     * \brief Returns the sum of component "comp"
     *
@@ -2506,6 +2510,17 @@ FabArray<FAB>::setDomainBndry (value_type val,
     }
 }
 
+
+template <class FAB>
+template <typename I, class F, std::enable_if_t<IsBaseFab<F>::value && std::is_integral_v<I> && (sizeof(I) >= sizeof(Long)), int> FOO>
+void
+FabArray<FAB>::capacityOfFabs (LayoutData<I>& mem) const {
+    AMREX_ASSERT(amrex::isMFIterSafe(*this, mem));
+    for (MFIter mfi(*this, MFItInfo{}.DisableDeviceSync()); mfi.isValid(); ++mfi) {
+        mem[mfi] += static_cast<I>((*this)[mfi].nBytesOwned());
+    }
+}
+
 template <class FAB>
 template <class F, std::enable_if_t<IsBaseFab<F>::value,int> FOO>
 typename F::value_type
diff --git a/Src/Particle/AMReX_ParIter.H b/Src/Particle/AMReX_ParIter.H
index 39e5d1a9eda..459ff31757a 100644
--- a/Src/Particle/AMReX_ParIter.H
+++ b/Src/Particle/AMReX_ParIter.H
@@ -93,6 +93,8 @@ public:
 
     [[nodiscard]] int numNeighborParticles () const { return GetParticleTile().numNeighborParticles(); }
 
+    [[nodiscard]] Long capacity () const { return GetParticleTile().capacity(); }
+
     [[nodiscard]] int GetLevel () const { return m_level; }
 
     [[nodiscard]] std::pair<int, int> GetPairIndex () const { return std::make_pair(this->index(), this->LocalTileIndex()); }
diff --git a/Src/Particle/AMReX_ParticleContainer.H b/Src/Particle/AMReX_ParticleContainer.H
index a6a05b28702..2397a7d4823 100644
--- a/Src/Particle/AMReX_ParticleContainer.H
+++ b/Src/Particle/AMReX_ParticleContainer.H
@@ -578,6 +578,12 @@ public:
 
     Vector<Long> NumberOfParticlesInGrid  (int level, bool only_valid = true, bool only_local = false) const;
 
+    /**
+     * \brief Return capacity of memory for particles at specific grid
+     */
+    template <typename I, std::enable_if_t<std::is_integral_v<I> && (sizeof(I) >= sizeof(Long)), int> = 0>
+    void CapacityOfParticlesInGrid (LayoutData<I>& mem, int lev) const;
+
     /**
     * \brief Returns # of particles at all levels
     *
diff --git a/Src/Particle/AMReX_ParticleContainerI.H b/Src/Particle/AMReX_ParticleContainerI.H
index 5927b0be7d0..aac96dad877 100644
--- a/Src/Particle/AMReX_ParticleContainerI.H
+++ b/Src/Particle/AMReX_ParticleContainerI.H
@@ -586,6 +586,25 @@ Long ParticleContainer_impl<ParticleType, NArrayReal, NArrayInt, Allocator, Cell
     return nparticles;
 }
 
+template <typename ParticleType, int NArrayReal, int NArrayInt,
+          template<class> class Allocator, class CellAssignor>
+template <typename I, std::enable_if_t<std::is_integral_v<I> && (sizeof(I) >= sizeof(Long)), int> FOO>
+void
+ParticleContainer_impl<ParticleType, NArrayReal, NArrayInt, Allocator, CellAssignor>::CapacityOfParticlesInGrid (LayoutData<I>& mem, int lev) const
+{
+    AMREX_ASSERT(lev >= 0 && lev < int(m_particles.size()));
+
+    AMREX_ASSERT(BoxArray::SameRefs(mem.boxArray(), ParticleBoxArray(lev)) &&
+                 mem.DistributionMap() == ParticleDistributionMap(lev));
+
+    [[maybe_unused]] Gpu::NoSyncRegion no_sync{};
+    for (ParConstIterType pti(*this, lev); pti.isValid(); ++pti)
+    {
+        int gid = pti.index();
+        mem[gid] += static_cast<I>(pti.capacity());
+    }
+}
+
 //
 // This includes both valid and invalid particles since invalid particles still take up space.
 //
diff --git a/build_docs.sh b/build_docs.sh
index 774238037f7..33e3df7ae64 100755
--- a/build_docs.sh
+++ b/build_docs.sh
@@ -7,6 +7,7 @@ cd Docs/Doxygen
 doxygen doxygen.conf &> doxygen.out
 if grep -q "warning:" doxygen.out; then
     echo "Doxygen warnings detected! Failing..."
+    cat doxygen.out
     exit 1
 fi
 cd ../..