Unify more functions in CUDA and OpenCL implementations of NBNXM
[alexxy/gromacs.git] / src / gromacs / nbnxm / opencl / nbnxm_ocl_data_mgmt.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5  * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  *  \brief Define OpenCL implementation of nbnxm_gpu_data_mgmt.h
38  *
39  *  \author Anca Hamuraru <anca@streamcomputing.eu>
40  *  \author Dimitrios Karkoulis <dimitris.karkoulis@gmail.com>
41  *  \author Teemu Virolainen <teemu@streamcomputing.eu>
42  *  \author Szilárd Páll <pall.szilard@gmail.com>
43  *  \ingroup module_nbnxm
44  */
45 #include "gmxpre.h"
46
47 #include <assert.h>
48 #include <stdarg.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include <cmath>
54
55 #include "gromacs/gpu_utils/device_stream_manager.h"
56 #include "gromacs/gpu_utils/oclutils.h"
57 #include "gromacs/hardware/device_information.h"
58 #include "gromacs/hardware/device_management.h"
59 #include "gromacs/math/vectypes.h"
60 #include "gromacs/mdlib/force_flags.h"
61 #include "gromacs/mdtypes/interaction_const.h"
62 #include "gromacs/mdtypes/md_enums.h"
63 #include "gromacs/nbnxm/atomdata.h"
64 #include "gromacs/nbnxm/gpu_data_mgmt.h"
65 #include "gromacs/nbnxm/gpu_jit_support.h"
66 #include "gromacs/nbnxm/nbnxm.h"
67 #include "gromacs/nbnxm/nbnxm_gpu.h"
68 #include "gromacs/nbnxm/nbnxm_gpu_data_mgmt.h"
69 #include "gromacs/nbnxm/pairlistsets.h"
70 #include "gromacs/pbcutil/ishift.h"
71 #include "gromacs/timing/gpu_timing.h"
72 #include "gromacs/utility/cstringutil.h"
73 #include "gromacs/utility/fatalerror.h"
74 #include "gromacs/utility/gmxassert.h"
75 #include "gromacs/utility/real.h"
76 #include "gromacs/utility/smalloc.h"
77
78 #include "nbnxm_ocl_types.h"
79
80 namespace Nbnxm
81 {
82
83 /*! \brief Copies of values from cl_driver_diagnostics_intel.h,
84  * which isn't guaranteed to be available. */
85 /**@{*/
86 #define CL_CONTEXT_SHOW_DIAGNOSTICS_INTEL 0x4106
87 #define CL_CONTEXT_DIAGNOSTICS_LEVEL_GOOD_INTEL 0x1
88 #define CL_CONTEXT_DIAGNOSTICS_LEVEL_BAD_INTEL 0x2
89 #define CL_CONTEXT_DIAGNOSTICS_LEVEL_NEUTRAL_INTEL 0x4
90 /**@}*/
91
92 /*! \brief This parameter should be determined heuristically from the
93  * kernel execution times
94  *
95  * This value is best for small systems on a single AMD Radeon R9 290X
96  * (and about 5% faster than 40, which is the default for CUDA
97  * devices). Larger simulation systems were quite insensitive to the
98  * value of this parameter.
99  */
100 static unsigned int gpu_min_ci_balanced_factor = 50;
101
102
103 /*! \brief Initializes the atomdata structure first time, it only gets filled at
104     pair-search.
105  */
106 static void init_atomdata_first(cl_atomdata_t* ad, int ntypes, const DeviceContext& deviceContext)
107 {
108     ad->ntypes = ntypes;
109
110     allocateDeviceBuffer(&ad->shift_vec, SHIFTS * DIM, deviceContext);
111     ad->bShiftVecUploaded = CL_FALSE;
112
113     allocateDeviceBuffer(&ad->fshift, SHIFTS * DIM, deviceContext);
114     allocateDeviceBuffer(&ad->e_lj, 1, deviceContext);
115     allocateDeviceBuffer(&ad->e_el, 1, deviceContext);
116
117     /* initialize to nullptr pointers to data that is not allocated here and will
118        need reallocation in nbnxn_gpu_init_atomdata */
119     ad->xq = nullptr;
120     ad->f  = nullptr;
121
122     /* size -1 indicates that the respective array hasn't been initialized yet */
123     ad->natoms = -1;
124     ad->nalloc = -1;
125 }
126
127 /*! \brief Returns the kinds of electrostatics and Vdw OpenCL
128  *  kernels that will be used.
129  *
130  * Respectively, these values are from enum eelOcl and enum
131  * evdwOcl. */
132 static void map_interaction_types_to_gpu_kernel_flavors(const interaction_const_t* ic,
133                                                         int                        combRule,
134                                                         int*                       gpu_eeltype,
135                                                         int*                       gpu_vdwtype)
136 {
137     if (ic->vdwtype == evdwCUT)
138     {
139         switch (ic->vdw_modifier)
140         {
141             case eintmodNONE:
142             case eintmodPOTSHIFT:
143                 switch (combRule)
144                 {
145                     case ljcrNONE: *gpu_vdwtype = evdwTypeCUT; break;
146                     case ljcrGEOM: *gpu_vdwtype = evdwTypeCUTCOMBGEOM; break;
147                     case ljcrLB: *gpu_vdwtype = evdwTypeCUTCOMBLB; break;
148                     default:
149                         gmx_incons(
150                                 "The requested LJ combination rule is not implemented in the "
151                                 "OpenCL GPU accelerated kernels!");
152                 }
153                 break;
154             case eintmodFORCESWITCH: *gpu_vdwtype = evdwTypeFSWITCH; break;
155             case eintmodPOTSWITCH: *gpu_vdwtype = evdwTypePSWITCH; break;
156             default:
157                 gmx_incons(
158                         "The requested VdW interaction modifier is not implemented in the GPU "
159                         "accelerated kernels!");
160         }
161     }
162     else if (ic->vdwtype == evdwPME)
163     {
164         if (ic->ljpme_comb_rule == ljcrGEOM)
165         {
166             *gpu_vdwtype = evdwTypeEWALDGEOM;
167         }
168         else
169         {
170             *gpu_vdwtype = evdwTypeEWALDLB;
171         }
172     }
173     else
174     {
175         gmx_incons("The requested VdW type is not implemented in the GPU accelerated kernels!");
176     }
177
178     if (ic->eeltype == eelCUT)
179     {
180         *gpu_eeltype = eelTypeCUT;
181     }
182     else if (EEL_RF(ic->eeltype))
183     {
184         *gpu_eeltype = eelTypeRF;
185     }
186     else if ((EEL_PME(ic->eeltype) || ic->eeltype == eelEWALD))
187     {
188         *gpu_eeltype = nbnxn_gpu_pick_ewald_kernel_type(*ic);
189     }
190     else
191     {
192         /* Shouldn't happen, as this is checked when choosing Verlet-scheme */
193         gmx_incons(
194                 "The requested electrostatics type is not implemented in the GPU accelerated "
195                 "kernels!");
196     }
197 }
198
199 /*! \brief Initializes the nonbonded parameter data structure.
200  */
201 static void init_nbparam(NBParamGpu*                     nbp,
202                          const interaction_const_t*      ic,
203                          const PairlistParams&           listParams,
204                          const nbnxn_atomdata_t::Params& nbatParams,
205                          const DeviceContext&            deviceContext)
206 {
207     set_cutoff_parameters(nbp, ic, listParams);
208
209     map_interaction_types_to_gpu_kernel_flavors(ic, nbatParams.comb_rule, &(nbp->eeltype), &(nbp->vdwtype));
210
211     if (ic->vdwtype == evdwPME)
212     {
213         if (ic->ljpme_comb_rule == ljcrGEOM)
214         {
215             GMX_ASSERT(nbatParams.comb_rule == ljcrGEOM, "Combination rule mismatch!");
216         }
217         else
218         {
219             GMX_ASSERT(nbatParams.comb_rule == ljcrLB, "Combination rule mismatch!");
220         }
221     }
222     /* generate table for PME */
223     nbp->coulomb_tab = nullptr;
224     if (nbp->eeltype == eelTypeEWALD_TAB || nbp->eeltype == eelTypeEWALD_TAB_TWIN)
225     {
226         GMX_RELEASE_ASSERT(ic->coulombEwaldTables, "Need valid Coulomb Ewald correction tables");
227         init_ewald_coulomb_force_table(*ic->coulombEwaldTables, nbp, deviceContext);
228     }
229     else
230     {
231         allocateDeviceBuffer(&nbp->coulomb_tab, 1, deviceContext);
232     }
233
234     const int nnbfp      = 2 * nbatParams.numTypes * nbatParams.numTypes;
235     const int nnbfp_comb = 2 * nbatParams.numTypes;
236
237     {
238         /* set up LJ parameter lookup table */
239         DeviceBuffer<real> nbfp;
240         initParamLookupTable(&nbfp, nullptr, nbatParams.nbfp.data(), nnbfp, deviceContext);
241         nbp->nbfp = nbfp;
242
243         if (ic->vdwtype == evdwPME)
244         {
245             DeviceBuffer<float> nbfp_comb;
246             initParamLookupTable(&nbfp_comb, nullptr, nbatParams.nbfp_comb.data(), nnbfp_comb, deviceContext);
247             nbp->nbfp_comb = nbfp_comb;
248         }
249     }
250 }
251
252 /*! \brief Initializes the OpenCL kernel pointers of the nbnxn_ocl_ptr_t input data structure. */
253 static cl_kernel nbnxn_gpu_create_kernel(NbnxmGpu* nb, const char* kernel_name)
254 {
255     cl_kernel kernel;
256     cl_int    cl_error;
257
258     kernel = clCreateKernel(nb->dev_rundata->program, kernel_name, &cl_error);
259     if (CL_SUCCESS != cl_error)
260     {
261         gmx_fatal(FARGS, "Failed to create kernel '%s' for GPU #%s: OpenCL error %d", kernel_name,
262                   nb->deviceContext_->deviceInfo().device_name, cl_error);
263     }
264
265     return kernel;
266 }
267
268 /*! \brief Clears nonbonded shift force output array and energy outputs on the GPU.
269  */
270 static void nbnxn_ocl_clear_e_fshift(NbnxmGpu* nb)
271 {
272
273     cl_int           cl_error;
274     cl_atomdata_t*   adat = nb->atdat;
275     cl_command_queue ls   = nb->deviceStreams[InteractionLocality::Local]->stream();
276
277     size_t local_work_size[3]  = { 1, 1, 1 };
278     size_t global_work_size[3] = { 1, 1, 1 };
279
280     cl_int shifts = SHIFTS * 3;
281
282     cl_int arg_no;
283
284     cl_kernel zero_e_fshift = nb->kernel_zero_e_fshift;
285
286     local_work_size[0] = 64;
287     // Round the total number of threads up from the array size
288     global_work_size[0] = ((shifts + local_work_size[0] - 1) / local_work_size[0]) * local_work_size[0];
289
290     arg_no   = 0;
291     cl_error = clSetKernelArg(zero_e_fshift, arg_no++, sizeof(cl_mem), &(adat->fshift));
292     cl_error |= clSetKernelArg(zero_e_fshift, arg_no++, sizeof(cl_mem), &(adat->e_lj));
293     cl_error |= clSetKernelArg(zero_e_fshift, arg_no++, sizeof(cl_mem), &(adat->e_el));
294     cl_error |= clSetKernelArg(zero_e_fshift, arg_no++, sizeof(cl_uint), &shifts);
295     GMX_ASSERT(cl_error == CL_SUCCESS, ocl_get_error_string(cl_error).c_str());
296
297     cl_error = clEnqueueNDRangeKernel(ls, zero_e_fshift, 3, nullptr, global_work_size,
298                                       local_work_size, 0, nullptr, nullptr);
299     GMX_ASSERT(cl_error == CL_SUCCESS, ocl_get_error_string(cl_error).c_str());
300 }
301
302 /*! \brief Initializes the OpenCL kernel pointers of the nbnxn_ocl_ptr_t input data structure. */
303 static void nbnxn_gpu_init_kernels(NbnxmGpu* nb)
304 {
305     /* Init to 0 main kernel arrays */
306     /* They will be later on initialized in select_nbnxn_kernel */
307     // TODO: consider always creating all variants of the kernels here so that there is no
308     // need for late call to clCreateKernel -- if that gives any advantage?
309     memset(nb->kernel_ener_noprune_ptr, 0, sizeof(nb->kernel_ener_noprune_ptr));
310     memset(nb->kernel_ener_prune_ptr, 0, sizeof(nb->kernel_ener_prune_ptr));
311     memset(nb->kernel_noener_noprune_ptr, 0, sizeof(nb->kernel_noener_noprune_ptr));
312     memset(nb->kernel_noener_prune_ptr, 0, sizeof(nb->kernel_noener_prune_ptr));
313
314     /* Init pruning kernels
315      *
316      * TODO: we could avoid creating kernels if dynamic pruning is turned off,
317      * but ATM that depends on force flags not passed into the initialization.
318      */
319     nb->kernel_pruneonly[epruneFirst] = nbnxn_gpu_create_kernel(nb, "nbnxn_kernel_prune_opencl");
320     nb->kernel_pruneonly[epruneRolling] =
321             nbnxn_gpu_create_kernel(nb, "nbnxn_kernel_prune_rolling_opencl");
322
323     /* Init auxiliary kernels */
324     nb->kernel_zero_e_fshift = nbnxn_gpu_create_kernel(nb, "zero_e_fshift");
325 }
326
327 /*! \brief Initializes simulation constant data.
328  *
329  *  Initializes members of the atomdata and nbparam structs and
330  *  clears e/fshift output buffers.
331  */
332 static void nbnxn_ocl_init_const(cl_atomdata_t*                  atomData,
333                                  NBParamGpu*                     nbParams,
334                                  const interaction_const_t*      ic,
335                                  const PairlistParams&           listParams,
336                                  const nbnxn_atomdata_t::Params& nbatParams,
337                                  const DeviceContext&            deviceContext)
338 {
339     init_atomdata_first(atomData, nbatParams.numTypes, deviceContext);
340     init_nbparam(nbParams, ic, listParams, nbatParams, deviceContext);
341 }
342
343
344 //! This function is documented in the header file
345 NbnxmGpu* gpu_init(const gmx::DeviceStreamManager& deviceStreamManager,
346                    const interaction_const_t*      ic,
347                    const PairlistParams&           listParams,
348                    const nbnxn_atomdata_t*         nbat,
349                    const bool                      bLocalAndNonlocal)
350 {
351     GMX_ASSERT(ic, "Need a valid interaction constants object");
352
353     auto nb            = new NbnxmGpu();
354     nb->deviceContext_ = &deviceStreamManager.context();
355     snew(nb->atdat, 1);
356     snew(nb->nbparam, 1);
357     snew(nb->plist[InteractionLocality::Local], 1);
358     if (bLocalAndNonlocal)
359     {
360         snew(nb->plist[InteractionLocality::NonLocal], 1);
361     }
362
363     nb->bUseTwoStreams = bLocalAndNonlocal;
364
365     nb->timers = new cl_timers_t();
366     snew(nb->timings, 1);
367
368     /* set device info, just point it to the right GPU among the detected ones */
369     nb->dev_rundata = new gmx_device_runtime_data_t();
370
371     /* init nbst */
372     pmalloc(reinterpret_cast<void**>(&nb->nbst.e_lj), sizeof(*nb->nbst.e_lj));
373     pmalloc(reinterpret_cast<void**>(&nb->nbst.e_el), sizeof(*nb->nbst.e_el));
374     pmalloc(reinterpret_cast<void**>(&nb->nbst.fshift), SHIFTS * sizeof(*nb->nbst.fshift));
375
376     init_plist(nb->plist[InteractionLocality::Local]);
377
378     /* OpenCL timing disabled if GMX_DISABLE_GPU_TIMING is defined. */
379     nb->bDoTime = (getenv("GMX_DISABLE_GPU_TIMING") == nullptr);
380
381     /* local/non-local GPU streams */
382     GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedLocal),
383                        "Local non-bonded stream should be initialized to use GPU for non-bonded.");
384     nb->deviceStreams[InteractionLocality::Local] =
385             &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedLocal);
386
387     if (nb->bUseTwoStreams)
388     {
389         init_plist(nb->plist[InteractionLocality::NonLocal]);
390
391         GMX_RELEASE_ASSERT(deviceStreamManager.streamIsValid(gmx::DeviceStreamType::NonBondedNonLocal),
392                            "Non-local non-bonded stream should be initialized to use GPU for "
393                            "non-bonded with domain decomposition.");
394         nb->deviceStreams[InteractionLocality::NonLocal] =
395                 &deviceStreamManager.stream(gmx::DeviceStreamType::NonBondedNonLocal);
396     }
397
398     if (nb->bDoTime)
399     {
400         init_timings(nb->timings);
401     }
402
403     nbnxn_ocl_init_const(nb->atdat, nb->nbparam, ic, listParams, nbat->params(), *nb->deviceContext_);
404
405     /* Enable LJ param manual prefetch for AMD or Intel or if we request through env. var.
406      * TODO: decide about NVIDIA
407      */
408     nb->bPrefetchLjParam = (getenv("GMX_OCL_DISABLE_I_PREFETCH") == nullptr)
409                            && ((nb->deviceContext_->deviceInfo().deviceVendor == DeviceVendor::Amd)
410                                || (nb->deviceContext_->deviceInfo().deviceVendor == DeviceVendor::Intel)
411                                || (getenv("GMX_OCL_ENABLE_I_PREFETCH") != nullptr));
412
413     /* NOTE: in CUDA we pick L1 cache configuration for the nbnxn kernels here,
414      * but sadly this is not supported in OpenCL (yet?). Consider adding it if
415      * it becomes supported.
416      */
417     nbnxn_gpu_compile_kernels(nb);
418     nbnxn_gpu_init_kernels(nb);
419
420     /* clear energy and shift force outputs */
421     nbnxn_ocl_clear_e_fshift(nb);
422
423     if (debug)
424     {
425         fprintf(debug, "Initialized OpenCL data structures.\n");
426     }
427
428     return nb;
429 }
430
431 /*! \brief Clears the first natoms_clear elements of the GPU nonbonded force output array.
432  */
433 static void nbnxn_ocl_clear_f(NbnxmGpu* nb, int natoms_clear)
434 {
435     if (natoms_clear == 0)
436     {
437         return;
438     }
439
440     cl_atomdata_t*      atomData    = nb->atdat;
441     const DeviceStream& localStream = *nb->deviceStreams[InteractionLocality::Local];
442
443     clearDeviceBufferAsync(&atomData->f, 0, natoms_clear * DIM, localStream);
444 }
445
446 //! This function is documented in the header file
447 void gpu_clear_outputs(NbnxmGpu* nb, bool computeVirial)
448 {
449     nbnxn_ocl_clear_f(nb, nb->atdat->natoms);
450     /* clear shift force array and energies if the outputs were
451        used in the current step */
452     if (computeVirial)
453     {
454         nbnxn_ocl_clear_e_fshift(nb);
455     }
456
457     /* kick off buffer clearing kernel to ensure concurrency with constraints/update */
458     cl_int gmx_unused cl_error;
459     cl_error = clFlush(nb->deviceStreams[InteractionLocality::Local]->stream());
460     GMX_ASSERT(cl_error == CL_SUCCESS, ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
461 }
462
463 //! This function is documented in the header file
464 void gpu_upload_shiftvec(NbnxmGpu* nb, const nbnxn_atomdata_t* nbatom)
465 {
466     cl_atomdata_t*      adat         = nb->atdat;
467     const DeviceStream& deviceStream = *nb->deviceStreams[InteractionLocality::Local];
468
469     /* only if we have a dynamic box */
470     if (nbatom->bDynamicBox || !adat->bShiftVecUploaded)
471     {
472         GMX_ASSERT(sizeof(float) * DIM == sizeof(*nbatom->shift_vec.data()),
473                    "Sizes of host- and device-side shift vectors should be the same.");
474         copyToDeviceBuffer(&adat->shift_vec, reinterpret_cast<const float*>(nbatom->shift_vec.data()),
475                            0, SHIFTS * DIM, deviceStream, GpuApiCallBehavior::Async, nullptr);
476         adat->bShiftVecUploaded = CL_TRUE;
477     }
478 }
479
480 //! This function is documented in the header file
481 void gpu_init_atomdata(NbnxmGpu* nb, const nbnxn_atomdata_t* nbat)
482 {
483     cl_int               cl_error;
484     int                  nalloc, natoms;
485     bool                 realloced;
486     bool                 bDoTime       = nb->bDoTime;
487     cl_timers_t*         timers        = nb->timers;
488     cl_atomdata_t*       d_atdat       = nb->atdat;
489     const DeviceContext& deviceContext = *nb->deviceContext_;
490     const DeviceStream&  deviceStream  = *nb->deviceStreams[InteractionLocality::Local];
491
492     natoms    = nbat->numAtoms();
493     realloced = false;
494
495     if (bDoTime)
496     {
497         /* time async copy */
498         timers->atdat.openTimingRegion(deviceStream);
499     }
500
501     /* need to reallocate if we have to copy more atoms than the amount of space
502        available and only allocate if we haven't initialized yet, i.e d_atdat->natoms == -1 */
503     if (natoms > d_atdat->nalloc)
504     {
505         nalloc = over_alloc_small(natoms);
506
507         /* free up first if the arrays have already been initialized */
508         if (d_atdat->nalloc != -1)
509         {
510             freeDeviceBuffer(&d_atdat->f);
511             freeDeviceBuffer(&d_atdat->xq);
512             freeDeviceBuffer(&d_atdat->lj_comb);
513             freeDeviceBuffer(&d_atdat->atom_types);
514         }
515
516
517         allocateDeviceBuffer(&d_atdat->f, nalloc * DIM, deviceContext);
518         allocateDeviceBuffer(&d_atdat->xq, nalloc * (DIM + 1), deviceContext);
519
520         if (useLjCombRule(nb->nbparam->vdwtype))
521         {
522             // Two Lennard-Jones parameters per atom
523             allocateDeviceBuffer(&d_atdat->lj_comb, nalloc * 2, deviceContext);
524         }
525         else
526         {
527             allocateDeviceBuffer(&d_atdat->atom_types, nalloc, deviceContext);
528         }
529
530         d_atdat->nalloc = nalloc;
531         realloced       = true;
532     }
533
534     d_atdat->natoms       = natoms;
535     d_atdat->natoms_local = nbat->natoms_local;
536
537     /* need to clear GPU f output if realloc happened */
538     if (realloced)
539     {
540         nbnxn_ocl_clear_f(nb, nalloc);
541     }
542
543     if (useLjCombRule(nb->nbparam->vdwtype))
544     {
545         GMX_ASSERT(sizeof(float) == sizeof(*nbat->params().lj_comb.data()),
546                    "Size of the LJ parameters element should be equal to the size of float2.");
547         copyToDeviceBuffer(&d_atdat->lj_comb, nbat->params().lj_comb.data(), 0, 2 * natoms,
548                            deviceStream, GpuApiCallBehavior::Async,
549                            bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
550     }
551     else
552     {
553         GMX_ASSERT(sizeof(int) == sizeof(*nbat->params().type.data()),
554                    "Sizes of host- and device-side atom types should be the same.");
555         copyToDeviceBuffer(&d_atdat->atom_types, nbat->params().type.data(), 0, natoms, deviceStream,
556                            GpuApiCallBehavior::Async, bDoTime ? timers->atdat.fetchNextEvent() : nullptr);
557     }
558
559     if (bDoTime)
560     {
561         timers->atdat.closeTimingRegion(deviceStream);
562     }
563
564     /* kick off the tasks enqueued above to ensure concurrency with the search */
565     cl_error = clFlush(deviceStream.stream());
566     GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
567                        ("clFlush failed: " + ocl_get_error_string(cl_error)).c_str());
568 }
569
570 /*! \brief Releases an OpenCL kernel pointer */
571 static void free_kernel(cl_kernel* kernel_ptr)
572 {
573     cl_int gmx_unused cl_error;
574
575     GMX_ASSERT(kernel_ptr, "Need a valid kernel pointer");
576
577     if (*kernel_ptr)
578     {
579         cl_error = clReleaseKernel(*kernel_ptr);
580         GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
581                            ("clReleaseKernel failed: " + ocl_get_error_string(cl_error)).c_str());
582
583         *kernel_ptr = nullptr;
584     }
585 }
586
587 /*! \brief Releases a list of OpenCL kernel pointers */
588 static void free_kernels(cl_kernel* kernels, int count)
589 {
590     int i;
591
592     for (i = 0; i < count; i++)
593     {
594         free_kernel(kernels + i);
595     }
596 }
597
598 /*! \brief Free the OpenCL program.
599  *
600  *  The function releases the OpenCL program assuciated with the
601  *  device that the calling PP rank is running on.
602  *
603  *  \param program [in]  OpenCL program to release.
604  */
605 static void freeGpuProgram(cl_program program)
606 {
607     if (program)
608     {
609         cl_int cl_error = clReleaseProgram(program);
610         GMX_RELEASE_ASSERT(cl_error == CL_SUCCESS,
611                            ("clReleaseProgram failed: " + ocl_get_error_string(cl_error)).c_str());
612         program = nullptr;
613     }
614 }
615
616 //! This function is documented in the header file
617 void gpu_free(NbnxmGpu* nb)
618 {
619     if (nb == nullptr)
620     {
621         return;
622     }
623
624     /* Free kernels */
625     int kernel_count = sizeof(nb->kernel_ener_noprune_ptr) / sizeof(nb->kernel_ener_noprune_ptr[0][0]);
626     free_kernels(nb->kernel_ener_noprune_ptr[0], kernel_count);
627
628     kernel_count = sizeof(nb->kernel_ener_prune_ptr) / sizeof(nb->kernel_ener_prune_ptr[0][0]);
629     free_kernels(nb->kernel_ener_prune_ptr[0], kernel_count);
630
631     kernel_count = sizeof(nb->kernel_noener_noprune_ptr) / sizeof(nb->kernel_noener_noprune_ptr[0][0]);
632     free_kernels(nb->kernel_noener_noprune_ptr[0], kernel_count);
633
634     kernel_count = sizeof(nb->kernel_noener_prune_ptr) / sizeof(nb->kernel_noener_prune_ptr[0][0]);
635     free_kernels(nb->kernel_noener_prune_ptr[0], kernel_count);
636
637     free_kernel(&(nb->kernel_zero_e_fshift));
638
639     /* Free atdat */
640     freeDeviceBuffer(&(nb->atdat->xq));
641     freeDeviceBuffer(&(nb->atdat->f));
642     freeDeviceBuffer(&(nb->atdat->e_lj));
643     freeDeviceBuffer(&(nb->atdat->e_el));
644     freeDeviceBuffer(&(nb->atdat->fshift));
645     freeDeviceBuffer(&(nb->atdat->lj_comb));
646     freeDeviceBuffer(&(nb->atdat->atom_types));
647     freeDeviceBuffer(&(nb->atdat->shift_vec));
648     sfree(nb->atdat);
649
650     /* Free nbparam */
651     freeDeviceBuffer(&(nb->nbparam->nbfp));
652     freeDeviceBuffer(&(nb->nbparam->nbfp_comb));
653     freeDeviceBuffer(&(nb->nbparam->coulomb_tab));
654     sfree(nb->nbparam);
655
656     /* Free plist */
657     auto* plist = nb->plist[InteractionLocality::Local];
658     freeDeviceBuffer(&plist->sci);
659     freeDeviceBuffer(&plist->cj4);
660     freeDeviceBuffer(&plist->imask);
661     freeDeviceBuffer(&plist->excl);
662     sfree(plist);
663     if (nb->bUseTwoStreams)
664     {
665         auto* plist_nl = nb->plist[InteractionLocality::NonLocal];
666         freeDeviceBuffer(&plist_nl->sci);
667         freeDeviceBuffer(&plist_nl->cj4);
668         freeDeviceBuffer(&plist_nl->imask);
669         freeDeviceBuffer(&plist_nl->excl);
670         sfree(plist_nl);
671     }
672
673     /* Free nbst */
674     pfree(nb->nbst.e_lj);
675     nb->nbst.e_lj = nullptr;
676
677     pfree(nb->nbst.e_el);
678     nb->nbst.e_el = nullptr;
679
680     pfree(nb->nbst.fshift);
681     nb->nbst.fshift = nullptr;
682
683     /* Free other events */
684     if (nb->nonlocal_done)
685     {
686         clReleaseEvent(nb->nonlocal_done);
687         nb->nonlocal_done = nullptr;
688     }
689     if (nb->misc_ops_and_local_H2D_done)
690     {
691         clReleaseEvent(nb->misc_ops_and_local_H2D_done);
692         nb->misc_ops_and_local_H2D_done = nullptr;
693     }
694
695     freeGpuProgram(nb->dev_rundata->program);
696     delete nb->dev_rundata;
697
698     /* Free timers and timings */
699     delete nb->timers;
700     sfree(nb->timings);
701     delete nb;
702
703     if (debug)
704     {
705         fprintf(debug, "Cleaned up OpenCL data structures.\n");
706     }
707 }
708
709 //! This function is documented in the header file
710 int gpu_min_ci_balanced(NbnxmGpu* nb)
711 {
712     return nb != nullptr ? gpu_min_ci_balanced_factor * nb->deviceContext_->deviceInfo().compute_units : 0;
713 }
714
715 } // namespace Nbnxm