Merge branch origin/release-2020 into master
authorPaul Bauer <paul.bauer.q@gmail.com>
Tue, 7 Jan 2020 13:09:57 +0000 (14:09 +0100)
committerPaul Bauer <paul.bauer.q@gmail.com>
Tue, 7 Jan 2020 13:09:57 +0000 (14:09 +0100)
Final merge needed for release-2020 into master

Change-Id: I93a44684f002997dfa8590c865103b436f3e3e93

16 files changed:
1  2 
admin/builds/gromacs.py
cmake/gmxVersionInfo.cmake
docs/CMakeLists.txt
docs/release-notes/index.rst
docs/user-guide/environment-variables.rst
src/gromacs/fileio/tests/CMakeLists.txt
src/gromacs/fileio/tests/fileioxdrserializer.cpp
src/gromacs/gmxpreprocess/topio.cpp
src/gromacs/listed_forces/bonded.cpp
src/gromacs/mdlib/lincs_cuda.cu
src/gromacs/mdlib/sim_util.cpp
src/gromacs/mdrun/runner.cpp
src/gromacs/taskassignment/decidegpuusage.cpp
src/gromacs/taskassignment/decidegpuusage.h
src/gromacs/utility/inmemoryserializer.cpp
src/gromacs/utility/tests/inmemoryserializer.cpp

diff --combined admin/builds/gromacs.py
index e429c623bc650b256c17992060950b8a5cd426dc,a734f2cd8b9bec82dda142602e4d65350ae38ab1..a8a4f7047df1ca6bb64849489056016bc1146732
@@@ -1,7 -1,7 +1,7 @@@
  #
  # This file is part of the GROMACS molecular simulation package.
  #
--# Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
++# Copyright (c) 2015,2016,2017,2018,2019,2020, 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.
@@@ -186,6 -186,8 +186,8 @@@ def do_build(context)
      # GPU update flag enables GPU update+constraints as well as buffer ops (dependency)
      if context.opts.gpuupdate:
          context.env.set_env_var('GMX_FORCE_UPDATE_DEFAULT_GPU', "1")
+         context.env.set_env_var('GMX_GPU_DD_COMMS', "1")
+         context.env.set_env_var('GMX_GPU_PME_PP_COMMS', "1")
  
      regressiontests_path = context.workspace.get_project_dir(Project.REGRESSIONTESTS)
  
index 42f59adeab0737fe75f04e4aebe7bfbfb52148d0,e9aca7b220d49ce5887bac2828d21995c0cd85ec..d5788c7ff3107267df35249bc42f475262f5fc49
@@@ -58,7 -58,6 +58,7 @@@
  #         GROMACS     2018   3
  #         GROMACS     2019   4
  #         GROMACS     2020   5
 +#         GROMACS     2021   6
  #   LIBRARY_SOVERSION_MINOR so minor version for the built libraries.
  #       Should be increased for each release that changes only the implementation.
  #       In GROMACS, the typical policy is to increase it for each patch version
  
  # The GROMACS convention is that these are the version number of the next
  # release that is going to be made from this branch.
 -set(GMX_VERSION_MAJOR 2020)
 -set(GMX_VERSION_PATCH 1)
 +set(GMX_VERSION_MAJOR 2021)
 +set(GMX_VERSION_PATCH 0)
  # The suffix, on the other hand, is used mainly for betas and release
  # candidates, where it signifies the most recent such release from
  # this branch; it will be empty before the first such release, as well
