Refactor PME tests for better usability
[alexxy/gromacs.git] / src / gromacs / ewald / tests / pmetestcommon.h
index d57bdb103e7ad618bb5a679ec59a7536b62aabcb..1dc2db66b49b1add82b799b8fb29c63ef1e5c237 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, by the GROMACS development team, led by
  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
  * and including many others, as listed in the AUTHORS file in the
  * top-level source directory and at http://www.gromacs.org.
 #include <map>
 #include <vector>
 
-#include <gtest/gtest.h>
-
 #include "gromacs/ewald/pme.h"
-#include "gromacs/ewald/pme-gpu-internal.h"
+#include "gromacs/ewald/pme_gpu_internal.h"
 #include "gromacs/math/gmxcomplex.h"
-#include "gromacs/utility/arrayref.h"
+#include "gromacs/mdtypes/state_propagator_data_gpu.h"
 #include "gromacs/utility/unique_cptr.h"
 
-#include "testhardwarecontexts.h"
+#include "testutils/test_device.h"
 
 namespace gmx
 {
+
+template<typename>
+class ArrayRef;
+
 namespace test
 {
 
+//! Hardware code path being tested
+enum class CodePath : int
+{
+    //! CPU code path
+    CPU,
+    //! GPU code path
+    GPU,
+    //! Total number of code paths
+    Count
+};
+
 // Convenience typedefs
 //! A safe pointer type for PME.
 typedef gmx::unique_cptr<gmx_pme_t, gmx_pme_destroy> PmeSafePointer;
 //! Charges
-typedef ArrayRef<const real> ChargesVector;
+typedef std::vector<real> ChargesVector;
 //! Coordinates
 typedef std::vector<RVec> CoordinatesVector;
 //! Forces
 typedef ArrayRef<RVec> ForcesVector;
 //! Gridline indices
-typedef ArrayRef<const IVec> GridLineIndicesVector;
+typedef std::vector<IVec> GridLineIndicesVector;
 /*! \brief Spline parameters (theta or dtheta).
  * A reference to a single dimension's spline data; this means (atomCount * pmeOrder) values or derivatives.
  */
@@ -81,13 +94,15 @@ typedef ArrayRef<const real> SplineParamsDimVector;
 typedef std::array<SplineParamsDimVector, DIM> SplineParamsVector;
 
 //! Non-zero grid values for test input; keys are 3d indices (IVec)
-template<typename ValueType>using SparseGridValuesInput = std::map<IVec, ValueType>;
+template<typename ValueType>
+using SparseGridValuesInput = std::map<IVec, ValueType>;
 //! Non-zero real grid values
 typedef SparseGridValuesInput<real> SparseRealGridValuesInput;
 //! Non-zero complex grid values
 typedef SparseGridValuesInput<t_complex> SparseComplexGridValuesInput;
 //! Non-zero grid values for test output; keys are string representations of the cells' 3d indices (IVec); this allows for better sorting.
-template<typename ValueType>using SparseGridValuesOutput = std::map<std::string, ValueType>;
+template<typename ValueType>
+using SparseGridValuesOutput = std::map<std::string, ValueType>;
 //! Non-zero real grid values
 typedef SparseGridValuesOutput<real> SparseRealGridValuesOutput;
 //! Non-zero complex grid values
@@ -95,84 +110,146 @@ typedef SparseGridValuesOutput<t_complex> SparseComplexGridValuesOutput;
 //! TODO: make proper C++ matrix for the whole Gromacs, get rid of this
 typedef std::array<real, DIM * DIM> Matrix3x3;
 //! PME solver type
-enum class PmeSolveAlgorithm
+enum class PmeSolveAlgorithm : int
 {
+    //! Coulomb electrostatics
     Coulomb,
+    //! Lennard-Jones
     LennardJones,
+    //! Total number of solvers
+    Count
 };
-//! PME solver results - reciprocal energy and virial
-typedef std::tuple<real, Matrix3x3> PmeSolveOutput;
 
 // Misc.
 
 //! Tells if this generally valid PME input is supported for this mode
-bool pmeSupportsInputForMode(const t_inputrec *inputRec, CodePath mode);
+bool pmeSupportsInputForMode(const gmx_hw_info_t& hwinfo, const t_inputrec* inputRec, CodePath mode);
 
 //! Spline moduli are computed in double precision, so they're very good in single precision
-constexpr gmx_int64_t c_splineModuliSinglePrecisionUlps = 1;
-//! Otherwise, there is lots of multiplication and addition
-constexpr gmx_int64_t c_splineModuliDoublePrecisionUlps = 12;
+constexpr int64_t c_splineModuliSinglePrecisionUlps = 1;
+/*! \brief For double precision checks, the recursive interpolation
+ * and use of trig functions in make_dft_mod require a lot more flops,
+ * and thus opportunity for deviation between implementations. */
+uint64_t getSplineModuliDoublePrecisionUlps(int splineOrder);
 
 // PME stages
 
-// TODO: currently PME initializations do not store CodePath. They probably should (unless we would need mixed CPU-GPU execution?).
-//! Simple PME initialization (no atom data)
-PmeSafePointer pmeInitEmpty(const t_inputrec *inputRec,
-                            CodePath mode = CodePath::CPU,
-                            gmx_device_info_t *gpuInfo = nullptr,
-                            const Matrix3x3 &box = {{1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}},
-                            real ewaldCoeff_q = 0.0f, real ewaldCoeff_lj = 0.0f);
+//! PME initialization
+PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
+                              CodePath             mode,
+                              const DeviceContext* deviceContext,
+                              const DeviceStream*  deviceStream,
+                              const PmeGpuProgram* pmeGpuProgram,
+                              const Matrix3x3&     box,
+                              real                 ewaldCoeff_q  = 1.0F,
+                              real                 ewaldCoeff_lj = 1.0F);
+
+//! Simple PME initialization based on inputrec only
+PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec);
+
+//! Make a GPU state-propagator manager
+std::unique_ptr<StatePropagatorDataGpu> makeStatePropagatorDataGpu(const gmx_pme_t&     pme,
+                                                                   const DeviceContext* deviceContext,
+                                                                   const DeviceStream* deviceStream);
 //! PME initialization with atom data and system box
