Skip to content

spline_spherical_gpu_pattern

Avirup Sircar edited this page Jun 25, 2026 · 4 revisions

Spline & SphericalDataNumerical — GPU Port Pattern

Step 1: View for Spline (MyClassView / MyClass)

The Storage is MemoryStorage<double, memSpace>.

MemoryStorage<double, utils::MemorySpace::DEVICE> d_x_device; ... etc. 

the raw pointers of storage are passed directly into Spline::Func

Spline::Func<memSpace> is the non-owning view of Spline. It holds raw pointers into device (or host) memory and is safe to copy into kernels.

// Spline.h / SplineKernels.h
template <utils::MemorySpace memorySpace>
class Spline::Func {          // <- MyClassView analogue
public:
    Func();
    Func(const double* knotX, const double* knotY,
         const double* coefB, const double* coefC, const double* coefD,
         size_type nKnots, double c0,
         bool isSubdivGrid, double a, double r, size_type numSubDiv);

    DFTEFE_HOST_DEVICE_FUNC double eval(double xi) const;   // <- key method
    DFTEFE_HOST_DEVICE_FUNC double deriv(int order, double xi) const;

private:
    const double *d_knotX, *d_knotY, *d_coefB, *d_coefC, *d_coefD;
    size_type     d_nKnots;
    double        d_c0;
    bool          d_isSubdivGrid;
    double        d_a, d_r;
    size_type     d_numSubDiv;
};

// Spline — the owning class (MyClass analogue)
class Spline {
public:
    template <utils::MemorySpace memSpace>
    Func<memSpace> getFunc() const;     // <- view() analogue

    template <utils::MemorySpace memSpace>
    void evalAll(size_type n, const double* x, double* y,
                 deviceStream_t stream) const;

    void set_points(...);               // host-side spline fitting + syncToDevice

private:
    std::vector<double> d_x, d_y, d_b, d_c, d_d;  // host
    MemoryStorage<double, DEVICE> d_x_device, d_y_device,
                                  d_b_device, d_c_device, d_d_device; // device
};

SphericalDataNumerical::Func<memSpace> is the nested view that composes a Spline::Func with quantum numbers and cutoff parameters.

// SphericalDataNumerical.h / SphericalDataNumericalKernels.h
template <utils::MemorySpace memorySpace>
class SphericalDataNumerical::Func {   // <- MyClassView analogue (one level up)
public:
    Func();
    Func(utils::Spline::Func<memorySpace> radialSpline,
         int l, int m, int mEff,
         double constant, double cutoff, double smoothness,
         double polarAngleTolerance, double cutoffTolerance,
         double radiusTolerance);

    DFTEFE_HOST_DEVICE_FUNC double getValue(const double* point,
                                            const double* origin) const;
    DFTEFE_HOST_DEVICE_FUNC void   getGradientValue(const double* point,
                                                    const double* origin,
                                                    double* grad) const;
private:
    utils::Spline::Func<memorySpace> d_radialSpline; // <- embedded view of Spline
    int    d_l, d_m, d_mEff;
    double d_constant, d_cutoff, d_smoothness;
    double d_polarAngleTolerance, d_cutoffTolerance, d_radiusTolerance;
};

// SphericalDataNumerical — owning class (MyClass analogue)
class SphericalDataNumerical : public SphericalData {
public:
    template <utils::MemorySpace memSpace>
    Func<memSpace> getFunc() const;   // <- view() analogue

    void getValueDevice(size_type n, const double* points,
                        const double* origin, double* out,
                        deviceStream_t stream) override;
    void getGradientValueDevice(...) override;

private:
    std::shared_ptr<const utils::Spline> d_spline;  // <- owns the Spline
    std::vector<int> d_qNumbers;
    double d_cutoff, d_smoothness, d_polarAngleTolerance,
           d_cutoffTolerance, d_radiusTolerance;
};

Step 2: Policy (kernel functor using the View)

The DFTEFE_CREATE_KERNEL macros define the policy: a kernel functor that takes Spline::Func<DEVICE> by value (cheap copy, safe for GPU), then calls spline.eval() inside the kernel body.

// SphericalDataNumericalDeviceKernels.cpp
DFTEFE_CREATE_KERNEL(
    void,
    SphericalDataNumericalValueKernel,   // <- kernel name (policy)
    {
        // kernel body — the "operator()" analogue
        for (size_type i = globalThreadId; i < numPoints;
             i += nThreadsPerBlock * nThreadBlock)
        {
            // ... convert point to spherical ...
            const double radialValue = spline.eval(r);   // <- view's eval called
            const double cutoffValue = smoothCutoffValue(r, cutoff, smoothness);
            const double plmVal      = plm(l, mEff, cos(theta));
            const double qm          = Qm(m, phi);
            out[i] = radialValue * cutoffValue * constant * plmVal * qm;
        }
    },
    const size_type numPoints,
    const double*   points,
    const double*   origin,
    // ... cutoff, smoothness, l, m, mEff, constant ...
    const utils::Spline::Func<utils::MemorySpace::DEVICE> spline, // <- View passed by value
    double* out
);

Similarly for SplineEvalAllKernel in SplineDeviceKernels.cpp, but it takes the raw pointer arguments directly (no embedded Func object) since it is the lower-level kernel that Spline::evalAll calls.


Usage

// Owning objects live on the host
SphericalDataNumerical sdn(qNumbers, rPoints, rValues, cutoff, smoothness, shFunc);

// Views are cheap to produce (just copy of raw pointers + scalars), safe for device
utils::Spline::Func<utils::MemorySpace::DEVICE> splineView =
    d_spline->getFunc<utils::MemorySpace::DEVICE>();

SphericalDataNumerical::Func<utils::MemorySpace::DEVICE> sdnView =
    sdn.getFunc<utils::MemorySpace::DEVICE>();

// Batch launch — views passed by value into the kernel
// (inside getValueDevice / getGradientValueDevice):
DFTEFE_LAUNCH_KERNEL(
    SphericalDataNumericalValueKernel,
    numPoints / DEVICE_BLOCK_SIZE + 1,
    DEVICE_BLOCK_SIZE,
    streamId,
    numPoints, pointsDev, originDev,
    cutoff, smoothness, polarAngleTol,
    l, m, mEff, constant,
    splineView,      // <- non-owning, copied by value into kernel
    outDev
);

// For scalar per-point evaluation (host or device), use the Func directly:
// HOST:
auto hostFunc = sdn.getFunc<utils::MemorySpace::HOST>();
double val = hostFunc.getValue(point.data(), origin.data());

// DEVICE (inside a user kernel):
// auto devFunc = sdn.getFunc<utils::MemorySpace::DEVICE>();
// DFTEFE_CREATE_KERNEL(..., { val[i] = devFunc.getValue(points+3*i, origin); }, ...);

Clone this wiki locally