Merge release-5-0 into master
authorRoland Schulz <roland@utk.edu>
Sun, 24 Aug 2014 17:36:51 +0000 (13:36 -0400)
committerRoland Schulz <roland@utk.edu>
Mon, 25 Aug 2014 03:48:57 +0000 (23:48 -0400)
Conflicts:
install-guide/configure-install-guide.cmake.in
applied to docs/configure-markdown.cmake.in
Moved doxygen/cycle-suppressions.txt -> docs/doxygen/cycle-suppressions.txt
docs/CMakeLists.txt: replaced PROJECT_VERSION with GMX_VERSION_STRING

Change-Id: I61724a10a5331fdf747edd411dfa2f2256cd3f6b

26 files changed:
1  2 
.gitattributes
CMakeLists.txt
docs/CMakeLists.txt
docs/configure-markdown.cmake.in
docs/doxygen/CMakeLists.txt
docs/doxygen/Doxyfile-common.cmakein
docs/doxygen/Doxyfile-version.cmakein
docs/doxygen/codelayout.md
docs/doxygen/cycle-suppressions.txt
docs/doxygen/directories.cpp
docs/doxygen/doxygen-check.py
docs/doxygen/doxygen.md
docs/doxygen/doxygenxml.py
docs/doxygen/gmxtree.py
docs/doxygen/graphbuilder.py
docs/doxygen/reporter.py
docs/doxygen/suppressions.txt
docs/doxygen/usinglibrary.md
docs/install-guide/install-guide.md
docs/man/CMakeLists.txt
docs/manual/README
docs/manual/macros.tex
docs/manual/monster.bib
docs/old-html/CMakeLists.txt
src/gromacs/CMakeLists.txt
src/gromacs/mdlib/constr.c

