File wallaverager.h
File List > code_source > templat > include > TempLat > lattice > measuringtools > wallaverager.h
Go to the documentation of this file
#ifndef TEMPLAT_LATTICE_MEASUREMENTS_WALLAVERAGER_H
#define TEMPLAT_LATTICE_MEASUREMENTS_WALLAVERAGER_H
/* This file is part of TempLat, available at https://cosmolattice.github.io/templat .
Copyright 2021-2026 The TempLat authors, see AUTHORS.md.
Released under the MIT license, see LICENSE.md. */
// File info: Main contributor(s): Adrien Florio, Year: 2025
#include "TempLat/util/getcpptypename.h"
#include "TempLat/lattice/algebra/helpers/getgetreturntype.h"
#include "TempLat/lattice/algebra/helpers/doeval.h"
#include "TempLat/lattice/algebra/helpers/getstring.h"
#include "TempLat/lattice/algebra/helpers/getndim.h"
#include "TempLat/lattice/algebra/helpers/haseval.h"
#include "TempLat/lattice/measuringtools/averagerhelper.h"
#include "TempLat/parallel/device_memory.h"
#include "TempLat/parallel/device_iteration.h"
namespace TempLat
{
template <typename T> class WallAverager
{
public:
using vType = typename GetGetReturnType<T>::type;
static constexpr bool isComplexValued = GetGetReturnType<T>::isComplex;
static constexpr size_t NDim = GetNDim::get<T>();
WallAverager(const T &pT, SpaceStateType spaceType)
requires requires {
{ pT.getToolBox() } -> std::same_as<device::memory::host_ptr<MemoryToolBox<NDim>>>;
}
: mT(pT), mSpaceType(spaceType)
{
if (mSpaceType != SpaceStateType::Configuration)
throw AveragerWrongSpace("Wall averager works only in configuration space.");
mToolBox = mT.getToolBox();
if (mToolBox == nullptr) throw std::runtime_error("WallAverager: ToolBox is null.");
const auto layout = mToolBox->mLayouts.getConfigSpaceLayout();
const auto localSizes = layout.getLocalSizes();
mLocalStarts = layout.getLocalStarts();
nGhosts = layout.getNGhosts();
// starts = absolute start positions; stops = counts (sizes)
// getLocalKokkosPolicy computes: kokkos_stop[d] = starts[d] + stops[d]
for (size_t d = 0; d < NDim; ++d) {
mStartIteration[d] = nGhosts;
mStopIteration[d] = localSizes[d];
mLocalSizes[d] = localSizes[d];
}
// Allocate device result buffer (sized for max local dimension)
size_t maxLocalSize = 0;
for (size_t d = 0; d < NDim; ++d)
maxLocalSize = device::max(maxLocalSize, static_cast<size_t>(localSizes[d]));
mMaxLocalSize = maxLocalSize;
mDeviceResult = device::memory::NDView<vType, 1>("wallResult", maxLocalSize);
// Host workspace: NDim vectors, each of global size N[t]
for (size_t t = 0; t < NDim; ++t)
mWorkspace[t].assign(mToolBox->mNGridPointsVec[t], vType{});
}
void compute()
{
AveragerHelper<vType, isComplexValued>::onBeforeAverageConfiguration(mT, mSpaceType);
computeConfigurationSpace();
// MPI Allreduce per dimension (vector overload, in-place)
for (size_t t = 0; t < NDim; ++t)
mToolBox->mGroup.getBaseComm().Allreduce(&mWorkspace[t], MPI_SUM);
// Normalize by codimension
for (size_t t = 0; t < NDim; ++t) {
vType codim = 1;
for (size_t j = 0; j < NDim; ++j)
if (j != t) codim *= mToolBox->mNGridPointsVec[j];
for (auto &val : mWorkspace[t])
val /= codim;
}
}
void computeConfigurationSpace()
{
auto functor = DEVICE_CLASS_LAMBDA(const device::IdxArray<NDim> &idx, vType &update)
{
device::apply([&](auto &&...args) { update += DoEval::eval(mT, args...); }, idx);
};
for (size_t t = 0; t < NDim; ++t) {
// Zero the host workspace for this dimension
std::fill(mWorkspace[t].begin(), mWorkspace[t].end(), vType{});
for (size_t local_j = 0; local_j < static_cast<size_t>(mLocalSizes[t]); ++local_j) {
// Restrict dimension t to a single slice
// starts = absolute positions, stops = counts
auto cur_start = mStartIteration;
auto cur_stop = mStopIteration;
cur_start[t] = nGhosts + local_j;
cur_stop[t] = 1; // count of 1 along dimension t
// Reduce this hyperplane into a single element of the device buffer
device::iteration::reduce("WallAverager", cur_start, cur_stop, functor,
device::memory::subview(mDeviceResult, local_j));
}
// Copy device results to host via appropriately-sized subview
size_t curLocalSize = static_cast<size_t>(mLocalSizes[t]);
std::vector<vType> localResults(curLocalSize, vType{});
if (curLocalSize == mMaxLocalSize) {
device::memory::copyDeviceToHost(mDeviceResult, localResults.data());
} else {
auto sliceView = device::memory::subview(mDeviceResult, std::pair<size_t, size_t>(0, curLocalSize));
device::memory::copyDeviceToHost(sliceView, localResults.data());
}
// Map local indices to global workspace positions
for (size_t local_j = 0; local_j < curLocalSize; ++local_j) {
size_t global_j = mLocalStarts[t] + local_j;
mWorkspace[t][global_j] = localResults[local_j];
}
}
}
std::string toString() const { return "<" + GetString::get(mT) + ">_walls"; }
inline auto getToolBox() const { return GetToolBox::get(mT); }
auto getWall(size_t dim) const { return mWorkspace[dim]; }
private:
T mT;
SpaceStateType mSpaceType;
device::memory::host_ptr<MemoryToolBox<NDim>> mToolBox;
device::array<device::Idx, NDim> mStartIteration{};
device::array<device::Idx, NDim> mStopIteration{};
device::IdxArray<NDim> mLocalSizes{};
device::IdxArray<NDim> mLocalStarts{};
device::memory::NDView<vType, 1> mDeviceResult;
size_t mMaxLocalSize;
std::array<std::vector<vType>, NDim> mWorkspace;
size_t nGhosts;
};
template <typename T>
requires HasEvalMethod<T>
auto wallAverager(T instance, SpaceStateType spaceType = SpaceStateType::Configuration)
{
return WallAverager<T>(instance, spaceType);
}
auto wallAverager(ZeroType a) { return 0; }
} // namespace TempLat
#endif