Skip to content

File momentummultiplicity.h

File List > algebra > coordinates > momentummultiplicity.h

Go to the documentation of this file

#ifndef TEMPLAT_LATTICE_ALGEBRA_COORDINATES_MULTIPLICITY_H
#define TEMPLAT_LATTICE_ALGEBRA_COORDINATES_MULTIPLICITY_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): Nicolás Loayza, Franz R. Sattler, Year: 2025

#include <vector>
#include <cmath>
#include <cstddef>
#include <utility>

#include "TempLat/lattice/algebra/helpers/isvariadicindex.h"
#include "TempLat/lattice/algebra/spacestateinterface.h"
#include "TempLat/util/exception.h"
#include "TempLat/lattice/memory/memorytoolbox.h"
#include "TempLat/util/constexpr_for.h"

namespace TempLat
{
  MakeException(MomentumMultiplicityWrongSpaceConfirmation);

  namespace detail
  {
    template <size_t Dim, size_t NDim, typename T>
    void iterateOctant(const device::Idx Nh, const device::Idx N, std::vector<int> &binP, device::Idx n2, int nEdge)
    {
      for (device::Idx a = 0; a <= Nh; ++a) {
        const device::Idx newN2 = n2 + a * a;
        const int newNEdge = nEdge + ((a == 0 || a == Nh) ? 1 : 0);

        if constexpr (Dim + 1 < NDim) {
          iterateOctant<Dim + 1, NDim, T>(Nh, N, binP, newN2, newNEdge);
        } else {
          // Innermost dimension: compute bin and accumulate
          if (newN2 == 0) continue; // skip the all-zero mode

          const int bin = static_cast<int>(std::sqrt(T(newN2)) + T(0.5)) - 1;
          binP[bin] += ((1 << NDim) >> newNEdge);
        }
      }
    }
  } // namespace detail

  template <size_t NDim> std::vector<int> getTypeIBinCounts(const device::Idx N)
  {
    const auto Nh = N / 2;

    // Max bin index: floor(sqrt(NDim) * Nh) + 1
    const int maxBin = static_cast<int>(std::sqrt(double(NDim)) * double(Nh)) + 1;
    std::vector<int> binP(maxBin, 0);

    detail::iterateOctant<0, NDim, double>(Nh, N, binP, 0, 0);

    return binP;
  }

  template <typename T, size_t NDim> class MomentumMultiplicity
  {
  public:
    MomentumMultiplicity(device::memory::host_ptr<MemoryToolBox<NDim>> toolBox)
        : mnGrid(toolBox->mNGridPointsVec[0]), mLayout(toolBox->mLayouts.getFourierSpaceLayout())
    {
      const auto binP = getTypeIBinCounts<NDim>(mnGrid);
      // Allocate device-accessible view and copy from host
      mmulti = device::memory::NDView<int, 1>("MomentumMultiplicity_bins", binP.size());
      device::memory::copyHostToDevice(binP.data(), mmulti);
    }

    DEVICE_FUNCTION
    MomentumMultiplicity(const MomentumMultiplicity &) = default;

    template <typename... IDX>
      requires IsVariadicNDIndex<NDim, IDX...>
    DEVICE_INLINE_FUNCTION T eval(const IDX &...idx) const
    {
      // Convert memory indices to spatial coordinates
      device::IdxArray<NDim> spatial;
      mLayout.putSpatialLocationFromMemoryIndexInto(spatial, idx...);

      const device::Idx half = mnGrid / 2;

      // Fold spatial coordinates (which may be negative) to [0, Nh]
      size_t n2 = 0;
      bool allZero = true;
      bool allHalf = true;
      for (size_t d = 0; d < NDim; ++d) {
        const size_t folded = static_cast<size_t>((spatial[d] >= 0) ? spatial[d] : -spatial[d]);
        n2 += folded * folded;
        if (folded != 0) allZero = false;
        if (folded != static_cast<size_t>(half)) allHalf = false;
      }

      T pair = T(1);

      if (!allZero) {
        const int bin = static_cast<int>(std::sqrt(T(n2)) + T(0.5)) - 1;
        pair = T(mmulti(bin));
      }

      if (allHalf) {
        pair = T(1 << NDim);
      }

      return T(1) / pair;
    }

    static std::string toString() { return "MomentumMultiplicity"; }

    void confirmSpace(device::Idx i, const LayoutStruct<NDim> &newLayout, const SpaceStateType &spaceType) const
    {
      switch (spaceType) {
      case SpaceStateType::Configuration:
        throw MomentumMultiplicityWrongSpaceConfirmation(
            "MomentumMultiplicity explicitly only can be used in fourier space. Abort.");
        break;
      case SpaceStateType::Fourier:
      default:
        break;
      }
    }

  private:
    device::Idx mnGrid;
    LayoutStruct<NDim> mLayout;
    device::memory::NDView<int, 1> mmulti;
  };

} // namespace TempLat

#endif