diff --combined .gitattributes
index 6d93759eddd5fac511fa012289049c421852a6a6,46d10516dfcbe26f3a5ca7072c04d4dd65881f82..7712db33da0db021cfdd65add55646481cea24be
@@@ -25,14 -25,15 +25,14 @@@ cmake/ThreadMPI.cmak
  cmake/Platform/BluegeneQ*.cmake         !filter
  cmake/*.c                               !filter
  cmake/*.c.cmakein                       !filter
- doxygen/Doxyfile-*.cmakein              !filter
- doxygen/*.cpp                           !filter
- doxygen/examples/*.cpp                  filter=uncrustify_only
- doxygen/examples/*.c                    filter=uncrustify_only
manual/UseLATEX.cmake                   !filter
+ docs/doxygen/Doxyfile-*.cmakein         !filter
+ docs/doxygen/*.cpp                      !filter
+ docs/doxygen/examples/*.cpp             filter=uncrustify_only
+ docs/doxygen/examples/*.c               filter=uncrustify_only
docs/manual/UseLATEX.cmake              !filter
  scripts/GMXRC.*                         !filter
  scripts/make_gromos_rtp.py              !filter
  src/contrib/*                           !filter
 -src/gromacs/gmxlib/gpu_utils/memtestG80_core.*             !filter
  src/gromacs/gmxlib/nonbonded/preprocessor/gmxpreprocess.py !filter
  src/gromacs/linearalgebra/gmx_blas/*    !filter
  src/gromacs/linearalgebra/gmx_lapack/*  !filter
diff --combined CMakeLists.txt
index 9df009f6f30253ee9193417f5335343245aa300a,ed24af96150276cb1bf39fd1106cd3212660f105..ce27e95cc1aa3c395bf6e2a00234fc5ff225a8f1
@@@ -48,28 -48,75 +48,28 @@@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CM
  set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
  set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
  
 -# PROJECT_VERSION should have the following structure:
 -# VERSION-dev[-SUFFIX] where the VERSION should have the for: vMajor.vMinor.vPatch
 -#
 -# The "-dev" suffix is important to keep because it makes possible to distinguish
 -# between a build from official release and a build from git release branch on a
 -# machine with no git.
 -#
 -# NOTE: when releasing the "-dev" suffix needs to be stripped off!
 -# REGRESSIONTEST_VERSION and REGRESSIONTEST_BRANCH should always be
 -# defined.
 -set(PROJECT_VERSION "5.0.1-dev")
 -# If this is a released tarball, "-dev" will not be present in
 -# PROJECT_VERSION, and REGRESSIONTEST_VERSION specifies the version
 -# number of the regressiontest tarball against which the code tarball
 -# can be tested. This will be the version of the last patch release.
 -set(REGRESSIONTEST_VERSION "5.0.1-dev")
 -# The MD5 checksum of the regressiontest tarball. Only used if "-dev"
 -# is not present in the PROJECT_VERSION
 -set(REGRESSIONTEST_MD5SUM "a07524afebca5013540d4f2f72df2dce")
 -# If this is not a released tarball, "-dev" will be present in
 -# PROJECT_VERSION, and REGRESSIONTEST_BRANCH specifies the name of the
 -# gerrit.gromacs.org branch whose HEAD can test this code, *if* this
 -# code contains all recent fixes from the corresponding code branch.
 -set(REGRESSIONTEST_BRANCH "refs/heads/release-5-0")
 -
 -set(CUSTOM_VERSION_STRING ""
 -    CACHE STRING "Custom version string (if empty, use hard-coded default)")
 -mark_as_advanced(CUSTOM_VERSION_STRING)
 -if (CUSTOM_VERSION_STRING)
 -    set(PROJECT_VERSION ${CUSTOM_VERSION_STRING})
 -endif()
 -set(LIBRARY_SOVERSION 0)
 -set(LIBRARY_VERSION ${LIBRARY_SOVERSION}.0.0)
 -# It is a bit irritating, but this has to be set separately for now!
 -SET(CPACK_PACKAGE_VERSION_MAJOR "5")
 -SET(CPACK_PACKAGE_VERSION_MINOR "0")
 -#SET(CPACK_PACKAGE_VERSION_PATCH "0")
 -
 -# The numerical gromacs version. It is 40600 for 4.6.0.
 -# The #define GMX_VERSION in gromacs/version.h is set to this value.
 -math(EXPR NUM_VERSION
 -    "${CPACK_PACKAGE_VERSION_MAJOR}*10000 + ${CPACK_PACKAGE_VERSION_MINOR}*100")
 -if(CPACK_PACKAGE_VERSION_PATCH)
 -    math(EXPR NUM_VERSION
 -         "${NUM_VERSION} + ${CPACK_PACKAGE_VERSION_PATCH}")
 -endif()
 -
 -# The API version tracks the numerical Gromacs version (for now).
 -# It is potentially different from the Gromacs version in the future, if
 -# the programs/libraries diverge from the presumably more stable API.
 -# The #define GMX_API_VERSION in version.h is set to this value to
 -# provide backward compatibility of software written against the Gromacs API.
 -set(API_VERSION ${NUM_VERSION})
 +# Set up common version variables, as well as general information about
 +# the build tree (whether the build is from a source package or from a git
 +# repository).  Also declares a few functions that will be used for generating
 +# version info files later.
 +include(gmxVersionInfo)
  
  if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT AND UNIX)
      set(CMAKE_INSTALL_PREFIX "/usr/local/gromacs" CACHE STRING "Installation prefix (installation will need write permissions here)" FORCE)
  endif()
  
  include(gmxBuildTypeReference)
 +include(gmxBuildTypeProfile)
  include(gmxBuildTypeTSAN)
  include(gmxBuildTypeASAN)
  include(gmxBuildTypeReleaseWithAssert)
  
  if(NOT CMAKE_BUILD_TYPE)
 -    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert." FORCE)
 +    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel Reference RelWithAssert Profile." FORCE)
      # There's no need to offer a user the choice of ThreadSanitizer
      # Set the possible values of build type for cmake-gui
      set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
 -        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert")
 +        "MinSizeRel" "RelWithDebInfo" "Reference" "RelWithAssert" "Profile")
  endif()
  if(CMAKE_CONFIGURATION_TYPES)
      # Add appropriate GROMACS-specific build types for the Visual
          "List of configuration types"
          FORCE)
  endif()
 -set(build_types_with_explicit_flags RELEASE DEBUG RELWITHDEBUGINFO RELWITHASSERT MINSIZEREL)
 -
 -enable_language(C)
 -enable_language(CXX)
 +set(build_types_with_explicit_flags RELEASE DEBUG RELWITHDEBUGINFO RELWITHASSERT MINSIZEREL PROFILE)
  
  set(CPACK_PACKAGE_NAME "gromacs")
 -set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
 +set(CPACK_PACKAGE_VERSION_MAJOR ${GMX_VERSION_MAJOR})
 +set(CPACK_PACKAGE_VERSION_MINOR ${GMX_VERSION_MINOR})
 +set(CPACK_PACKAGE_VERSION_PATCH ${GMX_VERSION_PATCH})
 +set(CPACK_PACKAGE_VERSION       ${GMX_VERSION_STRING})
  set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
  set(CPACK_PACKAGE_VENDOR "gromacs.org")
  set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Gromacs - a toolkit for high-performance molecular simulation")
@@@ -101,7 -148,7 +101,7 @@@ set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_
  # This variable is a list of pairs of names of source and destination
  # directories. Most of these are used for content GROMACS generates as
  # part of the configuration or build.
- set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR};/;${CMAKE_BINARY_DIR}/src/programs/completion;src/programs/completion;${CMAKE_BINARY_DIR}/share/man/man1;share/man/man1;${CMAKE_BINARY_DIR}/share/man/man7;share/man/man7;${CMAKE_BINARY_DIR}/share/html/final;share/html/final;${CMAKE_BINARY_DIR}/install-guide/final;/")
+ set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${CMAKE_SOURCE_DIR};/;${CMAKE_BINARY_DIR}/src/programs/completion;src/programs/completion;${CMAKE_BINARY_DIR}/docs/man/man1;docs/man/man1;${CMAKE_BINARY_DIR}/docs/man/man7;docs/man/man7;${CMAKE_BINARY_DIR}/docs/old-html/final;docs/old-html/final;${CMAKE_BINARY_DIR}/docs/install-guide/final;/")
  set(CPACK_PACKAGE_CONTACT "gmx-users@gromacs.org")
  set(CPACK_GMX_BUILD_HELP "${GMX_BUILD_HELP}") #Works even though GMX_BUILD_HELP is defined later because it is off by default.
  
@@@ -117,9 -164,14 +117,9 @@@ set(MEMORYCHECK_SUPPRESSIONS_FIL
      "File that contains suppressions for the memory checker")
  include(CTest)
  
 -set(SOURCE_IS_GIT_REPOSITORY OFF)
 -set(SOURCE_IS_SOURCE_DISTRIBUTION OFF)
 -if(EXISTS "${CMAKE_SOURCE_DIR}/.git")
 -    set(SOURCE_IS_GIT_REPOSITORY ON)
 -endif()
 -if(NOT EXISTS "${CMAKE_SOURCE_DIR}/admin/.isreposource")
 -    set(SOURCE_IS_SOURCE_DISTRIBUTION ON)
 -endif()
 +# Variables that accumulate stuff influencing the installed headers
 +set(INSTALLED_HEADER_INCLUDE_DIRS "")
 +set(INSTALLED_HEADER_DEFINITIONS "")
  
  ########################################################################
  # Check and warn if cache generated on a different host is being reused
@@@ -239,6 -291,9 +239,6 @@@ option(GMX_OPENMP "Enable OpenMP-based 
  
  option(GMX_USE_TNG "Use the TNG library for trajectory I/O" ON)
  
 -option(GMX_GIT_VERSION_INFO "Generate git version information" ${SOURCE_IS_GIT_REPOSITORY})
 -mark_as_advanced(GMX_GIT_VERSION_INFO)
 -
  if(UNIX)
      option(GMX_SYMLINK_OLD_BINARY_NAMES "Create symbolic links for pre-5.0 binary names" ON)
  endif()
@@@ -312,12 -367,14 +312,12 @@@ if(GMX_SIMD STREQUAL "AVX_256
  endif()
  
  
 -
 -set(PKG_CFLAGS "")
  if(GMX_DOUBLE)
      add_definitions(-DGMX_DOUBLE)
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_DOUBLE")
 +    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_DOUBLE")
  endif()
  if(GMX_SOFTWARE_INVSQRT)
 -  set(PKG_CFLAGS "${PKG_CFLAGS} -DGMX_SOFTWARE_INVSQRT")
 +    list(APPEND INSTALLED_HEADER_DEFINITIONS "-DGMX_SOFTWARE_INVSQRT")
  endif()
  
  if(WIN32 AND NOT CYGWIN)
@@@ -485,6 -542,25 +485,6 @@@ if(WIN32 AND NOT CYGWIN
      add_definitions(-DNOMINMAX)
  endif()
  
 -# only bother with finding git and using version.h if the source is a git repo
 -if(GMX_GIT_VERSION_INFO)
 -    if (NOT SOURCE_IS_GIT_REPOSITORY)
 -        message(FATAL_ERROR
 -            "Cannot generate git version information from source tree not under git. "
 -            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 -    endif()
 -    # We need at least git v1.5.3 be able to parse git's date output. If not
 -    # found or the version is too small, we can't generate version information.
 -    find_package(Git)
 -
 -    if(NOT GIT_FOUND OR GIT_VERSION_STRING VERSION_LESS "1.5.3")
 -        message(FATAL_ERROR
 -            "No compatible git version found (>= 1.5.3 required). "
 -            "Won't be able to generate development version information. "
 -            "Set GMX_GIT_VERSION_INFO=OFF to proceed.")
 -    endif()
 -endif()
 -
  # Detect boost unless GMX_EXTERNAL_BOOST is explicitly OFF
  # Used for default if GMX_EXTERNAL_BOOST is not defined (first CMake pass)
  if(NOT DEFINED GMX_EXTERNAL_BOOST OR GMX_EXTERNAL_BOOST)
@@@ -520,6 -596,12 +520,6 @@@ if (GMX_BUILD_UNITTESTS AND NOT HAVE_LI
          "Either set GMX_BUILD_UNITTESTS=OFF or tell CMake how to find a working version of libxml2.")
  endif()
  
 -########################################################################
 -# Generate development version info for cache
 -########################################################################
 -# set(GEN_VERSION_INFO_INTERNAL "ON")
 -# include(gmxGenerateVersionString)
 -
  ########################################################################
  # Our own GROMACS tests
  ########################################################################
@@@ -529,6 -611,10 +529,6 @@@ include_directories(BEFORE ${CMAKE_SOUR
  include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/external/thread_mpi/include)
  # Required for config.h, maybe should only be set in src/CMakeLists.txt
  include_directories(BEFORE ${CMAKE_BINARY_DIR}/src)
 -# Required for gmx_header_config_gen.h to be found before installation
 -include_directories(BEFORE ${CMAKE_BINARY_DIR}/src/gromacs/utility)
 -# Required for now to make old code compile
 -include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/gromacs/legacyheaders)
  
  include(gmxTestInlineASM)
  gmx_test_inline_asm_gcc_x86(GMX_X86_GCC_INLINE_ASM)
@@@ -722,6 -808,7 +722,6 @@@ else(
      set(GMX_EXE_LINKER_FLAGS ${GMX_EXE_LINKER_FLAGS} ${OpenMP_LINKER_FLAGS})
      set(GMX_SHARED_LINKER_FLAGS ${GMX_SHARED_LINKER_FLAGS} ${OpenMP_SHARED_LINKER_FLAGS})
  endif()
 -set(PKG_CFLAGS "${PKG_CFLAGS} ${OpenMP_C_FLAGS}")
  
  ########################################################################
  # Specify install locations
@@@ -738,24 -825,17 +738,24 @@@ mark_as_advanced(GMX_LIB_INSTALL_DIR GM
  
  # These variables are used internally to provide a central location for
  # customizing the install locations.
 -set(LIB_INSTALL_DIR  ${GMX_LIB_INSTALL_DIR})
 -set(BIN_INSTALL_DIR  bin)
 -set(DATA_INSTALL_DIR share/${GMX_DATA_INSTALL_DIR})
 -set(MAN_INSTALL_DIR  share/man)
 -set(INCL_INSTALL_DIR include)
 +set(LIB_INSTALL_DIR       ${GMX_LIB_INSTALL_DIR})
 +set(BIN_INSTALL_DIR       bin)
 +set(DATA_INSTALL_DIR      share/${GMX_DATA_INSTALL_DIR})
 +set(MAN_INSTALL_DIR       share/man)
 +# If the nesting level wrt. the installation root is changed,
 +# gromacs-config.cmake.cmakein needs to be adapted.
 +set(CMAKE_INSTALL_DIR     share/cmake)
 +# TODO: Make GMXRC adapt if this is changed
 +set(PKGCONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/pkgconfig)
 +set(INCL_INSTALL_DIR      include)
  
  # These variables get written into config.h for use in finding the data
  # directories.
  set(GMXLIB_SEARCH_DIR share/${GMX_DATA_INSTALL_DIR}/top)
  set(GMXLIB_FALLBACK   ${CMAKE_INSTALL_PREFIX}/${DATA_INSTALL_DIR}/top)
  
 +list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${INCL_INSTALL_DIR})
 +
  # Binary and library suffix options
  include(gmxManageSuffixes)
  
@@@ -801,14 -881,15 +801,14 @@@ install(FILES COPYING DESTINATION ${DAT
  
  if(GMX_EXTERNAL_BOOST)
      include_directories(${Boost_INCLUDE_DIRS})
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -I${Boost_INCLUDE_DIRS}")
 +    list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${Boost_INCLUDE_DIRS})
  else()
      include_directories(BEFORE ${CMAKE_SOURCE_DIR}/src/external/boost)
 +    list(APPEND INSTALLED_HEADER_INCLUDE_DIRS ${INCL_INSTALL_DIR}/gromacs/external/boost)
 +    list(APPEND INSTALLED_HEADED_DEFINITIONS "-DBOOST_NO_TYPEID")
      # typeid not supported for minimal internal version
      # (would add significant amount of code)
      add_definitions(-DBOOST_NO_TYPEID)
 -    # TODO: Propagate the above settings to the installed CMakeFiles.txt template
 -    # (from share/template/)
 -    set(PKG_CFLAGS "${PKG_CFLAGS} -DBOOST_NO_TYPEID -I${CMAKE_INSTALL_PREFIX}/${INCL_INSTALL_DIR}/gromacs/external/boost")
      if (NOT GMX_BUILD_MDRUN_ONLY)
          install(DIRECTORY ${CMAKE_SOURCE_DIR}/src/external/boost/boost
                  DESTINATION ${INCL_INSTALL_DIR}/gromacs/external/boost
@@@ -841,8 -922,7 +841,7 @@@ if (BUILD_TESTING
  endif()
  
  if (NOT GMX_BUILD_MDRUN_ONLY)
-     add_subdirectory(doxygen)
-     add_subdirectory(install-guide)
+     add_subdirectory(docs)
      add_subdirectory(share)
      add_subdirectory(scripts)
  endif()
@@@ -872,15 -952,3 +871,3 @@@ ADD_CUSTOM_TARGET(uninstal
  ###########################
  set_directory_properties(PROPERTIES
              ADDITIONAL_MAKE_CLEAN_FILES "install_manifest.txt")
- ########################################################################
- # Manual                                                               #
- ########################################################################
- option(GMX_BUILD_MANUAL "Whether to try to configure to build the PDF manual" OFF)
- mark_as_advanced(GMX_BUILD_MANUAL)
- if(GMX_BUILD_MANUAL)
-     # Make sure we only do detection of manual-building dependencies
-     # when the user opted in for that.
-     add_subdirectory(manual)
- endif()
diff --combined docs/CMakeLists.txt
index 0000000000000000000000000000000000000000,dd043ecd785c98214d2197e46302558158796664..6947156d36a78ac82e5d0f745bbea659dda48a22
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,305 +1,305 @@@
 -*   Source code - [gromacs-${PROJECT_VERSION}.tar.gz](gromacs-${PROJECT_VERSION}.tar.gz)  
+ #
+ # This file is part of the GROMACS molecular simulation package.
+ #
+ # Copyright (c) 2014, 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.
+ # This directory provides a unified place for building all kinds of
+ # GROMACS documentation. This includes some "static" content (Doxygen
+ # code documentation, reference manual, install guide, old online HTML
+ # pages), and content generated from the gmx program for the various
+ # tools (man and HTML pages). It also provides the "webpage" target,
+ # that combines all of the above (except man pages in man format) into
+ # a form suitable for automated deployment to the GROMACS website. It
+ # also provides the INSTALL file for the tarball.
+ #
+ # All of the markdown content is configured, and we'd like to do that
+ # at build time rather than configure time (for speed, when not
+ # building markdown content). Also, the way they should be configured
+ # varies with whether the source is a tarball or repo, and which file
+ # is being configured. So several *_IS_POSSIBLE variables are used to
+ # direct the configure-time logic so that all appropriate variables
+ # are set by the time the configure-markdown.cmake.in file is
+ # configured, so that later it can do the configuration of all the
+ # markdown files and the right thing will happen in each case.
+ # Even if we aren't going to make the full webpage, set up to put all
+ # the documentation output in the same place, for convenience
+ set(HTML_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/html")
+ file(MAKE_DIRECTORY ${HTML_OUTPUT_DIR})
+ if(${CMAKE_CURRENT_BINARY_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
+     set(MARKDOWN_CONFIGURE_IS_POSSIBLE off)
+ else()
+     set(MARKDOWN_CONFIGURE_IS_POSSIBLE on)
+ endif()
+ find_package(Pandoc)
+ # Set up common infrastructure for configuring markdown at build time.
+ # Do replacement of CMake variables for version strings, etc. The use
+ # of configure-markdown.cmake defers until build time the
+ # configuration of markdown files, which could be faster for all the
+ # configurations that don't make the documentation even though it was
+ # possible, and helps avoid global re-configures if these files
+ # change.
+ set(SCRIPT_TO_CONFIGURE_MARKDOWN ${CMAKE_CURRENT_BINARY_DIR}/configure-markdown.cmake)
+ configure_file(configure-markdown.cmake.in
+     ${SCRIPT_TO_CONFIGURE_MARKDOWN}
+     @ONLY)
+ # Makes a custom command to configure a Markdown file found in the
+ # current source directory with the configure-markdown.make script
+ # produced above. The result is placed in the current binary directory
+ # for future use.
+ function(configure_markdown MARKDOWN_FILE)
+     add_custom_command(
+         OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${MARKDOWN_FILE}
+         COMMAND ${CMAKE_COMMAND}
+             -D FILE_TO_CONFIGURE=${CMAKE_CURRENT_SOURCE_DIR}/${MARKDOWN_FILE}
+             -D CONFIGURED_FILE=${CMAKE_CURRENT_BINARY_DIR}/${MARKDOWN_FILE}
+             -P ${SCRIPT_TO_CONFIGURE_MARKDOWN}
+         DEPENDS
+             ${SCRIPT_TO_CONFIGURE_MARKDOWN}
+             ${CMAKE_CURRENT_SOURCE_DIR}/${MARKDOWN_FILE}
+         COMMENT "Configuring Markdown"
+         VERBATIM
+         )
+ endfunction()
+ # Makes a custom command to make single-page HTML from Markdown. Takes
+ # a NAME argument for an output filename prefix, and a list of full
+ # paths to input files to concatenate with Pandoc into the HTML
+ # output.
+ function(make_markdown_html NAME)
+     add_custom_command(
+         OUTPUT ${HTML_OUTPUT_DIR}/${NAME}.html
+         COMMAND
+             ${PANDOC_EXECUTABLE} ${ARGN} -o ${HTML_OUTPUT_DIR}/${NAME}.html -s --toc --css buttondown.css
+         DEPENDS ${ARGN}
+         VERBATIM
+         )
+ endfunction()
+ # Makes a custom command to make PDF from Markdown. Takes a NAME
+ # argument for an output filename prefix, and a list of full paths to
+ # input files to concatenate with Pandoc into the PDF.
+ function(make_markdown_pdf NAME)
+     add_custom_command(
+         OUTPUT ${HTML_OUTPUT_DIR}/${NAME}.pdf
+         COMMAND
+             ${PANDOC_EXECUTABLE} ${ARGN} -o ${HTML_OUTPUT_DIR}/${NAME}.pdf -s --toc
+         DEPENDS ${ARGN}
+         VERBATIM
+         )
+ endfunction()
+ # function(make_markdown_multipage_html NAME)
+ #     # Make the multi-page HTML install guide
+ #
+ #     # TODO This is currently disabled, because the pandoc-specific
+ #     # buttondown.css doesn't work with the different kind of output
+ #     # makeinfo produces. When we understand better how we want to do
+ #     # generation, decide whether we want multi-page HTML output and
+ #     # how to make it work well.
+ #
+ #     add_custom_command(
+ #         OUTPUT ${HTML_OUTPUT_DIR}/${HTML_DIR}/index.html
+ #         COMMAND
+ #         ${PANDOC_EXECUTABLE} ${ARGN} -o ${NAME}.texi -s
+ #         COMMAND
+ #         ${MAKEINFO_EXECUTABLE} ${NAME}.texi --html -o ${HTML_OUTPUT_DIR}/${NAME} --css-ref buttondown.css
+ #         DEPENDS ${ARGN}
+ #         VERBATIM
+ #         )
+ # endfunction()
+ add_subdirectory(install-guide)
+ add_subdirectory(user-guide)
+ add_subdirectory(man)
+ add_subdirectory(old-html)
+ add_subdirectory(doxygen)
+ option(GMX_BUILD_WEBPAGE "Whether to try to configure to build the GROMACS static webpages" OFF)
+ mark_as_advanced(GMX_BUILD_WEBPAGE)
+ option(GMX_BUILD_MANUAL "Whether to try to configure to build the PDF manual" ${GMX_BUILD_WEBPAGE})
+ mark_as_advanced(GMX_BUILD_MANUAL)
+ if(GMX_BUILD_MANUAL)
+     # Make sure we only do detection of manual-building dependencies
+     # when the user opted in for that.
+     add_subdirectory(manual)
+ endif()
+ set(HTML_BUILD_IS_POSSIBLE OFF)
+ # We can only configure to build the webpage if the user asked for it,
+ # the build is outside of the source dir, and all the components can
+ # be built. There's no need to be talkative if we fail - most people
+ # never need to know.
+ if(GMX_BUILD_WEBPAGE AND
+         GMX_BUILD_HELP AND
+         NOT ${CMAKE_BINARY_DIR} STREQUAL ${CMAKE_SOURCE_DIR} AND
+         MARKDOWN_CONFIGURE_IS_POSSIBLE AND
+         MANUAL_BUILD_IS_POSSIBLE AND
+         PANDOC_EXECUTABLE AND
+         DOXYGEN_EXECUTABLE AND
+         DOXYGEN_MSCGEN_EXECUTABLE)
+     set(HTML_BUILD_IS_POSSIBLE ON)
+ endif()
+ if(HTML_BUILD_IS_POSSIBLE)
+     # For a real build of the webpage, the md5sum of the tarballs must
+     # already be known, and so we may as well require that the real
+     # build of the webpage take place from cmake run from the unpacked
+     # tarball. Then, the *_MD5SUM and *_TARBALL variables will be able
+     # to be set on the cmake command line (e.g. by a Jenkins job
+     # configuration), and we can require that they are set. For local
+     # building of the webpages (e.g. for debugging), those variables
+     # can be left unset, and if so, the download section will not be
+     # constructed.
+     if(NOT SOURCE_IS_SOURCE_DISTRIBUTION)
+         if (SOURCE_TARBALL AND SOURCE_MD5SUM AND
+                 REGRESSIONTESTS_TARBALL AND REGRESSIONTESTS_MD5SUM)
+             set(BUILD_DOWNLOAD_SECTION on)
+         else()
+             set(BUILD_DOWNLOAD_SECTION off)
+         endif()
+     else()
+         foreach(VAR SOURCE_MD5SUM REGRESSIONTESTS_MD5SUM SOURCE_TARBALL REGRESSIONTESTS_TARBALL)
+             if(NOT DEFINED ${VAR})
+                 message(FATAL_ERROR "The build of the webpage requires that ${VAR} is set in the cmake cache, e.g. on the CMake command line")
+             endif()
+         endforeach()
+         set(BUILD_DOWNLOAD_SECTION on)
+     endif()
+     # If building the webpage from the repo, then tarballs may not
+     # exist, and if so, it would not make sense to build that part of
+     # the front page from index.md.
+     if(BUILD_DOWNLOAD_SECTION)
+         set(DOWNLOAD_SECTION
+ "# Downloads
 -*   Regression tests - [regressiontests-${PROJECT_VERSION}.tar.gz](regressiontests-${PROJECT_VERSION}.tar.gz)  
++*   Source code - [gromacs-${GMX_VERSION_STRING}.tar.gz](gromacs-${GMX_VERSION_STRING}.tar.gz)  
+     (md5sum ${SOURCE_MD5SUM})  
+     Other source code versions may be found at <ftp://ftp.gromacs.org/pub/gromacs/>
 -            OUTPUT ${HTML_OUTPUT_DIR}/gromacs-${PROJECT_VERSION}.tar.gz
++*   Regression tests - [regressiontests-${GMX_VERSION_STRING}.tar.gz](regressiontests-${GMX_VERSION_STRING}.tar.gz)  
+     (md5sum ${REGRESSIONTESTS_MD5SUM})
+ ")
+         # Copy the source tarball to the webpage output
+         add_custom_command(
 -               -E copy ${SOURCE_TARBALL} ${HTML_OUTPUT_DIR}/gromacs-${PROJECT_VERSION}.tar.gz
++            OUTPUT ${HTML_OUTPUT_DIR}/gromacs-${GMX_VERSION_STRING}.tar.gz
+             COMMAND ${CMAKE_COMMAND}
 -            OUTPUT ${HTML_OUTPUT_DIR}/regressiontests-${PROJECT_VERSION}.tar.gz
++               -E copy ${SOURCE_TARBALL} ${HTML_OUTPUT_DIR}/gromacs-${GMX_VERSION_STRING}.tar.gz
+             VERBATIM
+             )
+         # Copy the regressiontests tarball to the webpage output
+         add_custom_command(
 -               -E copy ${REGRESSIONTESTS_TARBALL} ${HTML_OUTPUT_DIR}/regressiontests-${PROJECT_VERSION}.tar.gz
++            OUTPUT ${HTML_OUTPUT_DIR}/regressiontests-${GMX_VERSION_STRING}.tar.gz
+             COMMAND ${CMAKE_COMMAND}
 -        OUTPUT ${HTML_OUTPUT_DIR}/manual-${PROJECT_VERSION}.pdf
++               -E copy ${REGRESSIONTESTS_TARBALL} ${HTML_OUTPUT_DIR}/regressiontests-${GMX_VERSION_STRING}.tar.gz
+             VERBATIM
+             )
+     else()
+         set(DOWNLOAD_SECTION "")
+     endif()
+     # Put the CSS in the HTML output directory
+     add_custom_command(
+         OUTPUT ${HTML_OUTPUT_DIR}/buttondown.css
+         COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/buttondown.css ${HTML_OUTPUT_DIR}/buttondown.css
+         DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/buttondown.css
+         VERBATIM
+         )
+     list(APPEND extra_webpage_dependencies ${HTML_OUTPUT_DIR}/buttondown.css)
+     # Make the PDF reference guide
+     # TODO Try to make the PDF arrive directly in ${HTML_OUTPUT_DIR}
+     add_custom_command(
 -            -E remove -f ${HTML_OUTPUT_DIR}/manual-${PROJECT_VERSION}.pdf
++        OUTPUT ${HTML_OUTPUT_DIR}/manual-${GMX_VERSION_STRING}.pdf
+         COMMAND ${CMAKE_COMMAND}
 -            -E copy ${CMAKE_CURRENT_BINARY_DIR}/manual/gromacs.pdf ${HTML_OUTPUT_DIR}/manual-${PROJECT_VERSION}.pdf
++            -E remove -f ${HTML_OUTPUT_DIR}/manual-${GMX_VERSION_STRING}.pdf
+         COMMAND ${CMAKE_COMMAND}
 -            ${HTML_OUTPUT_DIR}/gromacs-${PROJECT_VERSION}.tar.gz
 -            ${HTML_OUTPUT_DIR}/regressiontests-${PROJECT_VERSION}.tar.gz
++            -E copy ${CMAKE_CURRENT_BINARY_DIR}/manual/gromacs.pdf ${HTML_OUTPUT_DIR}/manual-${GMX_VERSION_STRING}.pdf
+         # UseLATEX.cmake makes a target called pdf, not ${CMAKE_CURRENT_BINARY_DIR}/manual/gromacs.pdf
+         DEPENDS pdf
+         VERBATIM
+         )
+     # TODO Move content from the "old" html output into the new user
+     # guide, or delete, as appropriate.
+     if(NOT SOURCE_IS_SOURCE_DISTRIBUTION)
+         # TODO If content remains here once the user guide is in
+         # decent shape, try to make the generated HTML arrive directly
+         # in ${HTML_OUTPUT_DIR}
+         add_custom_target(webpage-html
+             ${CMAKE_COMMAND} -E copy_directory old-html/final ${HTML_OUTPUT_DIR}
+             )
+         add_dependencies(webpage-html html)
+     else()
+         # In the source distribution, the html pages are already
+         # built, so we can avoid making gmx via the html target
+         add_custom_target(webpage-html
+             ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/old-html/final ${HTML_OUTPUT_DIR}
+             )
+     endif()
+     # The Doxygen configuration in doxygen/Doxyfile-common.cmakein
+     # makes all the Doxygen output directly in
+     # ${HTML_OUTPUT_DIR}/doxygen (and makes the directory if it needs
+     # to).
+     # Add other dependencies for doing the webpage build from the real
+     # tarball
+     if(BUILD_DOWNLOAD_SECTION)
+         list(APPEND extra_webpage_dependencies
 -           ${HTML_OUTPUT_DIR}/manual-${PROJECT_VERSION}.pdf
++            ${HTML_OUTPUT_DIR}/gromacs-${GMX_VERSION_STRING}.tar.gz
++            ${HTML_OUTPUT_DIR}/regressiontests-${GMX_VERSION_STRING}.tar.gz
+             )
+     endif()
+     configure_markdown(index.md)
+     make_markdown_html(index ${CMAKE_CURRENT_BINARY_DIR}/index.md)
+     # Add a top-level target for the others to hook onto
+     add_custom_target(webpage
+         DEPENDS
+            ${HTML_OUTPUT_DIR}/index.html
+            install-guide
+            user-guide
++           ${HTML_OUTPUT_DIR}/manual-${GMX_VERSION_STRING}.pdf
+            ${extra_webpage_dependencies}
+         VERBATIM
+         )
+     add_dependencies(webpage webpage-html doc-all)
+ endif()
index 0000000000000000000000000000000000000000,0f99002bea5fd873a2e6498484521b84b5995f9b..60a5ab4c56eb158a68a0a669e884f12c8ad16bab
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,15 +1,15 @@@
 -set(PROJECT_VERSION "@PROJECT_VERSION@")
+ # Helper script that defers configure_file of markdown until build
+ # time, so that changes to the files configured here don't trigger a
+ # global reconfigure
+ # Now configure the values for all the variables that might later
+ # configure any of the markdown files.
++set(PROJECT_VERSION "@GMX_VERSION_STRING@")
+ set(GMX_CMAKE_MINIMUM_REQUIRED_VERSION "@GMX_CMAKE_MINIMUM_REQUIRED_VERSION@")
+ set(REQUIRED_CUDA_VERSION "@REQUIRED_CUDA_VERSION@")
+ set(REQUIRED_CUDA_COMPUTE_CAPABILITY "@REQUIRED_CUDA_COMPUTE_CAPABILITY@")
+ set(REGRESSIONTEST_VERSION "@REGRESSIONTEST_VERSION@")
+ set(DOWNLOAD_SECTION "@DOWNLOAD_SECTION@")
+ configure_file(${FILE_TO_CONFIGURE}
+     ${CONFIGURED_FILE} @ONLY)
index 7401e9560c5e12c6c0c1183f4e7a37d44bc715b1,1e334a4fa8dfeb02e6d44621e70ec7cf0e1cbbe4..264f11446e7c6415dd11b88de21f8c23d47ba497
@@@ -93,10 -93,7 +93,9 @@@ if (DOXYGEN_FOUND
               "@INCLUDE   = ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile-compact\n")
      endif()
  
-     FILE(COPY index.html DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
      configure_file(RunDoxygen.cmake.cmakein RunDoxygen.cmake @ONLY)
 +    gmx_configure_version_file(Doxyfile-version.cmakein Doxyfile-version
 +                               TARGET doxygen-version)
      add_custom_target(doc-full
          ${CMAKE_COMMAND} -DDOCTYPE=full -P RunDoxygen.cmake
          WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
          COMMENT "Extracting Doxygen documentation to XML" VERBATIM)
      add_custom_target(doc-all)
      add_dependencies(doc-all doc-full doc-lib doc-user)
 -
 -    if (GMX_GIT_VERSION_INFO)
 -        add_custom_target(doxygen-version
 -                COMMAND ${CMAKE_COMMAND}
 -                    -D GIT_EXECUTABLE="${GIT_EXECUTABLE}"
 -                    -D PROJECT_VERSION="${PROJECT_VERSION}"
 -                    -D PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
 -                    -D VERSION_CMAKEIN="${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile-version.cmakein"
 -                    -D VERSION_OUT="${CMAKE_CURRENT_BINARY_DIR}/Doxyfile-version"
 -                    -D VERSION_NO_REMOTE_HASH=
 -                    -P ${CMAKE_SOURCE_DIR}/cmake/gmxGenerateVersionInfo.cmake
 -                WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 -                DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile-version.cmakein
 -                COMMENT "Generating version information for Doxygen")
 -        add_dependencies(doc-full doxygen-version)
 -        add_dependencies(doc-lib doxygen-version)
 -        add_dependencies(doc-user doxygen-version)
 -        add_dependencies(doc-xml doxygen-version)
 -    else()
 -        set(GMX_PROJECT_VERSION_STR ${PROJECT_VERSION})
 -        configure_file(Doxyfile-version.cmakein Doxyfile-version)
 -    endif()
 +    add_dependencies(doc-full doxygen-version)
 +    add_dependencies(doc-lib  doxygen-version)
 +    add_dependencies(doc-user doxygen-version)
 +    add_dependencies(doc-xml  doxygen-version)
  
      if (USE_PYTHON_SCRIPTS)
          # TODO: Consider whether this is the best name and location for this
              -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}
              --installed ${CMAKE_CURRENT_BINARY_DIR}/installed-headers.txt
              -l ${CMAKE_CURRENT_BINARY_DIR}/doxygen-check.log
 -            --ignore ${CMAKE_CURRENT_SOURCE_DIR}/suppressions.txt)
 +            --ignore ${CMAKE_CURRENT_SOURCE_DIR}/suppressions.txt
 +            --ignore-cycles ${CMAKE_CURRENT_SOURCE_DIR}/cycle-suppressions.txt)
          add_custom_target(doc-check COMMAND ${doc_check_command}
              COMMENT "Checking Doxygen documentation" VERBATIM)
          add_dependencies(doc-check doc-xml find-installed-headers)
              ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/graphbuilder.py
              -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}
              --installed ${CMAKE_CURRENT_BINARY_DIR}/installed-headers.txt
 +            --ignore-cycles ${CMAKE_CURRENT_SOURCE_DIR}/cycle-suppressions.txt
              -o ${CMAKE_CURRENT_BINARY_DIR}/depgraphs)
          set(dep_graphs_command_dot
              ${CMAKE_COMMAND} -DGRAPHDIR=${graphdir}
index dabd57d0ba8e96f62bd732883783a5810912b281,1ae0a1c340242b81558aba616fa7f089698c7b4c..20a03e069fd4594b10202233c2c42d45b8eaad15
@@@ -10,20 -10,20 +10,20 @@@ FILE_PATTERNS          = *.c *.cpp *.h 
  # FILE_PATTERNS         += *.cu *.cuh
  EXAMPLE_PATH           = @CMAKE_SOURCE_DIR@
  RECURSIVE              = YES
- EXCLUDE                = @CMAKE_SOURCE_DIR@/doxygen/examples \
+ EXCLUDE                = @CMAKE_CURRENT_SOURCE_DIR@/examples \
                           @CMAKE_SOURCE_DIR@/src/contrib \
                           @CMAKE_SOURCE_DIR@/src/external \
                           @CMAKE_SOURCE_DIR@/src/gromacs/selection/parser.cpp \
                           @CMAKE_SOURCE_DIR@/src/gromacs/selection/parser.h \
                           @CMAKE_SOURCE_DIR@/src/gromacs/selection/scanner.cpp @NB_KERNEL_DIRS_TO_IGNORE_IN_DOXYGEN@
  EXCLUDE_SYMBOLS        = YY* yy* _gmx_sel_yy*
 +EXCLUDE_SYMBOLS       += __STDC*
  EXCLUDE_SYMBOLS       += TEST TEST_F TEST_P TYPED_TEST_CASE TYPED_TEST INSTANTIATE_TEST_CASE_P
  EXCLUDE_SYMBOLS       += MOCK_METHOD* MOCK_CONST_METHOD*
  FULL_PATH_NAMES        = YES
  STRIP_FROM_PATH        = @CMAKE_SOURCE_DIR@
  STRIP_FROM_INC_PATH    = @CMAKE_SOURCE_DIR@/src
 -INCLUDE_PATH           = @CMAKE_SOURCE_DIR@/src \
 -                         @CMAKE_SOURCE_DIR@/src/gromacs/legacyheaders
 +INCLUDE_PATH           = @CMAKE_SOURCE_DIR@/src
  HAVE_DOT               = @DOXYGEN_DOT_FOUND@
  DOT_PATH               = @DOXYGEN_DOT_PATH@
  MSCGEN_PATH            = @DOXYGEN_MSCGEN_PATH@
@@@ -64,3 -64,5 +64,5 @@@ DOT_IMAGE_FORMAT       = sv
  DOT_MULTI_TARGETS      = YES
  # According to Doxygen docs, this is required for svg support on IE
  HTML_FILE_EXTENSION    = .xhtml
+ OUTPUT_DIRECTORY       = @HTML_OUTPUT_DIR@/doxygen
index 43e374e9c02c223d491326f5d27f8a191ae864e8,38549a345fdbbe990e0d3a947354a75ec44dfa56..43e374e9c02c223d491326f5d27f8a191ae864e8
@@@ -1,1 -1,1 +1,1 @@@
 -PROJECT_NUMBER = @GMX_PROJECT_VERSION_STR@
 +PROJECT_NUMBER = @GMX_VERSION_STRING_FULL@
index 0d104c06262d6aa086840c481ec02c171e479a49,24bb7ae0c5737ef0b47599658dbc5ebe7f78556f..0d104c06262d6aa086840c481ec02c171e479a49
@@@ -89,6 -89,10 +89,6 @@@ that do not follow the above rules.  Th
  `src/gromacs/legacyheaders/`.  The aim is to gradually get rid of these
  directories and move code into proper modules.
  
 -For similar historical reasons, the include path also includes
 -`src/gromacs/legacyheaders/`.  It is preferred that new code does not depend on
 -this.
 -
  Documentation organization
  ==========================
  
index b87e5469ca3b3bd6fa646e5e23d9d5aec3eb0b4a,0000000000000000000000000000000000000000..b87e5469ca3b3bd6fa646e5e23d9d5aec3eb0b4a
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,12 @@@
 +mdlib -> essentialdynamics
 +mdlib -> imd
 +mdlib -> pulling
 +mdlib -> swap
 +legacyheaders -> swap
 +legacyheaders -> fileio
 +timing -> legacyheaders
 +math -> legacyheaders
 +topology -> fileio
 +topology -> legacyheaders
 +pbcutil -> fileio
 +pbcutil -> legacyheaders
index c7bd01ab359d85347b799473abe79849377a5cd9,13de5ee37731f3dfd33eb0f30c902e3eeb0c0a52..c7bd01ab359d85347b799473abe79849377a5cd9
@@@ -48,7 -48,7 +48,7 @@@ Doxygen documentation file for director
  \ingroup module_commandline
   */
  
 -/*!
 +/*! \libinternal
  \dir src/gromacs/onlinehelp
  \brief \ref module_onlinehelp
  
index 50a7727d29a6b745916a82facfe8d4c7509ac2cf,f292b85abedb06194a0934b8aee968dd7d9b491b..50a7727d29a6b745916a82facfe8d4c7509ac2cf
@@@ -59,18 -59,7 +59,18 @@@ from gmxtree import GromacsTree, DocTyp
  from reporter import Reporter
  
  def check_file(fileobj, reporter):
 -    """Check file-level documentation."""
 +    """Check file-level issues."""
 +    if fileobj.is_source_file() and not fileobj.is_external() and \
 +            fileobj.get_relpath().startswith('src/'):
 +        includes = fileobj.get_includes()
 +        if includes:
 +            firstinclude = includes[0].get_file()
 +            if not firstinclude or firstinclude.get_name() != "gmxpre.h":
 +                reporter.code_issue(includes[0],
 +                                    "does not include \"gmxpre.h\" first")
 +        else:
 +            reporter.code_issue(fileobj, "does not include \"gmxpre.h\"")
 +
      if not fileobj.is_documented():
          # TODO: Add rules for required documentation
          return
@@@ -79,7 -68,7 +79,7 @@@
          # TODO: Add rule to exclude examples from this check
          if fileobj.is_installed():
              reporter.file_error(fileobj, "source file is installed")
 -        if fileobj.get_documentation_type() != DocType.internal:
 +        if fileobj.get_doc_type() != DocType.internal:
              reporter.file_error(fileobj,
                      "source file documentation appears outside full documentation")
          elif fileobj.get_api_type() != DocType.internal:
      elif fileobj.is_test_file() and fileobj.is_installed():
          reporter.file_error(fileobj, "test file is installed")
      elif fileobj.is_installed():
 -        if fileobj.get_documentation_type() != DocType.public:
 +        if fileobj.get_doc_type() != DocType.public:
              reporter.file_error(fileobj,
                      "public header has non-public documentation")
 -    elif fileobj.get_documentation_type() == DocType.public:
 +    elif fileobj.get_doc_type() == DocType.public:
          reporter.file_error(fileobj,
                  "non-installed header has public documentation")
      elif fileobj.get_api_type() == DocType.public:
          reporter.file_error(fileobj,
                  "non-installed header specified as part of public API")
 -    elif fileobj.get_documentation_type() < fileobj.get_api_type():
 +    elif fileobj.get_doc_type() < fileobj.get_api_type():
          reporter.file_error(fileobj,
                  "API type ({0}) conflicts with documentation visibility ({1})"
 -                .format(fileobj.get_api_type(), fileobj.get_documentation_type()))
 +                .format(fileobj.get_api_type(), fileobj.get_doc_type()))
  
      if not fileobj.has_brief_description():
          reporter.file_error(fileobj,
  
  def check_include(fileobj, includedfile, reporter):
      """Check an #include directive."""
 +    otherfile = includedfile.get_file()
      if includedfile.is_system():
 -        if includedfile.get_file():
 -            reporter.code_issue(includedfile,
 -                    "includes local file as {0}".format(includedfile))
 -    else:
 -        otherfile = includedfile.get_file()
 -        if not otherfile:
 -            reporter.code_issue(includedfile,
 -                    "includes non-local file as {0}".format(includedfile))
 -        elif fileobj.is_installed() and not includedfile.is_relative():
 -            reporter.code_issue(includedfile,
 -                    "installed header includes {0} using non-relative path"
 -                    .format(includedfile))
          if not otherfile:
              return
 -        if fileobj.is_installed() and not otherfile.is_installed():
 -            reporter.code_issue(includedfile,
 -                    "installed header includes non-installed {0}"
 -                    .format(includedfile))
 -        filemodule = fileobj.get_module()
 -        othermodule = otherfile.get_module()
 -        if fileobj.is_documented() and otherfile.is_documented():
 -            filetype = fileobj.get_documentation_type()
 -            othertype = otherfile.get_documentation_type()
 -            if filetype > othertype:
 -                reporter.code_issue(includedfile,
 -                        "{0} file includes {1} file {2}"
 -                        .format(filetype, othertype, includedfile))
 -        check_api = (othermodule and othermodule.is_documented() and
 -                filemodule != othermodule)
 -        if check_api and otherfile.get_api_type() < DocType.library:
 +        reporter.code_issue(includedfile,
 +                "includes local file as {0}".format(includedfile))
 +    if not otherfile:
 +        reporter.code_issue(includedfile,
 +                "includes non-local file as {0}".format(includedfile))
 +    # TODO: Reinstantiate a check once there is clarity on what we want
 +    # to enforce.
 +    #elif fileobj.is_installed() and not includedfile.is_relative():
 +    #    reporter.code_issue(includedfile,
 +    #            "installed header includes {0} using non-relative path"
 +    #            .format(includedfile))
 +    if not otherfile:
 +        return
 +    if fileobj.is_installed() and not otherfile.is_installed():
 +        reporter.code_issue(includedfile,
 +                "installed header includes non-installed {0}"
 +                .format(includedfile))
 +    filemodule = fileobj.get_module()
 +    othermodule = otherfile.get_module()
 +    if fileobj.is_documented() and otherfile.is_documented():
 +        filetype = fileobj.get_doc_type()
 +        othertype = otherfile.get_doc_type()
 +        if filetype > othertype:
              reporter.code_issue(includedfile,
 -                    "included file {0} is not documented as exposed outside its module"
 -                    .format(includedfile))
 +                    "{0} file includes {1} file {2}"
 +                    .format(filetype, othertype, includedfile))
 +    check_api = (otherfile.api_type_is_reliable() and filemodule != othermodule)
 +    if check_api and otherfile.get_api_type() < DocType.library:
 +        reporter.code_issue(includedfile,
 +                "included file {0} is not documented as exposed outside its module"
 +                .format(includedfile))
  
  def check_entity(entity, reporter):
      """Check documentation for a code construct."""
@@@ -168,8 -156,8 +168,8 @@@ def check_class(classobj, reporter)
      """Check documentation for a class/struct/union."""
      check_entity(classobj, reporter)
      if classobj.is_documented():
 -        classtype = classobj.get_documentation_type()
 -        filetype = classobj.get_file_documentation_type()
 +        classtype = classobj.get_doc_type()
 +        filetype = classobj.get_file_doc_type()
          if classtype == DocType.public and not classobj.is_in_installed_file():
              reporter.doc_error(classobj,
                      "has public documentation, but is not in installed header")
                      "is in {0} file(s), but appears in {1} documentation"
                      .format(filetype, classtype))
  
 -def check_member(member, reporter):
 +def check_member(member, reporter, check_ignored):
      """Check documentation for a generic member."""
      check_entity(member, reporter)
      if member.is_documented():
 -        if not member.is_visible():
 -            # TODO: This is triggered by members in anonymous namespaces.
 +        if check_ignored and not member.is_visible():
              reporter.doc_note(member,
                      "is documented, but is ignored by Doxygen, because its scope is not documented")
          if member.has_inbody_description():
              reporter.doc_note(member, "has in-body comments, which are ignored")
  
 +def check_cycles(graph, reporter):
 +    """Check cyclic dependencies in a dependency graph.
 +
 +    The graph parameter provides the graph to check.  It should be an object
 +    that has three methods:
 +      iternodes():
 +        Return the list of nodes in the graph.
 +      iteredges(node):
 +        Return the list of edges from a given node.
 +        The list should contain (node, edge) pairs, where node is an object
 +        returned by iternodes() and edge is any object.
 +      report_cycle(cycle, reporter):
 +        Process a found cycle. cycle contains a list of (node, edge) pairs
 +        that describe the cycle.  edge is the edge object that leads _to_
 +        the node in the cycle.
 +
 +    This is implemented using an extended DFS-based strongly connected
 +    component (SCC) search, written using a stack instead of recursion.
 +    The base algorithm is Tarjan's SCC search:
 +      http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm
 +
 +    Each back edge that is encountered during the search is reported as a
 +    cycle.  Additionally, if a cross edge is encountered that is within the
 +    current SCC, the target node and all its children in the current SCC will
 +    be visited again to find all cycles.  All steps except cycle detection are
 +    omitted for such re-traversal.
 +
 +    To avoid duplicates from cycles that do not include all nodes in an SCC,
 +    a cycle is only reported if the target of the back edge is still active
 +    in the search, i.e., all edges from it have not yet been traversed.
 +    """
 +    # The DFS stack; next node is always popped from the end.
 +    # Stores (node, edge) pairs.
 +    # edge is None for start nodes and for post-order processing.
 +    dfsstack = []
 +    for node in graph.iternodes():
 +        dfsstack.append((node, None))
 +    # Stack of visited nodes that have not yet been assigned to a strongly
 +    # connected component.
 +    visitstack = []
 +    # List of nodes in the DFS recursion stack.
 +    currlist = []
 +    # Set of nodes in currlist for more efficient searching.
 +    currset = set()
 +    # Counter for initializing preorder.
 +    visit_count = 0
 +    # DFS pre-order for nodes: initialized when a node is first encountered
 +    # in the search.
 +    preorder = dict()
 +    # Lowest pre-order index reachable from this node.
 +    # Initialized to pre-order, and updated during post-order processing.
 +    linkorder = dict()
 +    # Set to True for a node when first encountered, and set to False when
 +    # a strongly connected component has been processed.
 +    in_progress = dict()
 +    # The DFS search
 +    while dfsstack:
 +        currnode, curredge = dfsstack.pop()
 +        # curredge is None if this is a start node or post-order traversal.
 +        # currlist is empty if this is a start node.
 +        if curredge is None and currlist:
 +            # All children visited: post-order processing.
 +            done = currlist.pop()[0]
 +            assert done == currnode
 +            currset.remove(currnode)
 +            # If this is the first time this node is encountered, fill
 +            # linkorder and check for strongly connected components.
 +            if linkorder[currnode] == preorder[currnode]:
 +                children = [x for x, dummy in graph.iteredges(currnode) if in_progress[x]]
 +                if children:
 +                    linkorder[currnode] = min([linkorder[x] for x in children])
 +                if preorder[currnode] <= linkorder[currnode]:
 +                    # This is a root of a strongly connected component.
 +                    while visitstack:
 +                        node = visitstack.pop()
 +                        in_progress[node] = False
 +                        if node == currnode:
 +                            break
 +                    else:
 +                        assert False
 +            continue
 +        if currnode not in preorder:
 +            # First encounter of this node: pre-order processing.
 +            preorder[currnode] = visit_count
 +            linkorder[currnode] = visit_count
 +            visitstack.append(currnode)
 +            visit_count += 1
 +            in_progress[currnode] = True
 +        elif not in_progress[currnode]:
 +            # Do not enter processed components again.
 +            continue
 +        currlist.append((currnode, curredge))
 +        currset.add(currnode)
 +        # add entry for post-order traversal
 +        dfsstack.append((currnode, None))
 +        for nextnode, edge in graph.iteredges(currnode):
 +            if nextnode not in preorder:
 +                # Not seen previously: push
 +                dfsstack.append((nextnode, edge))
 +            else:
 +                # If an already visited node is in the same component, it is
 +                # either part of a cycle, or we need to traverse it again to
 +                # find all cycles.
 +                if in_progress[nextnode]:
 +                    if nextnode not in currset:
 +                        dfsstack.append((nextnode, edge))
 +                    # Only report cycles to nodes that haven't been processed
 +                    # yet to avoid duplicates.
 +                    elif linkorder[nextnode] == preorder[nextnode]:
 +                        for index in xrange(len(currlist)):
 +                            if currlist[index][0] == nextnode:
 +                                cycle = [(nextnode, edge)]
 +                                cycle.extend(currlist[index+1:])
 +                                graph.report_cycle(cycle, reporter)
 +                                break
 +                        else:
 +                            assert False
 +
 +class ModuleDependencyGraph(object):
 +
 +    """Module dependency graph representation for check_cycles().
 +
 +    In the reported graph, the nodes are gmxtree.Module objects and the edges
 +    are gmxtree.ModuleDependency objects.
 +    """
 +
 +    def __init__(self, tree):
 +        self._tree = tree
 +
 +    def iternodes(self):
 +        for module in self._tree.get_modules():
 +            if module.get_name() != 'module_testutils':
 +                yield module
 +
 +    def iteredges(self, module):
 +        for dependency in module.get_dependencies():
 +            if dependency.get_other_module().get_name() != 'module_testutils':
 +                yield (dependency.get_other_module(), dependency)
 +
 +    def report_cycle(self, cycle, reporter):
 +        if any([x[1].is_cycle_suppressed() for x in cycle]):
 +            # TODO: Report unused suppressions.
 +            return
 +        modulelist = ' -> '.join([x[0].get_name()[7:] for x in cycle])
 +        summary = 'module-level cyclic dependency: ' + modulelist
 +        reporter.cyclic_issue(summary)
 +
  def main():
      """Run the checking script."""
      parser = OptionParser()
                        help='Write issues into a given log file in addition to stderr')
      parser.add_option('--ignore',
                        help='Set file with patterns for messages to ignore')
 +    parser.add_option('--ignore-cycles',
 +                      help='Set file with module dependencies to ignore in cycles')
      parser.add_option('--check-ignored', action='store_true',
 -                      help='Check documentation ignored by Doxygen')
 +                      help='Issue notes for comments ignored by Doxygen')
      parser.add_option('-q', '--quiet', action='store_true',
                        help='Do not write status messages')
      options, args = parser.parse_args()
      if not options.quiet:
          sys.stderr.write('Reading source files...\n')
      tree.scan_files()
 +    if options.ignore_cycles:
 +        tree.load_cycle_suppression_list(options.ignore_cycles)
      if not options.quiet:
          sys.stderr.write('Reading Doxygen XML files...\n')
      tree.load_xml()
          check_class(classobj, reporter)
  
      for memberobj in tree.get_members():
 -        if memberobj.is_visible() or options.check_ignored:
 -            check_member(memberobj, reporter)
 +        check_member(memberobj, reporter, options.check_ignored)
 +
 +    check_cycles(ModuleDependencyGraph(tree), reporter)
  
      reporter.write_pending()
      reporter.report_unused_filters()
diff --combined docs/doxygen/doxygen.md
index 5a323d120cc65d9949747926eba6e17c990c0c4e,854989caecc00c4e660cd447c8ee5ad767ab9d42..2373591a730037dcb81ac939aa0e193de3148e78
@@@ -61,9 -61,10 +61,10 @@@ Building the documentatio
  ==========================
  
  If you simply want to see up-to-date documentation, you can go to
- http://jenkins.gromacs.org/job/Doxygen_Nightly_master/javadoc/html-lib/index.xhtml
+ http://jenkins.gromacs.org/job/Documentation_Nightly_master/javadoc/html-lib/index.xhtml
  to see the documentation for the current development version.
- Jenkins also runs Doxygen for all changes pushed to Gerrit for master, and the
+ Jenkins also runs Doxygen for all changes pushed to Gerrit for
+ release-5-0 and master branches, and the
  resulting documentation can be viewed from the link posted by Jenkins.  The
  Doxygen build is marked as unstable if it introduces any Doxygen warnings.
  
@@@ -76,7 -77,7 +77,7 @@@ For local work, it is generally a good 
  CMake option, which removes some large generated graphs from the documentation
  and speeds up the process significantly.
  
- All files related to Doxygen reside in the `doxygen/` subdirectory in the source
+ All files related to Doxygen reside in the `docs/doxygen/` subdirectory in the source
  and build trees.  In a freshly checked out source tree, this directory contains
  various `Doxyfile-*.cmakein` files.  When you run CMake, corresponding files
  `Doxyfile-user`, `Doxyfile-lib`, and `Doxyfile-full` are generated at the
@@@ -89,14 -90,15 +90,15 @@@ You can run Doxygen directly with one o
  be produced under the current working directory), or build one of the
  `doc-user`, `doc-lib`, and `doc-full` targets.  The targets run Doxygen in a
  quieter mode and only show the warnings if there were any, and put the output
- under `doxygen/` in the build tree.  The `doc-all` target builds all three
+ under `docs/html/doxygen/` in the build tree, so that the Doxygen build
+ cooperates with the broader `webpage` target.  The `doc-all` target builds all three
  targets with less typing.
  
  The generated documentation is put under `html-user/`, `html-lib/`, and/or
- `html-full/` in the output directory.  Open `index.xhtml` file from one of
+ `html-full/`.  Open `index.xhtml` file from one of
  these subdirectories to start browsing (for \Gromacs developers, the
  `html-lib/` is a reasonable starting point).  Log files with all Doxygen
- warnings are also produced as `doxygen-*.log`, so you can inspect them after
+ warnings are also produced as `docs/doxygen/doxygen-*.log`, so you can inspect them after
  the run.
  
  You will need Doxygen 1.8.5 to build the current documentation.  Other versions
@@@ -269,8 -271,8 +271,8 @@@ rules about dependencies between the mo
  run automatically.
  
  The checker currently checks for a few different types of issues:
 -* For all Doxygen documentation (currently does not apply for members within
 -  anonymous namespaces or members that do not appear in the documentation):
 +* For all Doxygen documentation (currently does not apply for members that do
 +  not appear in the documentation):
     * If a member has documentation, it should have a brief description.
     * A note is issued for in-body documentation for functions, since this is
       ignored by our current settings.
    (\c \\defgroup module_foo exists for the subdirectory):
     * Such files should not be included from outside their module if they are
       undocumented or are not specified as part of library or public API.
 +* For all modules:
 +   * There should not be cyclic include dependencies between modules.
  
  The checker is based on extracting the Doxygen documentation in XML format.
  This information is then read using a Python script, and combined with
@@@ -333,23 -333,11 +335,23 @@@ work even though the script reports abs
  Empty lines and lines starting with `#` are ignored.
  
  To add suppression for an issue, the line that reports the issue can be copied
- into `suppressios.txt`, and the line number (if any) removed.  If the
+ into `suppressions.txt`, and the line number (if any) removed.  If the
  issue does not have a file name (or a pseudo-file) associated, a leading `:`
  must be added.  To cover many similar issues, parts of the line can then be
  replaced with wildcards.
  
 +A separate suppression mechanism is in place for cyclic dependencies: to
 +suppress a cycle between moduleA and moduleB, add a line with format
 +
 +    moduleA -> moduleB
 +
 +into `doxygen/cycle-suppressions.txt`.  This suppresses all cycles that contain
 +the mentioned edge.  Since a cycle contains multiple edges, the suppression
 +should be made for the edge that is determined to be an incorrect dependency.
 +This also affects the layout of the include dependency graphs (see below): the
 +suppressed edge is not considered when determining the dependency order, and is
 +shown as invalid in the graph.
 +
  For some false positives from the script, the suppression mechanism is the
  easiest way to silence the script, but otherwise the goal would be to minimize
  the number of suppressions.
  As a side effect, the XML extraction makes Doxygen parse all comments in the
  code, even if they do not appear in the documentation.  This can reveal latent
  issues in the comments, like invalid Doxygen syntax.  The messages from the XML
- parsing are stored in `doxygen/doxygen-xml.log` in the build tree, similar to
+ parsing are stored in `docs/doxygen/doxygen-xml.log` in the build tree, similar to
  other Doxygen runs.
  
  The `doc-check` target requires Python 2.7 (other versions may work, but have
@@@ -454,7 -442,7 +456,7 @@@ Doxygen page
  -------------
  
  The pages that are accessible through navigation from the front page are
- written using Markdown and are located under `doxygen/`.  Each page should be
+ written using Markdown and are located under `docs/doxygen/`.  Each page should be
  placed in the page hierarchy by making it a subpage of another page, i.e., it
  should be referenced once using \c \\subpage.  `mainpage.md` is the root of the
  hierarchy.
@@@ -477,7 -465,7 +479,7 @@@ module).  This header should contain th
  module.  The name of the group should be `module_`<em>name</em>, where _name_
  is the name of the subdirectory that hosts the module.
  
- The module should be added to an appropriate group (see `doxygen/misc.cpp` for
+ The module should be added to an appropriate group (see `docs/doxygen/misc.cpp` for
  definitions) using \c \\ingroup to organize the "Modules" tab in the generated
  documentation.
  
@@@ -573,7 -561,7 +575,7 @@@ the modules themselves are documented
  provide a high-level overview of the source tree on the generated "Files" page.
  A reference to the module is typically sufficient as a brief description for a
  directory.  All directories are currently documented in
- `doxygen/directories.cpp`.
+ `docs/doxygen/directories.cpp`.
  
  
  Examples
index 49de76261c8bf72c27930099ecbd9499dbfd97b3,f26ad9cdbdf6102913fd8bed6760aefc60a9f120..49de76261c8bf72c27930099ecbd9499dbfd97b3
@@@ -370,10 -370,6 +370,10 @@@ class Member(Entity)
      def __init__(self, name, refid):
          Entity.__init__(self, name, refid)
          self._parents = set()
 +        self._class = None
 +        self._namespace = None
 +        self._files = set()
 +        self._group = None
          self._location = None
          self._alternates = set()
          self._loaded = False
      def add_parent_compound(self, compound):
          """Add a compound that contains this member."""
          self._parents.add(compound)
 +        if isinstance(compound, Class):
 +            assert self._class is None
 +            self._class = compound
 +        elif isinstance(compound, Namespace):
 +            assert self._namespace is None
 +            self._namespace = compound
 +        elif isinstance(compound, File):
 +            self._files.add(compound)
 +        elif isinstance(compound, Group):
 +            assert self._group is None
 +            self._group = compound
 +        else:
 +            assert False
  
 -    def _get_raw_location(self):
 -        """Returns the BodyLocation object associated with this member.
 +    def merge_definition(self, definition):
 +        """Merge another member into this.
  
 -        This is necessary so that EnumValue can override it report a non-empty
 -        location: Doxygen doesn't provide any location for <enumvalue>.
 +        See DocumentationSet.merge_duplicates().
          """
 -        return self._location
 -
 -    def get_parent_compounds(self):
 -        return self._parents
 -
 -    def get_inherited_visibility(self):
 -        return max([parent.get_visibility() for parent in self._parents])
 -
 -    def is_visible(self):
 -        return self.get_inherited_visibility() != DocType.none
 -
 -    def has_same_body_location(self):
 -        return self._get_raw_location().has_same_body_location()
 -
 -    def get_reporter_location(self):
 -        return self._get_raw_location().get_reporter_location()
 -
 -    def get_location(self):
 -        return self._get_raw_location().get_location()
 -
 -    def get_body_location(self):
 -        return self._get_raw_location().get_body_location()
 -
 -    def merge_definition(self, definition):
 +        assert self._class is None
 +        assert definition._class is None
 +        assert self._group == definition._group
 +        assert self._namespace == definition._namespace
          self._parents.update(definition._parents)
 +        self._files.update(definition._files)
          self._alternates.add(definition)
  
      def load_details_from_element(self, rootelem, xmlpath):
          """
          return False
  
 +    def _get_raw_location(self):
 +        """Returns the BodyLocation object associated with this member.
 +
 +        This is necessary so that EnumValue can override it report a non-empty
 +        location: Doxygen doesn't provide any location for <enumvalue>.
 +        """
 +        return self._location
 +
 +    def get_reporter_location(self):
 +        return self._get_raw_location().get_reporter_location()
 +
 +    def get_location(self):
 +        """Return main location for the member.
 +
 +        This typically corresponds to the declaration.
 +        """
 +        return self._get_raw_location().get_location()
 +
 +    def get_body_location(self):
 +        """Return location of the body for the member.
 +
 +        Some types of members do not have a body location, in which case this
 +        returns None.
 +        """
 +        return self._get_raw_location().get_body_location()
 +
 +    def has_same_body_location(self):
 +        """Check whether the main location is the same as body location."""
 +        return self._get_raw_location().has_same_body_location()
 +
 +    def get_namespace(self):
 +        return self._namespace
 +
 +    def get_parent_compounds(self):
 +        return self._parents
 +
 +    def get_inherited_visibility(self):
 +        return max([parent.get_visibility() for parent in self._parents])
 +
      def show(self):
          self.show_base()
 +        if self._alternates:
 +            idlist = [x.get_id() for x in self._alternates]
 +            print 'Alt. IDs:   {0}'.format(', '.join(idlist))
          print 'Parent vis: {0}'.format(self.get_inherited_visibility())
          print 'Location:   {0}'.format(self.get_location().get_full_string())
          print 'Body loc:   {0}'.format(self.get_body_location().get_full_string())
@@@ -578,7 -539,6 +578,7 @@@ class Compound(Entity)
      contains references to contained compounds, and details of all members
      within the compound.
      """
 +
      def __init__(self, name, refid):
          Entity.__init__(self, name, refid)
          self._members = dict()
@@@ -931,9 -891,6 +931,9 @@@ class Namespace(Compound)
      def get_reporter_location(self):
          return self._doclocation.get_reporter_location()
  
 +    def is_anonymous(self):
 +        return 'anonymous_namespace{' in self.get_name()
 +
      def show(self):
          self.show_base()
          print 'Doc. loc.: {0}'.format(self._doclocation.get_full_string())
@@@ -1136,9 -1093,9 +1136,9 @@@ class DocumentationSet(object)
      def merge_duplicates(self):
          """Merge duplicate member definitions based on body location.
  
 -        At least for functions that are declared in a header, but have their
 -        body in a source file, Doxygen seems to create two different IDs, but
 -        the contents of the members are the same, except for the location
 +        At least for some functions that are declared in a header, but have
 +        their body in a source file, Doxygen seems to create two different IDs,
 +        but the contents of the members are the same, except for the location
          attribute.  This method merges members that have identical name and
          body location into a single member that keeps the information from both
          instances (they should only differ in the location attribute and in
      def get_groups(self, name):
          return self.get_compounds(Group, lambda x: x.get_name() in name)
  
 -    def get_namespaces(self, name):
 -        return self.get_compounds(Namespace, lambda x: x.get_name() in name)
 +    def get_namespaces(self, name=None):
 +        if name:
 +            return self.get_compounds(Namespace, lambda x: x.get_name() in name)
 +        else:
 +            return self.get_compounds(Namespace)
  
      def get_classes(self, name=None):
          if name:
diff --combined docs/doxygen/gmxtree.py
index 77665dfc6c99b3fd127fdd7095895be4b7e84ec7,c974d13a1454d3462cad74e9d46899b86a811270..aaf21e01c7450b47988cf711ef82d17db2acc5c1
@@@ -75,8 -75,8 +75,8 @@@ class IncludedFile(object)
  
      """Information about an #include directive in a file."""
  
 -    def __init__(self, abspath, lineno, included_file, included_path, is_relative, is_system):
 -        self._abspath = abspath
 +    def __init__(self, including_file, lineno, included_file, included_path, is_relative, is_system):
 +        self._including_file = including_file
          self._line_number = lineno
          self._included_file = included_file
          self._included_path = included_path
      def is_relative(self):
          return self._is_relative
  
 +    def get_including_file(self):
 +        return self._including_file
 +
      def get_file(self):
          return self._included_file
  
      def get_reporter_location(self):
 -        return reporter.Location(self._abspath, self._line_number)
 +        return reporter.Location(self._including_file.get_abspath(), self._line_number)
  
  class File(object):
  
                  fileobj = sourcetree.get_file(fullpath)
              else:
                  fileobj = sourcetree.find_include_file(includedpath)
 -        self._includes.append(IncludedFile(self.get_abspath(), lineno, fileobj, includedpath,
 +        self._includes.append(IncludedFile(self, lineno, fileobj, includedpath,
                  is_relative, is_system))
  
      def scan_contents(self, sourcetree):
      def get_name(self):
          return os.path.basename(self._abspath)
  
 -    def get_documentation_type(self):
 +    def get_doc_type(self):
          if not self._rawdoc:
              return DocType.none
          return self._rawdoc.get_visibility()
      def get_api_type(self):
          return self._apitype
  
 +    def api_type_is_reliable(self):
 +        if self._apitype > DocType.internal:
 +            return True
 +        module = self.get_module()
 +        return module and module.is_documented()
 +
 +    def is_public(self):
 +        if self.api_type_is_reliable():
 +            return self.get_api_type() == DocType.public
 +        return self.get_api_type() == DocType.public or self.is_installed()
 +
 +    def is_module_internal(self):
 +        if self.is_source_file():
 +            return True
 +        return not self.is_installed() and self.get_api_type() <= DocType.internal
 +
      def get_expected_module(self):
          return self._dir.get_module()
  
@@@ -327,37 -308,6 +327,37 @@@ class Directory(object)
          for fileobj in self._files:
              yield fileobj
  
 +class ModuleDependency(object):
 +
 +    """Dependency between modules."""
 +
 +    def __init__(self, othermodule):
 +        """Initialize empty dependency object with given module as dependency."""
 +        self._othermodule = othermodule
 +        self._includedfiles = []
 +        self._cyclesuppression = None
 +
 +    def add_included_file(self, includedfile):
 +        """Add IncludedFile that is part of this dependency."""
 +        assert includedfile.get_file().get_module() == self._othermodule
 +        self._includedfiles.append(includedfile)
 +
 +    def set_cycle_suppression(self):
 +        """Set suppression on cycles containing this dependency."""
 +        self._cyclesuppression = True
 +
 +    def is_cycle_suppressed(self):
 +        """Return whether cycles containing this dependency are suppressed."""
 +        return self._cyclesuppression is not None
 +
 +    def get_other_module(self):
 +        """Get module that this dependency is to."""
 +        return self._othermodule
 +
 +    def get_included_files(self):
 +        """Get IncludedFile objects for the individual include dependencies."""
 +        return self._includedfiles
 +
  class Module(object):
  
      """Code module in the GROMACS source tree.
          self._rawdoc = None
          self._rootdir = rootdir
          self._group = None
 +        self._dependencies = dict()
  
      def set_doc_xml(self, rawdoc, sourcetree):
          """Assiociate Doxygen documentation entity with the module."""
                  if groupname.startswith('group_'):
                      self._group = groupname[6:]
  
 +    def add_dependency(self, othermodule, includedfile):
 +        """Add #include dependency from a file in this module."""
 +        assert includedfile.get_file().get_module() == othermodule
 +        if othermodule not in self._dependencies:
 +            self._dependencies[othermodule] = ModuleDependency(othermodule)
 +        self._dependencies[othermodule].add_included_file(includedfile)
 +
      def is_documented(self):
          return self._rawdoc is not None
  
      def get_group(self):
          return self._group
  
 +    def get_dependencies(self):
 +        return self._dependencies.itervalues()
 +
 +class Namespace(object):
 +
 +    """Namespace in the GROMACS source code."""
 +
 +    def __init__(self, rawdoc):
 +        self._rawdoc = rawdoc
 +
 +    def is_anonymous(self):
 +        return self._rawdoc.is_anonymous()
  
  class Class(object):
  
      def has_brief_description(self):
          return self._rawdoc.has_brief_description()
  
 -    def get_documentation_type(self):
 +    def get_doc_type(self):
 +        """Return documentation type (visibility) for the class.
 +
 +        In addition to the actual code, this encodes GROMACS-specific logic
 +        of setting EXTRACT_LOCAL_CLASSES=YES only for the full documentation.
 +        Local classes never appear outside the full documentation, no matter
 +        what is their visibility.
 +        """
          if not self.is_documented():
              return DocType.none
          if self._rawdoc.is_local():
              return DocType.internal
          return self._rawdoc.get_visibility()
  
 -    def get_file_documentation_type(self):
 -        return max([fileobj.get_documentation_type() for fileobj in self._files])
 +    def get_file_doc_type(self):
 +        return max([fileobj.get_doc_type() for fileobj in self._files])
  
      def is_in_installed_file(self):
          return any([fileobj.is_installed() for fileobj in self._files])
  
 +class Member(object):
 +
 +    """Member (in Doxygen terminology) in the GROMACS source tree.
 +
 +    Currently, modeling is limited to the minimal set of properties that the
 +    checker uses.
 +    """
 +
 +    def __init__(self, rawdoc, namespace):
 +        self._rawdoc = rawdoc
 +        self._namespace = namespace
 +
 +    def get_name(self):
 +        return self._rawdoc.get_name()
 +
 +    def get_reporter_location(self):
 +        return self._rawdoc.get_reporter_location()
 +
 +    def is_documented(self):
 +        return self._rawdoc.is_documented()
 +
 +    def has_brief_description(self):
 +        return self._rawdoc.has_brief_description()
 +
 +    def has_inbody_description(self):
 +        return self._rawdoc.has_inbody_description()
 +
 +    def is_visible(self):
 +        """Return whether the member is visible in Doxygen documentation.
 +
 +        Doxygen ignores members whose parent compounds are not documented.
 +        However, when EXTRACT_ANON_NPACES=ON (which is set for our full
 +        documentation), members of anonymous namespaces are extracted even if
 +        the namespace is the only parent and is not documented.
 +        """
 +        if self._namespace and self._namespace.is_anonymous():
 +            return True
 +        return self._rawdoc.get_inherited_visibility() != DocType.none
 +
 +
  class GromacsTree(object):
  
      """Root object for navigating the GROMACS source tree.
          self._files = dict()
          self._modules = dict()
          self._classes = set()
 +        self._namespaces = set()
 +        self._members = set()
          self._walk_dir(os.path.join(self._source_root, 'src'))
          rootdir = self._get_dir(os.path.join('src', 'gromacs'))
          for subdir in rootdir.get_subdirectories():
          for fileobj in self._files.itervalues():
              if not fileobj.is_external():
                  fileobj.scan_contents(self)
 +                module = fileobj.get_module()
 +                if module:
 +                    for includedfile in fileobj.get_includes():
 +                        otherfile = includedfile.get_file()
 +                        if otherfile:
 +                            othermodule = otherfile.get_module()
 +                            if othermodule and othermodule != module:
 +                                module.add_dependency(othermodule, includedfile)
  
      def load_xml(self, only_files=False):
          """Load Doxygen XML information.
          If only_files is True, XML data is not loaded for code constructs, but
          only for files, directories, and their potential parents.
          """
-         xmldir = os.path.join(self._build_root, 'doxygen', 'xml')
+         xmldir = os.path.join(self._build_root, 'docs', 'html', 'doxygen', 'xml')
          self._docset = xml.DocumentationSet(xmldir, self._reporter)
          if only_files:
              self._docset.load_file_details()
          self._load_modules()
          self._load_files()
          if not only_files:
 +            self._load_namespaces()
              self._load_classes()
 +            self._load_members()
  
      def _load_dirs(self):
          """Load Doxygen XML directory information."""
              fileobj.set_doc_xml(filedoc, self)
              self._docmap[filedoc] = fileobj
  
 +    def _load_namespaces(self):
 +        """Load Doxygen XML namespace information."""
 +        nsdocs = self._docset.get_namespaces()
 +        for nsdoc in nsdocs:
 +            nsobj = Namespace(nsdoc)
 +            self._docmap[nsdoc] = nsobj
 +            self._namespaces.add(nsobj)
 +
      def _load_classes(self):
          """Load Doxygen XML class information."""
          classdocs = self._docset.get_classes()
              self._docmap[classdoc] = classobj
              self._classes.add(classobj)
  
 +    def _load_members(self):
 +        """Load Doxygen XML member information."""
 +        memberdocs = self._docset.get_members()
 +        for memberdoc in memberdocs:
 +            nsdoc = memberdoc.get_namespace()
 +            nsobj = self.get_object(nsdoc)
 +            memberobj = Member(memberdoc, nsobj)
 +            self._docmap[memberdoc] = memberobj
 +            self._members.add(memberobj)
 +
      def _get_dir(self, relpath):
          """Get directory object for a path relative to source tree root."""
          return self._dirs.get(relpath)
  
      def find_include_file(self, includedpath):
          """Find a file object corresponding to an include path."""
 -        for testdir in ('src', 'src/gromacs/legacyheaders', 'src/external/thread_mpi/include'):
 +        for testdir in ('src', 'src/external/thread_mpi/include',
 +                'src/external/tng_io/include'):
              testpath = os.path.join(testdir, includedpath)
              if testpath in self._files:
                  return self._files[testpath]
                  continue
              self._files[relpath].set_installed()
  
 +    def load_cycle_suppression_list(self, filename):
 +        """Load a list of edges to suppress in cycles.
 +
 +        These edges between modules, if present, will be marked in the
 +        corresponding ModuleDependency objects.
 +        """
 +        with open(filename, 'r') as fp:
 +            for line in fp:
 +                line = line.strip()
 +                if not line or line.startswith('#'):
 +                    continue
 +                modulenames = ['module_' + x.strip() for x in line.split('->')]
 +                if len(modulenames) != 2:
 +                    self._reporter.input_error(
 +                            "invalid cycle suppression line: {0}".format(line))
 +                    continue
 +                firstmodule = self._modules.get(modulenames[0])
 +                secondmodule = self._modules.get(modulenames[1])
 +                if not firstmodule or not secondmodule:
 +                    self._reporter.input_error(
 +                            "unknown modules mentioned on cycle suppression line: {0}".format(line))
 +                    continue
 +                for dep in firstmodule.get_dependencies():
 +                    if dep.get_other_module() == secondmodule:
 +                        # TODO: Check that each suppression is actually part of
 +                        # a cycle.
 +                        dep.set_cycle_suppression()
 +
      def get_object(self, docobj):
          """Get tree object for a Doxygen XML object."""
 +        if docobj is None:
 +            return None
          return self._docmap.get(docobj)
  
      def get_files(self):
  
      def get_members(self):
          """Get iterable for all members (in Doxygen terms) in the source tree."""
 -        # TODO: Add wrappers to solve some issues.
 -        return self._docset.get_members()
 +        return self._members
index 10db472c407a5aa91245b245a807734311813452,47f1f70be8ce9c08c429e9ac3cb03353c265ff69..10db472c407a5aa91245b245a807734311813452
@@@ -97,9 -97,8 +97,9 @@@ EdgeType.public = EdgeType(4
  # Intramodule dependency
  EdgeType.intramodule = EdgeType(5)
  EdgeType.legacy = EdgeType(6)
 +EdgeType.cyclic = EdgeType(7)
  # Invalid dependency
 -EdgeType.undocumented = EdgeType(7)
 +EdgeType.undocumented = EdgeType(8)
  
  class Edge(object):
  
              properties = 'color=black'
          elif self._edgetype == EdgeType.legacy:
              properties = 'color=grey75'
 +        elif self._edgetype == EdgeType.cyclic:
 +            properties = 'color=red, constraint=no'
          else: # undocumented
              properties = 'color=red'
          return '{0} -> {1} [{2}]'.format(self._fromnode.get_nodename(),
@@@ -339,57 -336,49 +339,57 @@@ class GraphBuilder(object)
          filenodes[fileobj] = node
          return node
  
 -    def _create_file_edge(self, fromfile, tofile, filenodes):
 -        """Create edge between two file objects.
 +    def _get_file_edge_type(self, fromfile, tofile):
 +        """Get EdgeType for an edge between two file objects.
  
          Determines the type for the edge from the information provided by
          gmxtree.
          """
          intramodule = (fromfile.get_module() == tofile.get_module())
 -        is_legacy = not tofile.get_module().is_documented()
 +        is_legacy = not tofile.api_type_is_reliable()
          if fromfile.get_module() == tofile.get_module():
 -            edgetype = EdgeType.intramodule
 -        elif tofile.get_api_type() == DocType.internal:
 +            return EdgeType.intramodule
 +        elif tofile.get_api_type() == DocType.internal and not tofile.is_public():
              if is_legacy:
 -                edgetype = EdgeType.legacy
 +                return EdgeType.legacy
              else:
 -                edgetype = EdgeType.undocumented
 +                return EdgeType.undocumented
          elif fromfile.is_test_file():
 -            edgetype = EdgeType.test
 +            return EdgeType.test
          elif tofile.is_test_file():
 -            edgetype = EdgeType.undocumented
 -        elif fromfile.is_source_file() or \
 -                (fromfile.get_api_type() <= DocType.internal and \
 -                not fromfile.is_installed()):
 -            if tofile.get_api_type() == DocType.public:
 -                edgetype = EdgeType.pubimpl
 +            return EdgeType.undocumented
 +        elif fromfile.is_module_internal():
 +            if tofile.is_public():
 +                return EdgeType.pubimpl
              elif tofile.get_api_type() == DocType.library:
 -                edgetype = EdgeType.libimpl
 -            elif is_legacy or not tofile.is_documented():
 -                edgetype = EdgeType.legacy
 +                return EdgeType.libimpl
 +            elif is_legacy:
 +                return EdgeType.legacy
 +            elif not tofile.is_documented():
 +                return EdgeType.undocumented
              else:
                  raise ValueError('Unknown edge type between {0} and {1}'
 -                        .format(fromfile.path, tofile.path))
 +                        .format(fromfile.get_relpath(), tofile.get_relpath()))
          elif fromfile.get_api_type() == DocType.library:
 -            edgetype = EdgeType.library
 -        elif fromfile.get_api_type() == DocType.public or fromfile.is_installed():
 -            if tofile.get_api_type() == DocType.public or \
 -                    tofile.get_documentation_type() == DocType.public or \
 -                    (tofile.is_installed() and not tofile.is_documented()):
 -                edgetype = EdgeType.public
 +            return EdgeType.library
 +        elif fromfile.is_public() or fromfile.is_installed():
 +            if tofile.is_public() or tofile.is_installed():
 +                return EdgeType.public
              else:
 -                edgetype = EdgeType.undocumented
 +                return EdgeType.undocumented
 +        elif is_legacy:
 +            return EdgeType.legacy
          else:
              raise ValueError('Unknown edge type between {0} and {1}'
 -                    .format(fromfile.path, tofile.path))
 +                    .format(fromfile.get_relpath(), tofile.get_relpath()))
 +
 +    def _create_file_edge(self, fromfile, tofile, filenodes):
 +        """Create edge between two file objects.
 +
 +        Determines the type for the edge from the information provided by
 +        gmxtree.
 +        """
 +        edgetype = self._get_file_edge_type(fromfile, tofile)
          return Edge(filenodes[fromfile], filenodes[tofile], edgetype)
  
      def _create_file_edges(self, filenodes):
                      edges.append(edge)
          return edges
  
 -    def _create_module_node(self, module, filenodes):
 -        """Create node for a module.
 -
 -        The created node will have all files in the module as its child nodes.
 -        All created file nodes are added to the filenodes dict.
 -        """
 +    def _get_module_color(self, modulegroup):
 +        if modulegroup == 'legacy':
 +            return 'fillcolor=grey75'
 +        elif modulegroup == 'analysismodules':
 +            return 'fillcolor="0 .2 1"'
 +        elif modulegroup == 'utilitymodules':
 +            return 'fillcolor=".08 .2 1"'
 +        elif modulegroup == 'mdrun':
 +            return 'fillcolor=".75 .2 1"'
 +        return None
 +
 +    def _create_module_node(self, module):
 +        """Create node for a module."""
          style = []
          properties = []
          properties.append('shape=ellipse')
          properties.append('URL="\\ref module_{0}"'.format(module.get_name()))
          if not module.is_documented():
 +            fillcolor = self._get_module_color('legacy')
 +        else:
 +            fillcolor = self._get_module_color(module.get_group())
 +        if fillcolor:
              style.append('filled')
 -            properties.append('fillcolor=grey75')
 -        elif module.get_group() == 'analysismodules':
 -            style.append('filled')
 -            properties.append('fillcolor="0 .2 1"')
 -        elif module.get_group() == 'utilitymodules':
 -            style.append('filled')
 -            properties.append('fillcolor=".08 .2 1"')
 -        elif module.get_group() == 'mdrun':
 -            style.append('filled')
 -            properties.append('fillcolor=".75 .2 1"')
 +            properties.append(fillcolor)
          rootdir = module.get_root_dir()
          if rootdir.has_installed_files():
              properties.append('color=".66 .5 1"')
          nodename = 'module_' + re.subn(r'[-./]', '_', rootdir.get_relpath())[0]
          label = module.get_name()[7:]
          node = Node(nodename, label, style, properties)
 -        for childfile in module.get_files():
 -            node.add_child(self._create_file_node(childfile, filenodes))
          return node
  
 +    def _create_module_edges(self, modulenodes):
 +        """Create edges between all module nodes.
 +
 +        Create edges between module nodes specified in modulenodes from all
 +        include dependencies.  An edge is created only if both ends of the
 +        dependency are in the list of nodes.
 +        """
 +        edges = []
 +        for moduleobj in modulenodes.iterkeys():
 +            for dep in moduleobj.get_dependencies():
 +                othermodule = dep.get_other_module()
 +                if othermodule and othermodule in modulenodes:
 +                    if dep.is_cycle_suppressed():
 +                        edgetype = EdgeType.cyclic
 +                    else:
 +                        edgetype = max([
 +                            self._get_file_edge_type(x.get_including_file(), x.get_file())
 +                            for x in dep.get_included_files()])
 +                    edge = Edge(modulenodes[moduleobj], modulenodes[othermodule], edgetype)
 +                    edges.append(edge)
 +        return edges
 +
 +
 +    def _create_legend_node(self, label, modulegroup):
 +        if modulegroup:
 +            nodename = 'legend_' + modulegroup
 +            fillcolor = self._get_module_color(modulegroup)
 +        else:
 +            nodename = 'legend_' + label
 +            fillcolor = None
 +        style = []
 +        properties = []
 +        if fillcolor:
 +            style.append('filled')
 +            properties.append(fillcolor)
 +        return Node(nodename, label, style, properties)
 +
      def create_modules_graph(self):
          """Create module dependency graph."""
 -        filenodes = dict()
          nodes = []
 -        modulenodes = []
 +        modulenodes = dict()
          libgromacsnode = Node('libgromacs', 'libgromacs')
          nodes.append(libgromacsnode)
          for moduleobj in self._tree.get_modules():
 -            node = self._create_module_node(moduleobj, filenodes)
 +            node = self._create_module_node(moduleobj)
              if moduleobj.get_root_dir().get_relpath().startswith('src/gromacs'):
                  libgromacsnode.add_child(node)
              else:
                  nodes.append(node)
 -            modulenodes.append(node)
 -        edges = self._create_file_edges(filenodes)
 +            modulenodes[moduleobj] = node
 +        edges = self._create_module_edges(modulenodes)
 +        # TODO: Consider adding invisible edges to order the nodes better.
 +        # TODO: Consider adding legend for the edge types as well.
 +        legendnode = Node('legend', 'legend')
 +        legendnode.add_child(self._create_legend_node('legacy', 'legacy'))
 +        legendnode.add_child(self._create_legend_node('analysis', 'analysismodules'))
 +        legendnode.add_child(self._create_legend_node('utility', 'utilitymodules'))
 +        legendnode.add_child(self._create_legend_node('mdrun', 'mdrun'))
 +        legendnode.add_child(Node('legend_installed', 'installed', properties=['color=".66 .5 1"', 'penwidth=3']))
 +        nodes.append(legendnode)
          graph = Graph(nodes, edges)
 -        for node in modulenodes:
 -            graph.collapse_node(node)
          graph.set_options(concentrate=False)
          return graph
  
@@@ -533,8 -479,6 +533,8 @@@ def main()
                        help='Build tree root directory')
      parser.add_option('--installed',
                        help='Read list of installed files from given file')
 +    parser.add_option('--ignore-cycles',
 +                      help='Set file with module dependencies to ignore in cycles')
      parser.add_option('-o', '--outdir', default='.',
                        help='Specify output directory for graphs')
      parser.add_option('-q', '--quiet', action='store_true',
      if not options.quiet:
          sys.stderr.write('Reading source files...\n')
      tree.scan_files()
 +    if options.ignore_cycles:
 +        tree.load_cycle_suppression_list(options.ignore_cycles)
      if not options.quiet:
          sys.stderr.write('Reading Doxygen XML files...\n')
      tree.load_xml(only_files=True)
diff --combined docs/doxygen/reporter.py
index 9be3cdae66df5993a88f5d36ec25b3abcd7d1940,e541c54235f84f2fbf5c419f8d2e3397f49e6328..9be3cdae66df5993a88f5d36ec25b3abcd7d1940
@@@ -240,10 -240,6 +240,10 @@@ class Reporter(object)
          self._report(Message('warning: ' + message, details,
              location=entity.get_reporter_location()))
  
 +    def cyclic_issue(self, message, details=None):
 +        """Report a cyclic dependency issue."""
 +        self._report(Message('warning: ' + message, details))
 +
      def doc_error(self, entity, message):
          """Report an issue in documentation."""
          self._report(Message('error: ' + entity.get_name() + ': ' + message,
index 6d4cde7532288334ee9ff30812776bd154f792bf,592505a9621365d18c49fe914f035d8ae78291a4..6d4cde7532288334ee9ff30812776bd154f792bf
@@@ -6,17 -6,16 +6,17 @@@ src/gromacs/gmxlib/gmx_cpuid.c: warning
  # The script is currently a bit too eager
  share/template/template.cpp: error: source file documentation appears outside full documentation
  
 +# These are OK
 +src/gromacs/math/vec.h: warning: installed header includes non-installed "config.h"
 +src/gromacs/linearalgebra/gmx_blas/*: warning: does not include "gmxpre.h" first
 +src/gromacs/linearalgebra/gmx_lapack/*: warning: does not include "gmxpre.h" first
 +
  # This module name doesn't really fall into any currently used pattern; needs some thought
  : error: no matching directory for module: module_mdrun_integration_tests
  
  # These would be nice to fix, but can wait for later
 -*: warning: includes local file as <config.h>
  src/gromacs/gmxlib/nonbonded/nb_kernel_*/*: warning: included file "gromacs/simd/math_x86_*.h" is not documented as exposed outside its module
  
  # These are specific to Folding@Home, and easiest to suppress here
  *: warning: includes non-local file as "corewrap.h"
 -src/config.h.cmakein: warning: includes non-local file as "swindirect.h"
 -
 -# These are limitations in the current script
 -src/gromacs/utility/gmx_header_config.h: warning: includes non-local file as "gmx_header_config_gen.h"
 +src/gmxpre.h: warning: includes non-local file as "swindirect.h"