@@@ -213,7 -212,7 +213,7 @@@ set(GMX_VERSION_SUFFIX ""
  # here. The important thing is to minimize the chance of third-party
  # code being able to dynamically link with a version of libgromacs
  # that might not work.
 -set(LIBRARY_SOVERSION_MAJOR 5)
 +set(LIBRARY_SOVERSION_MAJOR 6)
  set(LIBRARY_SOVERSION_MINOR 0)
  set(LIBRARY_VERSION ${LIBRARY_SOVERSION_MAJOR}.${LIBRARY_SOVERSION_MINOR}.0)
  
@@@ -255,13 -254,13 +255,13 @@@ if (NOT SOURCE_IS_SOURCE_DISTRIBUTION A
  endif()
  
  set(REGRESSIONTEST_VERSION "${GMX_VERSION_STRING}")
 -set(REGRESSIONTEST_BRANCH "refs/heads/release-2020")
 +set(REGRESSIONTEST_BRANCH "refs/heads/master")
  # Run the regressiontests packaging job with the correct pakage
  # version string, and the release box checked, in order to have it
  # build the regressiontests tarball with all the right naming. The
  # naming affects the md5sum that has to go here, and if it isn't right
  # release workflow will report a failure.
- set(REGRESSIONTEST_MD5SUM "42e3bfe74a8e8bf8e38919e10aaf8fa1" CACHE INTERNAL "MD5 sum of the regressiontests tarball for this GROMACS version")
+ set(REGRESSIONTEST_MD5SUM "2fe8e35878bc9ee3cf60e92d5b250175" CACHE INTERNAL "MD5 sum of the regressiontests tarball for this GROMACS version")
  
  math(EXPR GMX_VERSION_NUMERIC
       "${GMX_VERSION_MAJOR}*10000 + ${GMX_VERSION_PATCH}")
diff --combined docs/CMakeLists.txt
index 99310f3417a84c977c67c63110ea08217d74de8c,67ba59f7464b0a4c1871320172bb6859f53f1b84..3830be1293ef17df9af99d03faa440195f5c3a27
@@@ -361,15 -361,7 +361,16 @@@ if (SPHINX_FOUND
          how-to/visualize.rst
          install-guide/index.rst
          release-notes/index.rst
 +        release-notes/2021/major/highlights.rst
 +        release-notes/2021/major/features.rst
 +        release-notes/2021/major/performance.rst
 +        release-notes/2021/major/tools.rst
 +        release-notes/2021/major/bugs-fixed.rst
 +        release-notes/2021/major/removed-functionality.rst
 +        release-notes/2021/major/deprecated-functionality.rst
 +        release-notes/2021/major/portability.rst
 +        release-notes/2021/major/miscellaneous.rst
+         release-notes/2020/2020.1.rst
          release-notes/2020/major/highlights.rst
          release-notes/2020/major/features.rst
          release-notes/2020/major/performance.rst
  
      # Sphinx cache with pickled ReST documents
      set(SPHINX_CACHE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_doctrees")
 +    set(SPHINX_CONFIG_OVERRIDES "")
 +    if (GMX_DEVELOPER_BUILD)
 +        set(SPHINX_CONFIG_OVERRIDES "-D todo_include_todos=1")
 +    endif()
      add_custom_target(webpage-sphinx
          DEPENDS sphinx-programs
          DEPENDS sphinx-input
              -q -b html
              -w sphinx-html.log
              -d "${SPHINX_CACHE_DIR}"
 +            ${SPHINX_CONFIG_OVERRIDES}
              "${SPHINX_INPUT_DIR}"
              "${HTML_OUTPUT_DIR}"
          WORKING_DIRECTORY
index ee390badc02d75946af0be1ea6b1008f569bdd35,3c82fdeee5dc824c3d409944ce0a64f9277d256e..db768ddfee3518c6f9652e702a577725995913f2
@@@ -8,43 -8,29 +8,51 @@@ releases of |Gromacs|. Major releases c
  functionality supported, whereas patch releases contain only fixes for
  issues identified in the corresponding major releases.
  
 -Two versions of |Gromacs| are under active maintenance, the 2020
 -series and the 2019 series. In the latter, only highly conservative
 +Two versions of |Gromacs| are under active maintenance, the 2021
 +series and the 2020 series. In the latter, only highly conservative
  fixes will be made, and only to address issues that affect scientific
  correctness. Naturally, some of those releases will be made after the
 -year 2019 ends, but we keep 2019 in the name so users understand how
 +year 2020 ends, but we keep 2019 in the name so users understand how
  up to date their version is. Such fixes will also be incorporated into
 -the 2020 release series, as appropriate. Around the time the 2021
 -release is made, the 2019 series will no longer be maintained.
 +the 2021 release series, as appropriate. Around the time the 2022
 +release is made, the 2020 series will no longer be maintained.
  
  Where issue numbers are reported in these release notes, more details
  can be found at https://redmine.gromacs.org at that issue number.
  
 +|Gromacs| 2021 series
 +---------------------
 +
 +.. todolist::
 +
 +Major release
 +^^^^^^^^^^^^^
 +
 +.. toctree::
 +   :maxdepth: 1
 +
 +   2021/major/highlights
 +   2021/major/features
 +   2021/major/performance
 +   2021/major/tools
 +   2021/major/bugs-fixed
 +   2021/major/deprecated-functionality
 +   2021/major/removed-functionality
 +   2021/major/portability
 +   2021/major/miscellaneous
 +
 +
  |Gromacs| 2020 series
  ---------------------
  
+ Patch releases
+ ^^^^^^^^^^^^^^
+ .. toctree::
+    :maxdepth: 1
+    2020/2020.1
  Major release
  ^^^^^^^^^^^^^
  
index d0fd44c7e9031ad1b9e8ef73cb7210c0a4cfe379,6e4da6382e34fe9af7deda832712bf24aae52be2..832554796881bc10ed30de5c69758fd3825b1793
@@@ -4,7 -4,7 +4,7 @@@
  .. Another useful one-liner to find undocumentedvariables:
  ..  ( export INPUT_FILE=docs/user-guide/environment-variables.rst; GIT_PAGER="cat ";   for ss in `for s in $(git grep getenv |  sed 's/.*getenv("\(.*\)".*/\1/' | sort -u  | grep '^[A-Z]'); do [ $(grep $s $INPUT_FILE -c) -eq 0 ] && echo $s; done `; do git grep $ss ; done )
  
 -.. TODO: still undocumented GMX_QM_GAUSSIAN_NCPUS
 +.. todo:: still undocumented GMX_QM_GAUSSIAN_NCPUS
  
  Environment Variables
  =====================
@@@ -230,6 -230,14 +230,14 @@@ Performance and Run Contro
  ``GMX_FORCE_UPDATE``
          update forces when invoking ``mdrun -rerun``.
  
+ ``GMX_FORCE_UPDATE_DEFAULT_GPU``
+         Force update to run on the GPU by default, overriding the ``mdrun -update auto`` option. Works similar to setting
+         ``mdrun -update gpu``, but (1) falls back to the CPU code-path, if set with input that is not supported and
+         (2) can be used to run update on GPUs in multi-rank cases. The latter case should be
+         considered experimental since it lacks substantial testing. Also, GPU update is only supported with the GPU direct
+         communications and ``GMX_FORCE_UPDATE_DEFAULT_GPU`` variable should be set simultaneously with ``GMX_GPU_DD_COMMS``
+         and ``GMX_GPU_PME_PP_COMMS`` environment variables in multi-rank case. Does not override ``mdrun -update cpu``.
  ``GMX_GPU_ID``
          set in the same way as ``mdrun -gpu_id``, ``GMX_GPU_ID``
          allows the user to specify different GPU IDs for different ranks, which can be useful for selecting different
index 788cdaa6da486929845760af4efa64cb151699d0,2f6e3e5deeb4166d622107702d24bb97a6621514..72984e185e26a12052d6c5b1c91ecce413084f63
@@@ -1,7 -1,7 +1,7 @@@
  #
  # This file is part of the GROMACS molecular simulation package.
  #
--# Copyright (c) 2013,2014,2015,2016,2018,2019, by the GROMACS development team, led by
++# Copyright (c) 2013,2014,2015,2016,2018,2019,2020, 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.
@@@ -39,6 -39,7 +39,7 @@@ set(test_source
      mrcdensitymap.cpp
      mrcdensitymapheader.cpp
      readinp.cpp
+     fileioxdrserializer.cpp
      )
  if (GMX_USE_TNG)
      list(APPEND test_sources tngio.cpp)
index 0000000000000000000000000000000000000000,1523ab20dbc8e8c38b3c921fc2393615c1e96f1d..fb4dcf5af078001504387bc8a15d56fc78a7ce72
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,150 +1,150 @@@
 - * Copyright (c) 2019, by the GROMACS development team, led by
+ /*
+  * This file is part of the GROMACS molecular simulation package.
+  *
++ * Copyright (c) 2019,2020, 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.
+  *
+  * GROMACS is free software; you can redistribute it and/or
+  * modify it under the terms of the GNU Lesser General Public License
+  * as published by the Free Software Foundation; either version 2.1
+  * of the License, or (at your option) any later version.
+  *
+  * GROMACS is distributed in the hope that it will be useful,
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  * Lesser General Public License for more details.
+  *
+  * You should have received a copy of the GNU Lesser General Public
+  * License along with GROMACS; if not, see
+  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+  *
+  * If you want to redistribute modifications to GROMACS, please
+  * consider that scientific software is very special. Version
+  * control is crucial - bugs must be traceable. We will be happy to
+  * consider code for inclusion in the official distribution, but
+  * derived work must not be called official GROMACS. Details are found
+  * in the README & COPYING files - if they are missing, get the
+  * official version at http://www.gromacs.org.
+  *
+  * To help us fund GROMACS development, we humbly ask that you cite
+  * the research papers on the package. Check out http://www.gromacs.org.
+  */
+ /*! \internal \file
+  * \brief
+  * Tests for gmx::FileIOXdrSerializer.
+  *
+  * \author Mark Abraham <mark.j.abraham@gmail.com>
+  * \ingroup module_fileio
+  */
+ #include "gmxpre.h"
+ #include <gtest/gtest.h>
+ #include "gromacs/fileio/gmxfio.h"
+ #include "gromacs/fileio/gmxfio_xdr.h"
+ #include "gromacs/utility/futil.h"
+ #include "testutils/testfilemanager.h"
+ namespace gmx
+ {
+ namespace test
+ {
+ namespace
+ {
+ union IntAndFloat32 {
+     std::int32_t int32Value_;
+     float        floatValue_;
+ };
+ union IntAndFloat64 {
+     std::int64_t int64Value_;
+     double       doubleValue_;
+ };
+ //! Constants used for testing endian swap operations
+ //! \{
+ constexpr std::int16_t c_int16Value = static_cast<std::int16_t>(0x7A2B);
+ constexpr std::int32_t c_int32Value = static_cast<std::int32_t>(0x78ABCDEF);
+ constexpr std::int64_t c_int64Value = static_cast<std::int64_t>(0x78ABCDEF12345678);
+ constexpr const IntAndFloat32 c_intAndFloat32{ c_int32Value };
+ constexpr const IntAndFloat64 c_intAndFloat64{ c_int64Value };
+ //! \}
+ //! Return the integer used for testing, depending on the size of int.
+ constexpr int integerSizeDependentTestingValue()
+ {
+     return sizeof(int) == 4 ? c_int32Value : sizeof(int) == 8 ? c_int64Value : c_int16Value;
+ }
+ class FileIOXdrSerializerTest : public ::testing::Test
+ {
+ public:
+     ~FileIOXdrSerializerTest() override
+     {
+         if (file_)
+         {
+             gmx_fio_close(file_);
+         }
+     }
+     struct SerializerValues
+     {
+         bool           boolValue_          = true;
+         unsigned char  unsignedCharValue_  = 0x78;
+         char           charValue_          = 0x78;
+         unsigned short unsignedShortValue_ = static_cast<unsigned short>(c_int16Value);
+         std::int32_t   int32Value_         = c_int32Value;
+         float          floatValue_         = c_intAndFloat32.floatValue_;
+         std::int64_t   int64Value_         = c_int64Value;
+         double         doubleValue_        = c_intAndFloat64.doubleValue_;
+         int            intValue_           = integerSizeDependentTestingValue();
+         real           realValue_          = std::is_same<real, double>::value
+                                   ? static_cast<real>(c_intAndFloat64.doubleValue_)
+                                   : static_cast<real>(c_intAndFloat32.floatValue_);
+     } defaultValues_;
+     TestFileManager fileManager_;
+     // Make sure the file extension is one that gmx_fio_open will
+     // recognize to open as binary, even though we're just abusing it
+     // to write arbitrary XDR output.
+     std::string filename_ = fileManager_.getTemporaryFilePath("data.edr");
+     t_fileio*   file_     = nullptr;
+ };
+ TEST_F(FileIOXdrSerializerTest, SizeIsCorrect)
+ {
+     file_ = gmx_fio_open(filename_.c_str(), "w");
+     FileIOXdrSerializer serializer(file_);
+     // These types all have well-defined widths in bytes AFTER XDR serialization,
+     // which we can test below.
+     serializer.doBool(&defaultValues_.boolValue_);     // 4 bytes
+     serializer.doChar(&defaultValues_.charValue_);     // 4 bytes
+     serializer.doInt32(&defaultValues_.int32Value_);   // 4 bytes
+     serializer.doInt64(&defaultValues_.int64Value_);   // 8 bytes
+     serializer.doFloat(&defaultValues_.floatValue_);   // 4 bytes
+     serializer.doDouble(&defaultValues_.doubleValue_); // 8 bytes
+     std::vector<char> charBuffer = { 'a', 'b', 'c' };
+     serializer.doCharArray(charBuffer.data(), charBuffer.size()); // 12 bytes
+     serializer.doOpaque(charBuffer.data(), charBuffer.size());    // 4 bytes
+     std::vector<int32_t> int32Buffer = { 0x1BCDEF78, 0x654321FE };
+     serializer.doInt32Array(int32Buffer.data(), int32Buffer.size()); // 8 bytes
+     std::vector<int64_t> int64Buffer = { 0x1BCDEF78654321FE, 0x3726ABFEAB34716C };
+     serializer.doInt64Array(int64Buffer.data(), int64Buffer.size()); // 16 bytes
+     gmx_fio_close(file_);
+     file_ = gmx_fio_open(filename_.c_str(), "r");
+     // Determine file size
+     gmx_fseek(gmx_fio_getfp(file_), 0, SEEK_END);
+     gmx_off_t fileSize = gmx_fio_ftell(file_);
+     EXPECT_EQ(fileSize, 72);
+ }
+ } // namespace
+ } // namespace test
+ } // namespace gmx
index 041cb951369c199c3d1edc0ab149e35e61be157b,d49b945f0842b9fd4c88f994c8a361d55150e169..40d6dde12c63f9b5ecb92289e3de5ea4237c6116
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
-- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
++ * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, 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.
@@@ -886,8 -886,14 +886,14 @@@ static char** read_topol(const char
                  "multiple-time-stepping scheme for a twin-range cut-off. When used with "
                  "a single-range cut-off (or a correct Trotter multiple-time-stepping scheme), "
                  "physical properties, such as the density, might differ from the intended values. "
-                 "Check if molecules in your system are affected by such issues before proceeding. "
-                 "Further information may be available at https://redmine.gromacs.org/issues/2884.");
+                 "Since there are researchers actively working on validating GROMOS with modern "
+                 "integrators we have not yet removed the GROMOS force fields, but you should be "
+                 "aware of these issues and check if molecules in your system are affected before "
+                 "proceeding. "
+                 "Further information is available at https://redmine.gromacs.org/issues/2884 , "
+                 "and a longer explanation of our decision to remove physically incorrect "
+                 "algorithms "
+                 "can be found at https://doi.org/10.26434/chemrxiv.11474583.v1 .");
      }
  
      cpp_done(handle);
@@@ -1376,7 -1382,7 +1382,7 @@@ void generate_qmexcl(gmx_mtop_t* sys, t
                      /* Copy the exclusions to a new array, since this is the only
                       * thing that needs to be modified for QMMM.
                       */
 -                    copy_blocka(&sys->moltype[molb->type].excls, &sys->moltype.back().excls);
 +                    sys->moltype.back().excls = sys->moltype[molb->type].excls;
                      /* Set the molecule type for the QMMM molblock */
                      molb->type = sys->moltype.size() - 1;
                  }
index 0d6fe533e511b8795c968333f7fe12ad421632ed,792542b87e81ad63c4bdaddae545c547ea606210..db99ec4d954df9312af6614a700bf05dfeafb8a4
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
-- * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, by the GROMACS development team, led by
++ * Copyright (c) 2013,2014,2015,2016,2017,2018,2019,2020, 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.
@@@ -896,7 -896,7 +896,7 @@@ real thole_pol(int             nbonds
  // #3205 for more information)
  #if defined(__GNUC__) && defined(__i386__) && defined(__OPTIMIZE__)
  #    pragma GCC push_options
- #    pragma GCC optimize("O2")
+ #    pragma GCC optimize("O1")
  #    define avoid_gcc_i386_o3_code_generation_bug
  #endif
  
index 1a48c3bc955bc92b089c4d1ba4cfccd513948897,fe7839086674790d60de1bd102c4b3cdac804bfa..08b17e3083ddde0275c9bc41374c216154eeba49
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
-- * Copyright (c) 2019, by the GROMACS development team, led by
++ * Copyright (c) 2019,2020, 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.
@@@ -40,6 -40,8 +40,6 @@@
   * using CUDA, including class initialization, data-structures management
   * and GPU kernel.
   *
 - * \note Management of periodic boundary should be unified with SETTLE and
 - *       removed from here.
   * \todo Reconsider naming, i.e. "cuda" suffics should be changed to "gpu".
   *
   * \author Artem Zhmurov <zhmurov@gmail.com>
@@@ -433,8 -435,7 +433,8 @@@ void LincsCuda::apply(const float3* d_x
                        float3*       d_v,
                        const real    invdt,
                        const bool    computeVirial,
 -                      tensor        virialScaled)
 +                      tensor        virialScaled,
 +                      const PbcAiuc pbcAiuc)
  {
      ensureNoPendingCudaError("In CUDA version of LINCS");
  
      }
      config.stream = commandStream_;
  
 +    kernelParams_.pbcAiuc = pbcAiuc;
 +
      const auto kernelArgs =
              prepareGpuKernelArguments(kernelPtr, config, &kernelParams_, &d_x, &d_xp, &d_v, &invdt);
  
@@@ -548,18 -547,35 +548,35 @@@ LincsCuda::~LincsCuda(
      }
  }
  
- /*! \brief Constructs and returns an atom constraint adjacency list
-  *
-  * Each constraint will be represented as a tuple, containing index of the second
-  * constrained atom, index of the constraint and a sign that indicates the order of atoms in
-  * which they are listed. Sign is needed to compute the mass factors.
-  */
- static std::vector<std::vector<std::tuple<int, int, int>>>
+ //! Helper type for discovering coupled constraints
+ struct AtomsAdjacencyListElement
+ {
+     AtomsAdjacencyListElement(const int indexOfSecondConstrainedAtom,
+                               const int indexOfConstraint,
+                               const int signFactor) :
+         indexOfSecondConstrainedAtom_(indexOfSecondConstrainedAtom),
+         indexOfConstraint_(indexOfConstraint),
+         signFactor_(signFactor)
+     {
+     }
+     //! The index of the other atom constrained to this atom.
+     int indexOfSecondConstrainedAtom_;
+     //! The index of this constraint in the container of constraints.
+     int indexOfConstraint_;
+     /*! \brief A multiplicative factor that indicates the relative
+      * order of the atoms in the atom list.
+      *
+      * Used for computing the mass factor of this constraint
+      * relative to any coupled constraints. */
+     int signFactor_;
+ };
+ //! Constructs and returns an atom constraint adjacency list
+ static std::vector<std::vector<AtomsAdjacencyListElement>>
  constructAtomsAdjacencyList(const int numAtoms, ArrayRef<const int> iatoms)
  {
      const int                                           stride         = 1 + NRAL(F_CONSTR);
      const int                                           numConstraints = iatoms.ssize() / stride;
-     std::vector<std::vector<std::tuple<int, int, int>>> atomsAdjacencyList(numAtoms);
+     std::vector<std::vector<AtomsAdjacencyListElement>> atomsAdjacencyList(numAtoms);
      for (int c = 0; c < numConstraints; c++)
      {
          int a1 = iatoms[stride * c + 1];
          // Each constraint will be represented as a tuple, containing index of the second
          // constrained atom, index of the constraint and a sign that indicates the order of atoms in
          // which they are listed. Sign is needed to compute the mass factors.
-         atomsAdjacencyList[a1].push_back(std::make_tuple(a2, c, +1));
-         atomsAdjacencyList[a2].push_back(std::make_tuple(a1, c, -1));
+         atomsAdjacencyList[a1].emplace_back(a2, c, +1);
+         atomsAdjacencyList[a2].emplace_back(a1, c, -1);
      }
  
      return atomsAdjacencyList;
   */
  inline int countCoupled(int           a,
                          ArrayRef<int> numCoupledConstraints,
-                         ArrayRef<const std::vector<std::tuple<int, int, int>>> atomsAdjacencyList)
+                         ArrayRef<const std::vector<AtomsAdjacencyListElement>> atomsAdjacencyList)
  
  {
      int counted = 0;
      for (const auto& adjacentAtom : atomsAdjacencyList[a])
      {
-         const int c2 = std::get<1>(adjacentAtom);
+         const int c2 = adjacentAtom.indexOfConstraint_;
          if (numCoupledConstraints[c2] == -1)
          {
              numCoupledConstraints[c2] = 0; // To indicate we've been here
-             counted += 1 + countCoupled(std::get<0>(adjacentAtom), numCoupledConstraints, atomsAdjacencyList);
+             counted += 1
+                        + countCoupled(adjacentAtom.indexOfSecondConstrainedAtom_,
+                                       numCoupledConstraints, atomsAdjacencyList);
          }
      }
      return counted;
   */
  inline void addWithCoupled(ArrayRef<const int>                                    iatoms,
                             const int                                              stride,
-                            ArrayRef<const std::vector<std::tuple<int, int, int>>> atomsAdjacencyList,
+                            ArrayRef<const std::vector<AtomsAdjacencyListElement>> atomsAdjacencyList,
                             ArrayRef<int>                                          splitMap,
                             const int                                              c,
                             int*                                                   currentMapIndex)
              const int a1 = iatoms[stride * c + 1 + atomIndexInConstraint];
              for (const auto& adjacentAtom : atomsAdjacencyList[a1])
              {
-                 const int c2 = std::get<1>(adjacentAtom);
+                 const int c2 = adjacentAtom.indexOfConstraint_;
                  if (c2 != c)
                  {
                      addWithCoupled(iatoms, stride, atomsAdjacencyList, splitMap, c2, currentMapIndex);
   * numCoupledConstraints vector is also used to keep track if the constrain was already counted.
   */
  static std::vector<int> countNumCoupledConstraints(ArrayRef<const int> iatoms,
-                                                    ArrayRef<const std::vector<std::tuple<int, int, int>>> atomsAdjacencyList)
+                                                    ArrayRef<const std::vector<AtomsAdjacencyListElement>> atomsAdjacencyList)
  {
      const int        stride         = 1 + NRAL(F_CONSTR);
      const int        numConstraints = iatoms.ssize() / stride;
@@@ -821,21 -839,17 +840,17 @@@ void LincsCuda::set(const t_idef& idef
          coupledConstraintsCountsHost.at(splitMap.at(c1)) = 0;
          int c1a1                                         = iatoms[stride * c1 + 1];
          int c1a2                                         = iatoms[stride * c1 + 2];
-         int c2;
-         int c2a1;
-         int c2a2;
-         int sign;
  
-         // Constraints, coupled trough the first atom.
-         c2a1 = c1a1;
-         for (unsigned j = 0; j < atomsAdjacencyList.at(c1a1).size(); j++)
+         // Constraints, coupled through the first atom.
+         int c2a1 = c1a1;
+         for (const auto& atomAdjacencyList : atomsAdjacencyList[c1a1])
          {
-             std::tie(c2a2, c2, sign) = atomsAdjacencyList.at(c1a1).at(j);
+             int c2 = atomAdjacencyList.indexOfConstraint_;
  
              if (c1 != c2)
              {
+                 int c2a2  = atomAdjacencyList.indexOfSecondConstrainedAtom_;
+                 int sign  = atomAdjacencyList.signFactor_;
                  int index = kernelParams_.numConstraintsThreads
                                      * coupledConstraintsCountsHost.at(splitMap.at(c1))
                              + splitMap.at(c1);
  
          // Constraints, coupled through the second atom.
          c2a1 = c1a2;
-         for (unsigned j = 0; j < atomsAdjacencyList.at(c1a2).size(); j++)
+         for (const auto& atomAdjacencyList : atomsAdjacencyList[c1a2])
          {
-             std::tie(c2a2, c2, sign) = atomsAdjacencyList.at(c1a2).at(j);
+             int c2 = atomAdjacencyList.indexOfConstraint_;
  
              if (c1 != c2)
              {
+                 int c2a2  = atomAdjacencyList.indexOfSecondConstrainedAtom_;
+                 int sign  = atomAdjacencyList.signFactor_;
                  int index = kernelParams_.numConstraintsThreads
                                      * coupledConstraintsCountsHost.at(splitMap.at(c1))
                              + splitMap.at(c1);
                         GpuApiCallBehavior::Sync, nullptr);
  }
  
 -void LincsCuda::setPbc(const t_pbc* pbc)
 -{
 -    setPbcAiuc(pbc->ndim_ePBC, pbc->box, &kernelParams_.pbcAiuc);
 -}
 -
  } // namespace gmx
index 938e774d5d85a65cd365b78d3ffbc2d2eb7d925f,91761324e020f4d30aa973bb9e72c52d9f1f1d92..2f0018f4d12e188329570591505d4b2af24df3e1
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
 - * Copyright (c) 2013-2019, by the GROMACS development team, led by
 + * Copyright (c) 2013-2019,2020, 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.
@@@ -843,7 -843,7 +843,7 @@@ static StepWorkload setupStepWorkload(c
  
  /* \brief Launch end-of-step GPU tasks: buffer clearing and rolling pruning.
   *
 - * TODO: eliminate the \p useGpuNonbonded and \p useGpuNonbonded when these are
 + * TODO: eliminate \p useGpuPmeOnThisRank when this is
   * incorporated in DomainLifetimeWorkload.
   */
  static void launchGpuEndOfStepTasks(nonbonded_verlet_t*               nbv,
                                      gmx_pme_t*                        pmedata,
                                      gmx_enerdata_t*                   enerd,
                                      const gmx::MdrunScheduleWorkload& runScheduleWork,
 -                                    bool                              useGpuNonbonded,
 -                                    bool                              useGpuPme,
 +                                    bool                              useGpuPmeOnThisRank,
                                      int64_t                           step,
                                      gmx_wallcycle_t                   wcycle)
  {
 -    if (useGpuNonbonded)
 +    if (runScheduleWork.simulationWork.useGpuNonbonded)
      {
          /* Launch pruning before buffer clearing because the API overhead of the
           * clear kernel launches can leave the GPU idle while it could be running
          wallcycle_stop(wcycle, ewcLAUNCH_GPU);
      }
  
 -    if (useGpuPme)
 +    if (useGpuPmeOnThisRank)
      {
          pme_gpu_reinit_computation(pmedata, wcycle);
      }
@@@ -1166,7 -1167,10 +1166,7 @@@ void do_force(FILE
              }
              wallcycle_stop(wcycle, ewcLAUNCH_GPU);
          }
 -    }
  
 -    if (stepWork.doNeighborSearch)
 -    {
          // Need to run after the GPU-offload bonded interaction lists
          // are set up to be able to determine whether there is bonded work.
          runScheduleWork->domainWork = setupDomainLifetimeWorkload(
          wallcycle_start_nocount(wcycle, ewcNS);
          wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_LOCAL);
          /* Note that with a GPU the launch overhead of the list transfer is not timed separately */
 -        nbv->constructPairlist(InteractionLocality::Local, &top->excls, step, nrnb);
 +        nbv->constructPairlist(InteractionLocality::Local, top->excls, step, nrnb);
  
          nbv->setupGpuShortRangeWork(fr->gpuBonded, InteractionLocality::Local);
  
              wallcycle_start_nocount(wcycle, ewcNS);
              wallcycle_sub_start(wcycle, ewcsNBS_SEARCH_NONLOCAL);
              /* Note that with a GPU the launch overhead of the list transfer is not timed separately */
 -            nbv->constructPairlist(InteractionLocality::NonLocal, &top->excls, step, nrnb);
 +            nbv->constructPairlist(InteractionLocality::NonLocal, top->excls, step, nrnb);
  
              nbv->setupGpuShortRangeWork(fr->gpuBonded, InteractionLocality::NonLocal);
              wallcycle_sub_stop(wcycle, ewcsNBS_SEARCH_NONLOCAL);
                  // Note: GPU update + DD without direct communication is not supported,
                  // a waitCoordinatesReadyOnHost() should be issued if it will be.
                  GMX_ASSERT(!simulationWork.useGpuUpdate,
-                            "GPU update is not supported with halo exchange");
+                            "GPU update is not supported with CPU halo exchange");
                  dd_move_x(cr->dd, box, x.unpaddedArrayRef(), wcycle);
              }
  
      }
  
      launchGpuEndOfStepTasks(nbv, fr->gpuBonded, fr->pmedata, enerd, *runScheduleWork,
 -                            simulationWork.useGpuNonbonded, useGpuPmeOnThisRank, step, wcycle);
 +                            useGpuPmeOnThisRank, step, wcycle);
  
      if (DOMAINDECOMP(cr))
      {
index 95a95f5c24decd92f1de1f8df98417ca5ec6c3a6,58c12d092d594f20d51e96aed45b106255aefd19..6f287b8d93c1dd1405c4893867ff3c9528c37fa7
@@@ -3,7 -3,7 +3,7 @@@
   *
   * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
   * Copyright (c) 2001-2004, The GROMACS development team.
 - * Copyright (c) 2011-2019, by the GROMACS development team, led by
 + * Copyright (c) 2011-2019,2020, 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.
@@@ -1191,9 -1191,10 +1191,10 @@@ int Mdrunner::mdrunner(
          const bool useUpdateGroups = cr->dd ? ddUsesUpdateGroups(*cr->dd) : false;
  
          useGpuForUpdate = decideWhetherToUseGpuForUpdate(
-                 devFlags.forceGpuUpdateDefault, useDomainDecomposition, useUpdateGroups, useGpuForPme,
-                 useGpuForNonbonded, updateTarget, gpusWereDetected, *inputrec, mtop, doEssentialDynamics,
-                 gmx_mtop_ftype_count(mtop, F_ORIRES) > 0, replExParams.exchangeInterval > 0, doRerun);
+                 devFlags.forceGpuUpdateDefault, useDomainDecomposition, useUpdateGroups, pmeRunMode,
+                 domdecOptions.numPmeRanks > 0, useGpuForNonbonded, updateTarget, gpusWereDetected,
+                 *inputrec, mtop, doEssentialDynamics, gmx_mtop_ftype_count(mtop, F_ORIRES) > 0,
+                 replExParams.exchangeInterval > 0, doRerun, mdlog);
      }
      GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
  
              /* This call is not included in init_domain_decomposition mainly
               * because fr->cginfo_mb is set later.
               */
 -            dd_init_bondeds(fplog, cr->dd, &mtop, vsite.get(), inputrec,
 +            dd_init_bondeds(fplog, cr->dd, mtop, vsite.get(), inputrec,
                              domdecOptions.checkBondedInteractions, fr->cginfo_mb);
          }
  
      }
  
      // FIXME: this is only here to manually unpin mdAtoms->chargeA_ and state->x,
 -    // before we destroy the GPU context(s) in free_gpu_resources().
 +    // before we destroy the GPU context(s) in free_gpu().
      // Pinned buffers are associated with contexts in CUDA.
      // As soon as we destroy GPU contexts after mdrunner() exits, these lines should go.
      mdAtoms.reset(nullptr);
      globalState.reset(nullptr);
      mdModules_.reset(nullptr); // destruct force providers here as they might also use the GPU
 +    /* Free pinned buffers in *fr */
 +    delete fr;
 +    fr = nullptr;
 +
 +    if (hwinfo->gpu_info.n_dev > 0)
 +    {
 +        /* stop the GPU profiler (only CUDA) */
 +        stopGpuProfiler();
 +    }
 +
 +    /* With tMPI we need to wait for all ranks to finish deallocation before
 +     * destroying the CUDA context in free_gpu() as some tMPI ranks may be sharing
 +     * GPU and context.
 +     *
 +     * This is not a concern in OpenCL where we use one context per rank which
 +     * is freed in nbnxn_gpu_free().
 +     *
 +     * Note: it is safe to not call the barrier on the ranks which do not use GPU,
 +     * but it is easier and more futureproof to call it on the whole node.
 +     *
 +     * Note that this function needs to be called even if GPUs are not used
 +     * in this run because the PME ranks have no knowledge of whether GPUs
 +     * are used or not, but all ranks need to enter the barrier below.
 +     * \todo Remove this physical node barrier after making sure
 +     * that it's not needed anymore (with a shared GPU run).
 +     */
 +    if (GMX_THREAD_MPI)
 +    {
 +        physicalNodeComm.barrier();
 +    }
  
 -    /* Free GPU memory and set a physical node tMPI barrier (which should eventually go away) */
 -    free_gpu_resources(fr, physicalNodeComm, hwinfo->gpu_info);
      free_gpu(nonbondedDeviceInfo);
      free_gpu(pmeDeviceInfo);
 -    done_forcerec(fr, mtop.molblock.size());
      sfree(fcd);
  
      if (doMembed)
index bfd43291b253cb55c63ad9c10c65d26064b208ff,3974e7b013fcf103cdbf74c4cad9a6decbc6e874..c184f89a7698117d27fd597f90c11179fff99b75
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
 - * Copyright (c) 2015,2016,2017,2018,2019, by the GROMACS development team, led by
 + * Copyright (c) 2015,2016,2017,2018,2019,2020, 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.
@@@ -489,19 -489,21 +489,21 @@@ bool decideWhetherToUseGpusForBonded(co
      return gpusWereDetected && usingOurCpuForPmeOrEwald;
  }
  
- bool decideWhetherToUseGpuForUpdate(const bool        forceGpuUpdateDefault,
-                                     const bool        isDomainDecomposition,
-                                     const bool        useUpdateGroups,
-                                     const bool        useGpuForPme,
-                                     const bool        useGpuForNonbonded,
-                                     const TaskTarget  updateTarget,
-                                     const bool        gpusWereDetected,
-                                     const t_inputrec& inputrec,
-                                     const gmx_mtop_t& mtop,
-                                     const bool        useEssentialDynamics,
-                                     const bool        doOrientationRestraints,
-                                     const bool        useReplicaExchange,
-                                     const bool        doRerun)
+ bool decideWhetherToUseGpuForUpdate(const bool           forceGpuUpdateDefault,
+                                     const bool           isDomainDecomposition,
+                                     const bool           useUpdateGroups,
+                                     const PmeRunMode     pmeRunMode,
+                                     const bool           havePmeOnlyRank,
+                                     const bool           useGpuForNonbonded,
+                                     const TaskTarget     updateTarget,
+                                     const bool           gpusWereDetected,
+                                     const t_inputrec&    inputrec,
+                                     const gmx_mtop_t&    mtop,
+                                     const bool           useEssentialDynamics,
+                                     const bool           doOrientationRestraints,
+                                     const bool           useReplicaExchange,
+                                     const bool           doRerun,
+                                     const gmx::MDLogger& mdlog)
  {
  
      // '-update cpu' overrides the environment variable, '-update auto' does not
      // Using the GPU-version of update if:
      // 1. PME is on the GPU (there should be a copy of coordinates on GPU for PME spread), or
      // 2. Non-bonded interactions are on the GPU.
-     if (!(useGpuForPme || useGpuForNonbonded))
+     if (pmeRunMode == PmeRunMode::CPU && !useGpuForNonbonded)
      {
          errorMessage +=
                  "Either PME or short-ranged non-bonded interaction tasks must run on the GPU.\n";
      }
+     // Since only direct GPU communications are supported with GPU update, PME should be fully offloaded in DD and PME only cases.
+     if (pmeRunMode != PmeRunMode::GPU && (isDomainDecomposition || havePmeOnlyRank))
+     {
+         errorMessage += "PME should run on GPU.\n";
+     }
      if (!gpusWereDetected)
      {
          errorMessage += "Compatible GPUs must have been found.\n";
  
      if (!errorMessage.empty())
      {
-         if (updateTarget == TaskTarget::Gpu)
+         if (updateTarget != TaskTarget::Gpu && forceGpuUpdateDefault)
+         {
+             GMX_LOG(mdlog.warning)
+                     .asParagraph()
+                     .appendText(
+                             "Update task on the GPU was required, by the "
+                             "GMX_FORCE_UPDATE_DEFAULT_GPU environment variable, but the following "
+                             "condition(s) were not satisfied:");
+             GMX_LOG(mdlog.warning).asParagraph().appendText(errorMessage.c_str());
+             GMX_LOG(mdlog.warning).asParagraph().appendText("Will use CPU version of update.");
+         }
+         else if (updateTarget == TaskTarget::Gpu)
          {
              std::string prefix = gmx::formatString(
                      "Update task on the GPU was required,\n"
index 573cac02cfcbf742df5015b9a403c6913643f275,6d22cf843ae1efb509f97d4905945e3ecba6ed49..4d924438fbe3d01662799dce118a485e6729f410
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
 - * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
 + * Copyright (c) 2017,2018,2019,2020, 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.
  struct gmx_hw_info_t;
  struct gmx_mtop_t;
  struct t_inputrec;
+ enum class PmeRunMode;
  
  namespace gmx
  {
  
+ class MDLogger;
  //! Record where a compute task is targetted.
  enum class TaskTarget : int
  {
@@@ -235,7 -238,8 +238,8 @@@ bool decideWhetherToUseGpusForBonded(bo
   * \param[in]  forceGpuUpdateDefault        If update should run on GPU by default.
   * \param[in]  isDomainDecomposition        Whether there more than one domain.
   * \param[in]  useUpdateGroups              If the constraints can be split across domains.
-  * \param[in]  useGpuForPme                 Whether GPUs will be used for PME interactions.
+  * \param[in]  pmeRunMode                   PME running mode: CPU, GPU or mixed.
+  * \param[in]  havePmeOnlyRank              If there is a PME-only rank in the simulation.
   * \param[in]  useGpuForNonbonded           Whether GPUs will be used for nonbonded interactions.
   * \param[in]  updateTarget                 User choice for running simulation on GPU.
   * \param[in]  gpusWereDetected             Whether compatible GPUs were detected on any node.
   * \param[in]  doOrientationRestraints      If orientation restraints are enabled.
   * \param[in]  useReplicaExchange           If this is a REMD simulation.
   * \param[in]  doRerun                      It this is a rerun.
+  * \param[in]  mdlog                        MD logger.
   *
   * \returns    Whether complete simulation can be run on GPU.
   * \throws     std::bad_alloc            If out of memory
   *             InconsistentInputError    If the user requirements are inconsistent.
   */
- bool decideWhetherToUseGpuForUpdate(bool              forceGpuUpdateDefault,
-                                     bool              isDomainDecomposition,
-                                     bool              useUpdateGroups,
-                                     bool              useGpuForPme,
-                                     bool              useGpuForNonbonded,
-                                     TaskTarget        updateTarget,
-                                     bool              gpusWereDetected,
-                                     const t_inputrec& inputrec,
-                                     const gmx_mtop_t& mtop,
-                                     bool              useEssentialDynamics,
-                                     bool              doOrientationRestraints,
-                                     bool              useReplicaExchange,
-                                     bool              doRerun);
+ bool decideWhetherToUseGpuForUpdate(bool                 forceGpuUpdateDefault,
+                                     bool                 isDomainDecomposition,
+                                     bool                 useUpdateGroups,
+                                     PmeRunMode           pmeRunMode,
+                                     bool                 havePmeOnlyRank,
+                                     bool                 useGpuForNonbonded,
+                                     TaskTarget           updateTarget,
+                                     bool                 gpusWereDetected,
+                                     const t_inputrec&    inputrec,
+                                     const gmx_mtop_t&    mtop,
+                                     bool                 useEssentialDynamics,
+                                     bool                 doOrientationRestraints,
+                                     bool                 useReplicaExchange,
+                                     bool                 doRerun,
+                                     const gmx::MDLogger& mdlog);
  
  
  } // namespace gmx
index e24befae3f47af7ca253e5f1dcfa722bf8167a88,2dec0caec9ae0345c3609152cf09e265d63be4d0..832077e9da5c354dc7f182c8d5825e16b7b96ee3
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
 - * Copyright (c) 2016,2017,2018,2019, by the GROMACS development team, led by
 + * Copyright (c) 2016,2017,2018,2019,2020, 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.
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
+ /*! \internal \file
+  * \brief
+  * Defines gmx::ISerializer implementation for in-memory serialization.
+  *
+  * \author Teemu Murtola <teemu.murtola@gmail.com>
+  * \ingroup module_utility
+  */
  #include "gmxpre.h"
  
  #include "inmemoryserializer.h"
@@@ -90,11 -97,11 +97,11 @@@ T swapEndian(const T& value
      return endianessSwappedValue.value_;
  }
  
- //! \brief Change the host-dependent endian settings to either Swap or DoNotSwap.
- //
// \param endianSwapBehavior input swap behavior, might depend on host.
- //
- // \return Host-independent setting, either Swap or DoNotSwap.
+ /*! \brief Change the host-dependent endian settings to either Swap or DoNotSwap.
+  *
 * \param endianSwapBehavior input swap behavior, might depend on host.
+  *
+  * \return Host-independent setting, either Swap or DoNotSwap. */
  EndianSwapBehavior setEndianSwapBehaviorFromHost(EndianSwapBehavior endianSwapBehavior)
  {
      if (endianSwapBehavior == EndianSwapBehavior::SwapIfHostIsBigEndian)
index d11e6c91697dafe448431750ea50ecd196437a7f,f17a83894f3c1ca0b8b8feda1bed6a21d2c678bc..06b3d0e23683b8d4fd6fec95df76cb98d89051a6
@@@ -1,7 -1,7 +1,7 @@@
  /*
   * This file is part of the GROMACS molecular simulation package.
   *
 - * Copyright (c) 2019, by the GROMACS development team, led by
 + * Copyright (c) 2019,2020, 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.
@@@ -241,6 -241,28 +241,28 @@@ TEST_F(InMemorySerializerTest, Deserial
      checkSerializerValuesforEquality(endianessSwappedValues_, deserialisedValues);
  }
  
+ TEST_F(InMemorySerializerTest, SizeIsCorrect)
+ {
+     InMemorySerializer serializer;
+     // These types all have well-defined widths in bytes,
+     // which we can test below.
+     serializer.doBool(&defaultValues_.boolValue_);     // 1 bytes
+     serializer.doChar(&defaultValues_.charValue_);     // 1 bytes
+     serializer.doInt32(&defaultValues_.int32Value_);   // 4 bytes
+     serializer.doInt64(&defaultValues_.int64Value_);   // 8 bytes
+     serializer.doFloat(&defaultValues_.floatValue_);   // 4 bytes
+     serializer.doDouble(&defaultValues_.doubleValue_); // 8 bytes
+     std::vector<char> charBuffer = { 'a', 'b', 'c' };
+     serializer.doCharArray(charBuffer.data(), charBuffer.size()); // 3 bytes
+     serializer.doOpaque(charBuffer.data(), charBuffer.size());    // 3 bytes
+     std::vector<int32_t> int32Buffer = { 0x1BCDEF78, 0x654321FE };
+     serializer.doInt32Array(int32Buffer.data(), int32Buffer.size()); // 8 bytes
+     std::vector<int64_t> int64Buffer = { 0x1BCDEF78654321FE, 0x3726ABFEAB34716C };
+     serializer.doInt64Array(int64Buffer.data(), int64Buffer.size()); // 16 bytes
+     auto buffer = serializer.finishAndGetBuffer();
+     EXPECT_EQ(buffer.size(), 56);
+ }
  } // namespace
  } // namespace test
  } // namespace gmx