/*
* 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 <vector>
#include "gromacs/ewald/pme.h"
-#include "gromacs/utility/arrayref.h"
+#include "gromacs/ewald/pme_gpu_internal.h"
+#include "gromacs/math/gmxcomplex.h"
+#include "gromacs/mdtypes/state_propagator_data_gpu.h"
#include "gromacs/utility/unique_cptr.h"
-struct t_inputrec;
+#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;
typedef std::vector<real> ChargesVector;
//! Coordinates
typedef std::vector<RVec> CoordinatesVector;
+//! Forces
+typedef ArrayRef<RVec> ForcesVector;
//! Gridline indices
-typedef ConstArrayRef<IVec> GridLineIndicesVector;
-//! Type of spline data
-enum class PmeSplineDataType
-{
- Values, // theta
- Derivatives, //dtheta
-};
+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.
*/
-typedef ConstArrayRef<real> SplineParamsVector;
-//! Non-zero grid values; keys are string representations of the cells' 3d indices (IVec)
-typedef std::map<std::string, real> SparseGridValues;
+typedef ArrayRef<const real> SplineParamsDimVector;
+/*! \brief Spline parameters (theta or dtheta) in all 3 dimensions
+ */
+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>;
+//! 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>;
+//! Non-zero real grid values
+typedef SparseGridValuesOutput<real> SparseRealGridValuesOutput;
+//! Non-zero complex grid values
+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 code path being tested
-enum class CodePath
+//! PME solver type
+enum class PmeSolveAlgorithm : int
{
- CPU, // serial CPU code
+ //! Coulomb electrostatics
+ Coulomb,
+ //! Lennard-Jones
+ LennardJones,
+ //! Total number of solvers
+ Count
};
+// Misc.
+
+//! Tells if this generally valid PME input is supported for this 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 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
-//! Simple PME initialization based on input, no atom data; only good for testing the initialization stage
-PmeSafePointer pmeInitEmpty(const t_inputrec *inputRec);
+//! 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 pmeInitWithAtoms(const t_inputrec *inputRec,
- 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);
+//! PME force gathering
+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);
+
+// 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);
+
+//! Setting gridline indices be used in spread/gather
+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);
// PME state getters
//! Getting the single dimension's spline values or derivatives
-SplineParamsVector 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_t* pme, CodePath mode);
+//! Getting the real grid (spreading output of pmePerformSplineAndSpread())
+SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t* pme, CodePath mode);
+//! Getting the complex grid output of pmePerformSolve()
+SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering);
+//! Getting the reciprocal energy and virial
+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;
-//! Getting the real grid (spreading output of PmePerformSplineAndSpread())
-SparseGridValues pmeGetRealGrid(const gmx_pme_t *pme, CodePath mode);
+//! Valid PME orders for testing
+extern std::vector<int> c_inputPmeOrders;
-}
-}
+} // namespace test
+} // namespace gmx
-#endif
+#endif // GMX_EWALD_PME_TEST_COMMON_H