-PmeSafePointer pmeInitAtoms(const t_inputrec         *inputRec,
-                            CodePath                  mode,
-                            gmx_device_info_t        *gpuInfo,
-                            const CoordinatesVector  &coordinates,
-                            const ChargesVector      &charges,
-                            const Matrix3x3          &box
-                            );
+void pmeInitAtoms(gmx_pme_t*               pme,
+                  StatePropagatorDataGpu*  stateGpu,
+                  CodePath                 mode,
+                  const CoordinatesVector& coordinates,
+                  const ChargesVector&     charges);
 //! PME spline computation and charge spreading
-void pmePerformSplineAndSpread(gmx_pme_t *pme, CodePath mode,
-                               bool computeSplines, bool spreadCharges);
+void pmePerformSplineAndSpread(gmx_pme_t* pme, CodePath mode, bool computeSplines, bool spreadCharges);
 //! PME solving
-void pmePerformSolve(const gmx_pme_t *pme, CodePath mode,
-                     PmeSolveAlgorithm method, real cellVolume,
-                     GridOrdering gridOrdering, bool computeEnergyAndVirial);
+void pmePerformSolve(const gmx_pme_t*  pme,
+                     CodePath          mode,
+                     PmeSolveAlgorithm method,
+                     real              cellVolume,
+                     GridOrdering      gridOrdering,
+                     bool              computeEnergyAndVirial);
 //! PME force gathering
-void pmePerformGather(gmx_pme_t *pme, CodePath mode,
-                      PmeForceOutputHandling inputTreatment, ForcesVector &forces);
+void pmePerformGather(gmx_pme_t*    pme,
+                      CodePath      mode,
+                      ForcesVector& forces); //NOLINT(google-runtime-references)
 //! PME test finalization before fetching the outputs
-void pmeFinalizeTest(const gmx_pme_t *pme, CodePath mode);
+void pmeFinalizeTest(const gmx_pme_tpme, CodePath mode);
 
 // PME state setters
 
 //! Setting atom spline values or derivatives to be used in spread/gather