index 8afcb715dfd441f245c40a85ba2ae9c54239e985,7419b5dcd4f9753c574a56979f67b893f7bac091..8afcb715dfd441f245c40a85ba2ae9c54239e985
@@@ -4,6 -4,9 +4,6 @@@ Using \Gromacs as a library {#page_usin
  Getting started
  ===============
  
 -\todo
 -Describe how to link against \Gromacs (pkg-config, FindGromacs.cmake, etc.)
 -
  The \Gromacs library (`libgromacs`) provides a few different alternatives for
  using it.  These are listed here from the highest level of abstraction to the
  low-level functions.
@@@ -48,142 -51,6 +48,142 @@@ think that it would be easy to expose, 
  `gmx-developers` mailing list, or contribute the necessary changes on
  http://gerrit.gromacs.org/.
  
 +Linking against `libgromacs`
 +============================
 +
 +\Gromacs is a bit picky on how the headers need to be used: depending on
 +compilation options used for \Gromacs, some preprocessor defines may need to be
 +set, the required include path may also depend on compilation options, and some
 +extra libraries may need to be linked.  You will also likely need to use the
 +same compiler (or sufficiently similar one that uses the same standard library)
 +that was used to compile \Gromacs.
 +
 +To manage this more easily, \Gromacs provides two mechanisms for getting the
 +correct flags for compilation and linking against the \Gromacs library:
 + - `pkg-config`: \Gromacs installs `libgromacs.pc` file (suffixed with the
 +   library suffix) for use with `pkg-config` if that is present on the system.
 +   Sourcing `GMXRC` adjusts the `pkg-config` search path such that these files
 +   are found automatically.
 +   See `Makefile.pkg` installed with the analysis template for one example of
 +   how to use it (to use it with a differently suffixed \Gromacs, just replace
 +   `libgromacs` with `libgromacs`<em>_suffix</em> in the `pkg-config` calls).
 + - CMake package configuration files and a find module that allow
 +   `find_package(GROMACS)` to work.  See below for details about how to use
 +   this in CMake.  Sourcing `GMXRC` sets an environment variable that allows
 +   CMake to find the configuration file automatically.
 +   See `CMakeLists.txt` installed with the analysis template for one example of
 +   how to use it.
 +
 +These mechanisms are currently provided on a best-effort basis, but are not
 +routinely tested on a wide range of configurations.  Please report any issues
 +with details of how \Gromacs was built so that the mechanism can be improved.
 +Known issues:
 + - `pkg-config` files are not relocatable, i.e., they hard-code the
 +   installation prefix as an absolute path.
 + - Installing both static and shared libraries with the same suffix to the same
 +   installation prefix is guaranteed to work only if both are built with
 +   exactly the same configuration options (except for `BUILD_SHARED_LIBS`) from
 +   exactly the same version.  There are several files that are shared between
 +   the installations in such a case, and the latter installation will overwrite
 +   those from the former.
 + - Further, if both static and shared libraries have been installed in the past
 +   to a prefix, then future installations to the same prefix should also
 +   install both static and shared libraries.  Otherwise, some obsolete CMake
 +   package configuration files will be left behind which can lead to finding
 +   the old library.  Alternatively, you can delete `share/cmake/` from the
 +   installation directory before doing the install.
 + - If a mechanism other than the CMake-generated `install` target is used to
 +   install \Gromacs over an existing installation, and the build type (e.g.,
 +   Release vs.\ Debug) does not match what was previously installed, some
 +   obsolete CMake import target definition files are left behind in
 +   `share/cmake/`, and may cause failures whey trying to use the package
 +   configuration files.
 + - If \Gromacs is built with `GMX_BUILD_OWN_FFTW=ON`, the CMake-generated
 +   import definitions for `libgromacs` reference a `gmxfftw` target that was
 +   used in the build to reference the `fftw` library.  As this library only
 +   exists in the \Gromacs build tree, and the CMake machinery does not write
 +   any import definitions for it anywhere, linking will fail with errors about
 +   not being able to find a `gmxfftw` library.  So the CMake package
 +   configuration files can only be used with `GMX_BUILD_OWN_FFTW=OFF`.
 +
 +CMake `find_package(GROMACS)` details
 +-------------------------------------
 +
 +The CMake machinery to support `find_package(GROMACS)` has two parts: a
 +`FindGROMACS.cmake` find module (found in `share/gromacs/template/cmake/` in
 +the installation and `share/template/cmake/` in the source tree), and actual
 +package configuration files (`gromacs-config.cmake` and supporting files
 +installed to `share/cmake/` from input files in `src/gromacs/`).
 +
 +`FindGROMACS.cmake` is a simple wrapper over the package configuration files,
 +providing a somewhat more convenient interface to the machinery that supports
 +multiple suffixed \Gromacs installations in the same installation prefix (see
 +`GROMACS_SUFFIX` variable below).  This file is intended to be version-agnostic
 +and remain both forward- and backward-compatible even between major \Gromacs
 +releases.  All version-specific information and the actual details about the
 +compilation and linking settings is in the package configuration files.
 +Build systems willing to utilize `FindGROMACS.cmake` can create a local copy of
 +it and use it like it is used in the installed
 +`share/gromacs/template/CMakeLists.txt`.
 +The package configuration files can also be used directly if desired, bypassing
 +`FindGROMACS.cmake`.
 +
 +Input options for influencing what to find:
 +
 +<dl>
 +<dt>`GROMACS_SUFFIX` (only for `FindGROMACS.cmake`)</dt>
 +<dd>This CMake variable can be set before calling `find_package(GROMACS)` to
 +specify the \Gromacs suffix to search for.  If not set, an unsuffixed version
 +is searched for.  If using the package configuration files directly, the suffix
 +must be set using `find_package(GROMACS NAMES gromacs<suffix>)`.</dd>
 +<dt>`GROMACS_PREFER_STATIC`</dt>
 +<dd>This CMake variable can be set before calling `find_package(GROMACS)` to
 +specify whether static or shared libraries are preferred if both are available.
 +It does not affect which \Gromacs installation is chosen, but if that
 +installation has both static and shared libraries available (installed from two
 +different builds with the same suffix), then this chooses the library to be
 +returned in `GROMACS_LIBRARIES`.</dd>
 +<dt>`GROMACS_DIR`</dt>
 +<dd>This CMake (cache) variable is a standard mechanism provided by
 +`find_package`, and can be used to specify a hint where to search for \Gromacs.
 +Also `CMAKE_PREFIX_PATH` can be used for this purpose; see CMake documentation
 +for `find_package` for more details.
 +`GROMACS_DIR` can also be set as an environment variable, and this is done by
 +`GMXRC`.</dd>
 +</dl>
 +
 +Output variables that specify how the found `libgromacs` and header should be
 +used:
 +
 +<dl>
 +<dt>`GROMACS_INCLUDE_DIRS`</dt>
 +<dd>List of include directories necessary to compile against the \Gromacs
 +headers.  Currently, this includes the path to \Gromacs headers, as well as the
 +path to Boost headers that were used to compile \Gromacs.</dd>
 +<dt>`GROMACS_LIBRARIES`</dt>
 +<dd>List of libraries to link with to link against \Gromacs.
 +Under the hood, this uses imported CMake targets to represent `libgromacs`.</dd>
 +<dt>`GROMACS_DEFINITIONS`</dt>
 +<dd>List of compile definitions (with `-D` in front) that are required to
 +compile the \Gromacs headers.</dd>
 +<dt>`GROMACS_IS_DOUBLE`</dt>
 +<dd>Whether the found \Gromacs was compiled in double precision.</dd>
 +</dl>
 +
 +Declared macros/functions that can be used for checking for correctness of some
 +settings:
 +
 +<dl>
 +<dt>`gromacs_check_double(GMX_DOUBLE)`</dt>
 +<dd>Checks that the found \Gromacs is in the expected precision.
 +The parameter `GMX_DOUBLE` should be the name of a cache variable that
 +specified whether double-precision was requested.</dd>
 +<dt>`gromacs_check_compiler(LANG)`<dt>
 +<dd>Checks that the found \Gromacs was compiled with the same compiler
 +that is used by the current CMake system.
 +Currently only `LANG=CXX` is supported.</dd>
 +</dl>
 +
  Notes on \Gromacs API
  =====================
  
index 5964f05f335d34a99eced0bd73cf52e6f5c20bee,9ab4db6b1095967e2d01bbd77527c1496098659d..1a973dad5d785a91848d4640d864add005c8a475
@@@ -1,12 -1,12 +1,12 @@@
  % Installation guide for GROMACS @PROJECT_VERSION@
  
- # Building GROMACS #
+ # Introduction to building GROMACS #
  
  These instructions pertain to building GROMACS
  @PROJECT_VERSION@. Up-to-date installation instructions may be found
  at <http://www.gromacs.org/Documentation/Installation_Instructions>.
  
- # Quick and dirty installation #
+ ## Quick and dirty installation ##
  
  1. Get the latest version of your C and C++ compilers.
  2. Check that you have CMake version @GMX_CMAKE_MINIMUM_REQUIRED_VERSION@ or later.
@@@ -36,7 -36,7 +36,7 @@@ GROMACS, you will have to read further
  hardware, libraries, and compilers are only going to continue to get
  more complex.
  
- # Typical GROMACS installation #
+ ## Typical GROMACS installation ##
  
  As above, and with further details below, but you should consider
  using the following [CMake options](#using-cmake-command-line-options) with the
@@@ -55,7 -55,7 +55,7 @@@ appropriate value instead of `xxx` 
  * `-DGMX_FFT_LIBRARY=xxx` to select whether to use `fftw`, `mkl` or `fftpack` libraries for [FFT support](#fast-fourier-transform-library)
  * `-DCMAKE_BUILD_TYPE=Debug` to build GROMACS in debug mode
  
- # Building older GROMACS versions #
+ ## Building older GROMACS versions ##
  
  For installation instructions for old GROMACS versions, see the
  documentation at
@@@ -200,7 -200,7 +200,7 @@@ Many simulations in GROMACS make extens
  transforms, and a software library to perform these is always
  required. We recommend [FFTW](http://www.fftw.org) (version 3 or
  higher only) or
- [Intel MKL](http://software.intel.com/en-us/intel-mkl). The choice of
+ [Intel MKL](https://software.intel.com/en-us/intel-mkl). The choice of
  library can be set with `cmake -DGMX_FFT_LIBRARY=<name>`, where
  `<name>` is one of `fftw`, `mkl`, or `fftpack`. FFTPACK is bundled
  with GROMACS as a fallback, and is acceptable if mdrun performance is
@@@ -650,13 -650,6 +650,13 @@@ CMakeLists.txt
      is determined by CMake.
      The name of the directory can be changed using `GMX_LIB_INSTALL_DIR` CMake
      variable.
 +`lib/pkgconfig/`
 +  : Information about the installed `libgromacs` library for `pkg-config` is
 +    installed here.  The `lib/` part adapts to the installation location of the
 +    libraries.  The installed files contain the installation prefix as absolute
 +    paths.
 +`share/cmake/`
 +  : CMake package configuration files are installed here.
  `share/gromacs/`
    : Various data files and some documentation go here.
      The `gromacs` part can be changed using `GMX_DATA_INSTALL_DIR`. Using this
  `share/man/`
    : Installed man pages go here.
  
- ## Building GROMACS ##
+ ## Compiling and linking ##
  
- Once you have configured with `cmake`, you can build GROMACS. It is
- expected that the `make` procedure will always complete successfully,
and give few or no warnings. The tests GROMACS makes on the settings
+ Once you have configured with `cmake`, you can build GROMACS with `make`.
+ It is expected that this will always complete successfully, and
give few or no warnings. The CMake-time tests GROMACS makes on the settings
  you choose are pretty extensive, but there are probably a few cases we
  have not thought of yet. Search the web first for solutions to
  problems, but if you need help, ask on gmx-users, being sure to
@@@ -682,7 -675,9 +682,9 @@@ message
  
  If you have a multi-core or multi-CPU machine with `N`
  processors, then using
      $ make -j N
  will generally speed things up by quite a bit. Other build generator systems
  supported by `cmake` (e.g. `ninja`) also work well.
  
diff --combined docs/man/CMakeLists.txt
index 372c797a4c6561332af02d7890b3fd303ea1e42f,ffcff945a74d5fc21ffbb1157f794befb4452cfd..372c797a4c6561332af02d7890b3fd303ea1e42f
  # To help us fund GROMACS development, we humbly ask that you cite
  # the research papers on the package. Check out http://www.gromacs.org.
  
 -if(SOURCE_IS_SOURCE_DISTRIBUTION)
 -    # Make sure source package contains all man pages.
 -    if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/man1/gmx-view.1")
 -        message(FATAL_ERROR "Man pages are missing from source package.")
 -    endif()
 -endif()
 -
  add_custom_target(man
      ${CMAKE_COMMAND} -E make_directory man1
      COMMAND ${CMAKE_COMMAND} -E make_directory man7
diff --combined docs/manual/README
index 38a07a50e016a1dd7102902da7d0bab36c31ad95,74bc93d515ae4378afafe80221c0983c5bae1440..38a07a50e016a1dd7102902da7d0bab36c31ad95
@@@ -1,7 -1,7 +1,7 @@@
  So, you want to build the manual? This is now easy.
  
  1. Configure the main GROMACS repo with GMX_BUILD_MANUAL=on
 -2. cmake will run some detection about whetehr this is possible
 +2. cmake will run some detection about whether this is possible
  3. Later, use "make manual," which will build GROMACS in the
     usual way, and use the generated binaries to build the manual.
  
diff --combined docs/manual/macros.tex
index ed4b53ce251289f9e3f8bbf059b64021464c12f7,ed8442fbb3b586ac81a1d5005e2c4cddd750b920..ed4b53ce251289f9e3f8bbf059b64021464c12f7
  \newcommand{\ttw}{4.33cm}  % 1/3 of the textwidth
  \newcommand{\htw}{7.5cm}  % 1/2 of the textwidth
  \newcommand{\ntw}{13cm} % 0.9 of the textwidth
 -\newcommand{\gmxver}{@PROJECT_VERSION@}
 +\newcommand{\gmxver}{@GMX_VERSION_STRING@}
  \newcommand{\gmxyear}{\the\year}
  \newcommand{\figref}[1]{Fig.~\ref{fig:#1}}
  \newcommand{\figsref}[2]{Figs.~\ref{fig:#1} and ~\ref{fig:#2}}
diff --combined docs/manual/monster.bib
index 0774ae539cbb954bcf42bb600075a03f676570b2,3c14898f4be4dc2ef87ab17ccc77ca0cc7b98dcc..0774ae539cbb954bcf42bb600075a03f676570b2
@@@ -7398,7 -7398,7 +7398,7 @@@ rov and V. S. Ananthanarayanan}
  @Article{PSmith93c,
    author =     {P. E. Smith and W. F. van Gunsteren},
    title =      {{The Viscosity of SPC and SPC/E Water}},
 -  journal =    BTcpc,
 +  journal =    BTcpl,
    year =       1993,
    volume =     215,
    pages =      {315--318}
index 176901b7ed0189ac6bb9688c3e2bb266e9c38857,861151879c922bfdb290c138c33c6dbe3f7cfbbf..176901b7ed0189ac6bb9688c3e2bb266e9c38857
  set(OUTPUT_DIR final)
  set(HTML_PAGE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${OUTPUT_DIR})
  
 -if(SOURCE_IS_SOURCE_DISTRIBUTION)
 -    # Make sure source package contains HTML pages.
 -    if(NOT EXISTS "${HTML_PAGE_DIR}/online.html")
 -        message(FATAL_ERROR "Online HTML pages are missing from source package.")
 -    endif()
 -endif()
 -
  add_custom_target(html
      ${CMAKE_COMMAND}
      -D SOURCE_HTML_DIR=${CMAKE_CURRENT_SOURCE_DIR}
index 0da72898c9dc76348c36e61d3ff7f5f96c1d28bc,c765548ae1e9bc708815fe9f6c77a3059c87adb3..e9de38158b74379428f01b60aa057ce6652555c2
@@@ -71,6 -71,7 +71,7 @@@ if(GMX_USE_TNG
  
          if (HAVE_ZLIB)
              list(APPEND GMX_EXTRA_LIBRARIES ${ZLIB_LIBRARIES})
+             include_directories(${ZLIB_INCLUDE_DIRS})
          endif()
      endif()
  else()
@@@ -88,9 -89,7 +89,9 @@@ add_subdirectory(math
  add_subdirectory(random)
  add_subdirectory(onlinehelp)
  add_subdirectory(options)
 +add_subdirectory(pbcutil)
  add_subdirectory(timing)
 +add_subdirectory(topology)
  add_subdirectory(utility)
  add_subdirectory(fileio)
  add_subdirectory(swap)
@@@ -120,26 -119,37 +121,26 @@@ list(APPEND LIBGROMACS_SOURCES ${GMXLIB
  tmpi_get_source_list(THREAD_MPI_SOURCES ${CMAKE_SOURCE_DIR}/src/external/thread_mpi/src)
  list(APPEND LIBGROMACS_SOURCES ${THREAD_MPI_SOURCES})
  
 -file(GLOB LIBGROMACS_HEADERS *.h)
 +set(LIBGROMACS_HEADERS
 +    analysisdata.h
 +    commandline.h
 +    options.h
 +    selection.h
 +    trajectoryanalysis.h
 +    utility.h)
  configure_file(version.h.cmakein version.h)
  gmx_install_headers("" ${LIBGROMACS_HEADERS})
  gmx_install_headers("" ${CMAKE_CURRENT_BINARY_DIR}/version.h)
  
 -# Add target that generates baseversion-gen.c every time make is run
 -# if git version info is requested, or create it statically.
 -# This code is here instead of utility/CMakeLists.txt because CMake
 -# ignores set_source_file_properties from subdirectories.
 -set(GENERATED_VERSION_FILE
 -    ${CMAKE_CURRENT_BINARY_DIR}/utility/baseversion-gen.c)
 -set(GENERATED_VERSION_FILE_SOURCE
 -    ${CMAKE_CURRENT_SOURCE_DIR}/utility/baseversion-gen.c.cmakein)
 -if (GMX_GIT_VERSION_INFO)
 -    add_custom_target(gmx-version ALL
 -            COMMAND ${CMAKE_COMMAND}
 -                -D GIT_EXECUTABLE="${GIT_EXECUTABLE}"
 -                -D PROJECT_VERSION="${PROJECT_VERSION}"
 -                -D PROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}"
 -                -D VERSION_CMAKEIN=${GENERATED_VERSION_FILE_SOURCE}
 -                -D VERSION_OUT=${GENERATED_VERSION_FILE}
 -                -P ${CMAKE_SOURCE_DIR}/cmake/gmxGenerateVersionInfo.cmake
 -            WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
 -            DEPENDS ${GENERATED_VERSION_FILE_SOURCE}
 -            COMMENT "Generating git version information")
 -    set_source_files_properties(${GENERATED_VERSION_FILE}
 -                                PROPERTIES GENERATED true)
 -else()
 -    set(GMX_PROJECT_VERSION_STR ${PROJECT_VERSION})
 -    configure_file(${GENERATED_VERSION_FILE_SOURCE} ${GENERATED_VERSION_FILE})
 -endif()
 +# This code is here instead of utility/CMakeLists.txt, because CMake
 +# custom commands and source file properties can only be set in the directory
 +# that contains the target that uses them.
 +# TODO: Generate a header instead that can be included from baseversion.c.
 +# That probably simplifies things somewhat.
 +set(GENERATED_VERSION_FILE utility/baseversion-gen.c)
 +gmx_configure_version_file(
 +    utility/baseversion-gen.c.cmakein ${GENERATED_VERSION_FILE}
 +    REMOTE_HASH SOURCE_FILE)
  list(APPEND LIBGROMACS_SOURCES ${GENERATED_VERSION_FILE})
  
  # apply gcc 4.4.x bug workaround
@@@ -150,13 -160,9 +151,13 @@@ if(GMX_USE_GCC44_BUG_WORKAROUND
     gmx_apply_gcc44_bug_workaround("mdlib/constr.c")
  endif()
  
 -add_library(libgromacs ${LIBGROMACS_SOURCES})
 -if (GMX_GIT_VERSION_INFO)
 -    add_dependencies(libgromacs gmx-version)
 +if (GMX_GPU)
 +    cuda_add_library(libgromacs ${LIBGROMACS_SOURCES}
 +            OPTIONS
 +            RELWITHDEBINFO -g
 +            DEBUG -g -D_DEBUG_=1)
 +else()
 +    add_library(libgromacs ${LIBGROMACS_SOURCES})
  endif()
  
  # Recent versions of gcc and clang give warnings on scanner.cpp, which
@@@ -183,6 -189,7 +184,6 @@@ set_source_files_properties(selection/s
  
  target_link_libraries(libgromacs
                        ${EXTRAE_LIBRARIES}
 -                      ${GMX_GPU_LIBRARIES}
                        ${GMX_EXTRA_LIBRARIES}
                        ${TNG_IO_LIBRARIES}
                        ${FFT_LIBRARIES} ${LINEAR_ALGEBRA_LIBRARIES}
@@@ -197,14 -204,16 +198,14 @@@ set_target_properties(libgromacs PROPER
  # Only install the library in mdrun-only mode if it is actually necessary
  # for the binary
  if (NOT GMX_BUILD_MDRUN_ONLY OR BUILD_SHARED_LIBS)
 -    install(TARGETS libgromacs DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)
 +    install(TARGETS libgromacs
 +            EXPORT libgromacs
 +            DESTINATION ${LIB_INSTALL_DIR}
 +            COMPONENT libraries)
  endif()
  
  if (NOT GMX_BUILD_MDRUN_ONLY)
 -    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgromacs.pc.cmakein
 -                   ${CMAKE_CURRENT_BINARY_DIR}/libgromacs.pc @ONLY)
 -    install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgromacs.pc
 -            DESTINATION ${LIB_INSTALL_DIR}/pkgconfig
 -            RENAME "libgromacs${GMX_LIBS_SUFFIX}.pc"
 -            COMPONENT development)
 +    include(InstallLibInfo.cmake)
  endif()
  
  if (INSTALL_CUDART_LIB) #can be set manual by user
index 6b2d983df53940c994c694773871b27859f92d8a,b6878e6bd12107d9bb4faf3e298b2f8c4853a758..ca4c898e6827d60791af7f8eece2afb551ddaf28
   * To help us fund GROMACS development, we humbly ask that you cite
   * the research papers on the package. Check out http://www.gromacs.org.
   */
 -#ifdef HAVE_CONFIG_H
 -#include <config.h>
 -#endif
 +#include "gmxpre.h"
 +
 +#include "config.h"
  
  #include <assert.h>
 +#include <stdlib.h>
  
  #include "gromacs/fileio/confio.h"
 -#include "types/commrec.h"
 -#include "constr.h"
 -#include "copyrite.h"
 -#include "invblock.h"
 -#include "main.h"
 -#include "mdrun.h"
 -#include "nrnb.h"
 -#include "gromacs/utility/smalloc.h"
 -#include "vec.h"
 -#include "physics.h"
 -#include "names.h"
 -#include "txtdump.h"
 -#include "domdec.h"
 +#include "gromacs/legacyheaders/types/commrec.h"
 +#include "gromacs/legacyheaders/constr.h"
 +#include "gromacs/legacyheaders/copyrite.h"
 +#include "gromacs/legacyheaders/mdrun.h"
 +#include "gromacs/legacyheaders/nrnb.h"
 +#include "gromacs/math/vec.h"
 +#include "gromacs/legacyheaders/names.h"
 +#include "gromacs/legacyheaders/txtdump.h"
 +#include "gromacs/legacyheaders/domdec.h"
  #include "gromacs/fileio/pdbio.h"
 -#include "splitter.h"
 -#include "mtop_util.h"
 +#include "gromacs/legacyheaders/splitter.h"
 +#include "gromacs/topology/mtop_util.h"
  #include "gromacs/fileio/gmxfio.h"
 -#include "macros.h"
 -#include "gmx_omp_nthreads.h"
 +#include "gromacs/legacyheaders/macros.h"
 +#include "gromacs/legacyheaders/gmx_omp_nthreads.h"
  #include "gromacs/essentialdynamics/edsam.h"
  #include "gromacs/pulling/pull.h"
  
 -#include "gmx_fatal.h"
 +#include "gromacs/pbcutil/pbc.h"
 +#include "gromacs/topology/block.h"
 +#include "gromacs/topology/invblock.h"
 +#include "gromacs/utility/fatalerror.h"
 +#include "gromacs/utility/smalloc.h"
  
  typedef struct gmx_constr {
      int                ncon_tot;       /* The total number of constraints    */
@@@ -204,6 -203,18 +204,18 @@@ int n_flexible_constraints(struct gmx_c
      return nflexcon;
  }
  
+ static void clear_constraint_quantity_nonlocal(gmx_domdec_t *dd, rvec *q)
+ {
+     int nonlocal_at_start, nonlocal_at_end, at;
+     dd_get_constraint_range(dd, &nonlocal_at_start, &nonlocal_at_end);
+     for (at = nonlocal_at_start; at < nonlocal_at_end; at++)
+     {
+         clear_rvec(q[at]);
+     }
+ }
  void too_many_constraint_warnings(int eConstrAlg, int warncount)
  {
      const char *abort = "- aborting to avoid logfile runaway.\n"
@@@ -432,6 -443,15 +444,15 @@@ gmx_bool constrain(FILE *fplog, gmx_boo
      if (cr->dd)
      {
          dd_move_x_constraints(cr->dd, box, x, xprime, econq == econqCoord);
+         if (v != NULL)
+         {
+             /* We need to initialize the non-local components of v.
+              * We never actually use these values, but we do increment them,
+              * so we should avoid uninitialized variables and overflows.
+              */
+             clear_constraint_quantity_nonlocal(cr->dd, v);
+         }
      }
  
      if (constr->lincsd != NULL)
@@@ -810,7 -830,7 +831,7 @@@ static void make_shake_sblock_serial(st
                  j, constr->nblocks, ncons);
          for (i = 0; (i < ncons); i++)
          {
 -            fprintf(stderr, "i: %5d  sb[i].blocknr: %5u\n", i, sb[i].blocknr);
 +            fprintf(stderr, "i: %5d  sb[i].blocknr: %5d\n", i, sb[i].blocknr);
          }
          for (j = 0; (j <= constr->nblocks); j++)
          {