-void pmeSetSplineData(const gmx_pme_t *pme, CodePath mode,
-                      const SplineParamsDimVector &splineValues, PmeSplineDataType type, int dimIndex);
+void pmeSetSplineData(const gmx_pme_t*             pme,
+                      CodePath                     mode,
+                      const SplineParamsDimVector& splineValues,
+                      PmeSplineDataType            type,
+                      int                          dimIndex);
+
 //! Setting gridline indices be used in spread/gather
-void pmeSetGridLineIndices(const gmx_pme_t *pme, CodePath mode,
-                           const GridLineIndicesVector &gridLineIndices);
+void pmeSetGridLineIndices(gmx_pme_t* pme, CodePath mode, const GridLineIndicesVector& gridLineIndices);
 //! Setting real grid to be used in gather
-void pmeSetRealGrid(const gmx_pme_t *pme, CodePath mode,
-                    const SparseRealGridValuesInput &gridValues);
-void pmeSetComplexGrid(const gmx_pme_t *pme, CodePath mode, GridOrdering gridOrdering,
-                       const SparseComplexGridValuesInput &gridValues);
+void pmeSetRealGrid(const gmx_pme_t* pme, CodePath mode, const SparseRealGridValuesInput& gridValues);
+void pmeSetComplexGrid(const gmx_pme_t*                    pme,
+                       CodePath                            mode,
+                       GridOrdering                        gridOrdering,
+                       const SparseComplexGridValuesInput& gridValues);
 
 // PME state getters
 
 //! Getting the single dimension's spline values or derivatives
-SplineParamsDimVector pmeGetSplineData(const gmx_pme_t *pme, CodePath mode,
-                                       PmeSplineDataType type, int dimIndex);
+SplineParamsDimVector pmeGetSplineData(const gmx_pme_t* pme, CodePath mode, PmeSplineDataType type, int dimIndex);
 //! Getting the gridline indices
-GridLineIndicesVector pmeGetGridlineIndices(const gmx_pme_t *pme, CodePath mode);
+GridLineIndicesVector pmeGetGridlineIndices(const gmx_pme_tpme, CodePath mode);
 //! Getting the real grid (spreading output of pmePerformSplineAndSpread())
-SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t *pme, CodePath mode);
+SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_tpme, CodePath mode);
 //! Getting the complex grid output of pmePerformSolve()
-SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t *pme, CodePath mode,
-                                                GridOrdering gridOrdering);
+SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering);
 //! Getting the reciprocal energy and virial
-PmeSolveOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t *pme, CodePath mode,
-                                               PmeSolveAlgorithm method);
-}
-}
+PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method);
+
+struct PmeTestHardwareContext
+{
+    //! Hardware path for the code being tested.
+    CodePath codePath_;
+    //! Returns a human-readable context description line
+    std::string description() const;
+    //! Pointer to the global test hardware device (if on GPU)
+    TestDevice* testDevice_ = nullptr;
+    //! PME GPU program if needed
+    PmeGpuProgramStorage pmeGpuProgram_ = nullptr;
+    // Constructor for CPU context
+    PmeTestHardwareContext();
+    // Constructor for GPU context
+    explicit PmeTestHardwareContext(TestDevice* testDevice);
+
+    //! Get the code path
+    CodePath codePath() const { return codePath_; }
+    //! Get the PME GPU program
+    const PmeGpuProgram* pmeGpuProgram() const
+    {
+        return codePath() == CodePath::GPU ? pmeGpuProgram_.get() : nullptr;
+    }
+
+    const DeviceContext* deviceContext() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceContext() : nullptr;
+    }
+
+    const DeviceStream* deviceStream() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceStream() : nullptr;
+    }
+
+    //! Activate the context (set the device)
+    void activate() const;
+};
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList();
+
+//! A couple of valid inputs for boxes.
+extern const std::map<std::string, Matrix3x3> c_inputBoxes;
+
+//! Valid PME orders for testing
+extern std::vector<int> c_inputPmeOrders;
+
+} // namespace test
+} // namespace gmx
 
-#endif
+#endif // GMX_EWALD_PME_TEST_COMMON_H