Skip to content
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
73166fe
Added support for older versions of GROMACS to EB
Feb 2, 2015
c777e6e
Updates to support multiple configurations for MPI, etc.
Feb 3, 2015
4a68728
Put things in GROMACS easyblock to deal with LAPACK libraries, etc.
Mar 5, 2015
37f0780
Finished GROMACS easyblock to support version 3.3.3
Mar 5, 2015
7fd1809
Merge in from develop branch
Mar 31, 2015
768f63f
GROMACS EasyBlock now does a separate MPI build to avoid screwing up …
Mar 31, 2015
c551d18
An attempted merge of the new changes into an easyblock that supports…
Sep 24, 2015
98c5382
Merge branch 'develop' into GROMACS-3.3.3
Sep 30, 2015
f89bd06
Merge branch 'develop' into GROMACS-3.3.3
Oct 28, 2015
0384b27
Merge branch 'develop' into GROMACS-3.3.3
Dec 10, 2015
861d5e7
Merge branch 'develop' into GROMACS-3.3.3
Dec 14, 2015
c8f6e73
Merge branch 'develop' into GROMACS-3.3.3
Jan 5, 2016
5aaa95c
Merge branch 'develop' into GROMACS-3.3.3
Jan 13, 2016
1f86517
Merge branch 'develop' into GROMACS-3.3.3
Jan 24, 2016
a1ade2e
Merge branch 'develop' into GROMACS-3.3.3
Jan 27, 2016
31937db
Merge branch 'develop' into GROMACS-3.3.3
Feb 14, 2016
74339eb
Merge branch 'develop' into GROMACS-3.3.3
Feb 17, 2016
5c679ea
Merge branch 'develop' into GROMACS-3.3.3
Feb 21, 2016
b5728f7
Merge branch 'develop' into GROMACS-3.3.3
Feb 24, 2016
bf9eb3e
Merge branch 'develop' into GROMACS-3.3.3
Mar 1, 2016
ef43372
Merge branch 'develop' into GROMACS-3.3.3
Mar 2, 2016
2957ca1
Merge branch 'develop' into GROMACS-3.3.3
Mar 15, 2016
68733ca
Merge branch 'develop' into GROMACS-3.3.3
Mar 29, 2016
885013f
Merge branch 'develop' into GROMACS-3.3.3
Apr 5, 2016
83cff89
Merge branch 'develop' into GROMACS-3.3.3
Apr 13, 2016
265c57f
Merge branch 'develop' into GROMACS-3.3.3
Apr 18, 2016
23f0a52
Placeholders for updating BLAS and LAPACK lines
Apr 21, 2016
ee3ca77
Interim modifications to GROMACS EasyBlock
Apr 28, 2016
e9844c6
Merge branch 'develop' into GROMACS-3.3.3
Apr 28, 2016
be58871
Improve GROMACS build process with non-Intel MKL for GROMACS 5.1.2 etc
Apr 29, 2016
37a192c
Fix sanity check section
May 10, 2016
47c1f16
Replace self.log.error with EasyBuildError
May 10, 2016
bf53051
Merge branch 'develop' into GROMACS-3.3.3
May 10, 2016
9b6ec60
Add support for custom MPI executable
May 10, 2016
a3448b5
Supplementary information printed in logs
May 11, 2016
6141b7c
Further changes for GROMACS 5.1
May 13, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 172 additions & 53 deletions easybuild/easyblocks/g/gromacs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,73 @@
from vsc.utils.missing import any

import easybuild.tools.environment as env
from easybuild.easyblocks.generic.configuremake import ConfigureMake
from easybuild.easyblocks.generic.cmakemake import CMakeMake
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.filetools import download_file, extract_file
from easybuild.tools.modules import get_software_root
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import get_platform_name


class EB_GROMACS(CMakeMake):
"""Support for building/installing GROMACS."""

@staticmethod
def extra_options():
extra_vars = {
'mpisuffix': ['_mpi', "Suffix to append to MPI-enabled executables", CUSTOM],
'mpiexec': ['mpirun', "MPI executable to use when running tests", CUSTOM],
'mpiexec_numproc_flag': ['-np', "Flag to introduce the number of MPI tasks when running tests", CUSTOM],
'mpi_numprocs': [0, "Number of MPI tasks to use when running tests", CUSTOM],
}
return ConfigureMake.extra_options(extra_vars)

def __init__(self, *args, **kwargs):
"""Initialize GROMACS-specific variables."""
super(EB_GROMACS, self).__init__(*args, **kwargs)
self.lib_subdir = ''
self.cmake_objdir_normal = ''
self.cmake_objdir_mdrun = ''

def configure_step(self):
"""Custom configuration procedure for GROMACS: set configure options for configure or cmake."""

if LooseVersion(self.version) < LooseVersion('4.6'):
self.log.info("Using configure script for configuring GROMACS build.")
raise EasyBuildError("Configuration procedure for older GROMACS versions not implemented yet.")
# Use static libraries if possible
self.cfg.update('configopts', "--enable-static")

# Use external BLAS and LAPACK
self.cfg.update('configopts', "--with-external-blas --with-external-lapack")
self.cfg.update("preconfigopts", 'LIBS="${EBVARLIBLAPACK} ${LIBS}"')

# Don't use the X window system
self.cfg.update('configopts', "--without-x")

# OpenMP is not supported for versions older than 4.5.
if LooseVersion(self.version) >= LooseVersion('4.5'):
# enable OpenMP support if desired
if self.toolchain.options.get('openmp', None):
self.cfg.update('configopts', "--enable-threads")
else:
self.cfg.update('configopts', "--disable-threads")
elif self.toolchain.options.get('openmp', None):
raise EasyBuildError("GROMACS version {0} does not support OpenMP.".format(LooseVersion(self.version)))

# GSL support
if get_software_root('GSL'):
self.cfg.update('configopts', "--with-gsl")
else:
self.cfg.update('configopts', "--without-gsl")

# I don't think it's necessary to explicitly set the location
# of math libraries (MKL and/or BLAS, LAPACK) but we shall see.

# Because ConfigureMake is (currently) an ancestral class of
# CMakeMake, we may not need to specify it as an ancestral class
# of gromacs.py.
ConfigureMake.configure_step(self)
else:
# build a release build
self.cfg.update('configopts', "-DCMAKE_BUILD_TYPE=Release")
Expand All @@ -68,19 +115,22 @@ def configure_step(self):
# disable GUI tools
self.cfg.update('configopts', "-DGMX_X11=OFF")

# set regression test path
prefix = 'regressiontests'
if any([src['name'].startswith(prefix) for src in self.src]):
major_minor_version = '.'.join(self.version.split('.')[:2])
self.cfg.update('configopts', "-DREGRESSIONTEST_PATH='%%(builddir)s/%s-%%(version)s' " % prefix)

# enable OpenMP support if desired
if self.toolchain.options.get('openmp', None):
self.cfg.update('configopts', "-DGMX_OPENMP=ON")
else:
self.cfg.update('configopts', "-DGMX_OPENMP=OFF")

# enable MPI support if desired
if self.toolchain.options.get('usempi', None):
self.cfg.update('configopts', "-DGMX_MPI=ON -DGMX_THREAD_MPI=OFF")
else:
self.cfg.update('configopts', "-DGMX_MPI=OFF")
# Disable MPI support (for initial, serial/SMP build)
self.cfg.update('configopts', "-DGMX_MPI=OFF")

# explicitely disable GPU support if CUDA is not available,
# explicitly disable GPU support if CUDA is not available,
# to avoid that GROMACS find and uses a system-wide CUDA compiler
cuda = get_software_root('CUDA')
if cuda:
Expand All @@ -94,53 +144,76 @@ def configure_step(self):
mkl_libs = [os.path.join(os.getenv('LAPACK_LIB_DIR'), lib) for lib in ['libmkl_lapack.a']]
self.cfg.update('configopts', '-DMKL_LIBRARIES="%s" ' % ';'.join(mkl_libs))
else:
# This may not work in all versions of GROMACS post 4.6.
for libname in ['BLAS', 'LAPACK']:
lib_dir = os.getenv('%s_LIB_DIR' % libname)
libs = os.getenv('LIB%s' % libname)
self.cfg.update('configopts', '-DGMX_%s_USER="-L%s %s"' % (libname, lib_dir, libs))

# set regression test path
prefix = 'regressiontests'
if any([src['name'].startswith(prefix) for src in self.src]):
self.cfg.update('configopts', "-DREGRESSIONTEST_PATH='%%(builddir)s/%s-%%(version)s' " % prefix)

# no more GSL support in GROMACS 5.x, see http://redmine.gromacs.org/issues/1472
if LooseVersion(self.version) < LooseVersion('5.0'):
# enable GSL when it's provided
if get_software_root('GSL'):
self.cfg.update('configopts', "-DGMX_GSL=ON")
else:
self.cfg.update('configopts', "-DGMX_GSL=OFF")

# complete configuration with configure_method of parent
out = super(EB_GROMACS, self).configure_step()

# for recent GROMACS versions, make very sure that a decent BLAS, LAPACK and FFT is found and used
if LooseVersion(self.version) >= LooseVersion('4.6.5'):
patterns = [
r"Using external FFT library - \S*",
r"Looking for dgemm_ - found",
r"Looking for cheev_ - found",
]
for pattern in patterns:
regex = re.compile(pattern, re.M)
if not regex.search(out):
raise EasyBuildError("Pattern '%s' not found in GROMACS configuration output.", pattern)
libfile = "lib" + libs.split('-l')[1].rstrip() + ".so"
libstr = os.path.join(lib_dir, libfile)
self.cfg.update('configopts', '-DGMX_%s_USER="%s"' % (libname, libstr))

# no more GSL support in GROMACS 5.x, see http://redmine.gromacs.org/issues/1472
if LooseVersion(self.version) < LooseVersion('5.0'):
# enable GSL when it's provided
if get_software_root('GSL'):
self.cfg.update('configopts', "-DGMX_GSL=ON")
else:
self.cfg.update('configopts', "-DGMX_GSL=OFF")

# complete configuration with configure_method of parent
self.cmake_objdir_normal = 'build-normal'
os.mkdir(self.cmake_objdir_normal)
os.chdir(self.cmake_objdir_normal)
out = super(EB_GROMACS, self).configure_step(srcdir='..')

# for recent GROMACS versions, make very sure that a decent BLAS, LAPACK and FFT is found and used
if LooseVersion(self.version) >= LooseVersion('4.6.5'):
patterns = [
r"Using external FFT library - \S*",
r"Looking for dgemm_ - found",
r"Looking for cheev_ - found",
]
for pattern in patterns:
regex = re.compile(pattern, re.M)
if not regex.search(out):
raise EasyBuildError("Pattern '%s' not found in GROMACS configuration output.", pattern)

os.chdir('..')

def build_step(self):

if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir(self.cmake_objdir_normal)
else:
self.cfg.update("prebuildopts", 'LIBS="${EBVARLIBLAPACK} ${LIBS}"')
super(EB_GROMACS, self).build_step()
if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir('..')

def test_step(self):
"""Specify to running tests is done using 'make check'."""
"""Run the basic tests (but not necessarily the full regression tests)
using make check"""
# allow to escape testing by setting runtest to False
if not self.cfg['runtest'] and not isinstance(self.cfg['runtest'], bool):
self.cfg['runtest'] = 'check'

# make very sure OMP_NUM_THREADS is set to 1, to avoid hanging GROMACS regression test
env.setvar('OMP_NUM_THREADS', '1')

super(EB_GROMACS, self).test_step()
# make very sure OMP_NUM_THREADS is set to 1, to avoid hanging GROMACS regression test
env.setvar('OMP_NUM_THREADS', '1')
if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir(self.cmake_objdir_normal)
else:
self.cfg['runtest'] = 'LIBS="${EBVARLIBLAPACK} ${LIBS}" check'
super(EB_GROMACS, self).test_step()
if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir('..')

def install_step(self):
"""Custom install step for GROMACS; figure out where libraries were installed to."""
"""Custom install step for GROMACS; figure out where libraries were installed to.
Also, install the MPI version of the executable in a separate step."""
if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir(self.cmake_objdir_normal)
super(EB_GROMACS, self).install_step()
if LooseVersion(self.version) >= LooseVersion('4.6'):
os.chdir('..')

# the GROMACS libraries get installed in different locations (deeper subdirectory), depending on the platform;
# this is determined by the GNUInstallDirs CMake module;
Expand All @@ -161,10 +234,41 @@ def install_step(self):
self.lib_subdir = os.path.dirname(libpaths[0])[len(self.installdir)+1:]
self.log.info("Found lib subdirectory that contains %s: %s", libname, self.lib_subdir)
break

if not self.lib_subdir:
raise EasyBuildError("Failed to determine lib subdirectory in %s", self.installdir)

# Install a version with the MPI suffix
if self.toolchain.options.get('usempi', None):
if LooseVersion(self.version) < LooseVersion('4.6'):
cmd = "make distclean"
(out, _) = run_cmd(cmd, log_all=True, simple=False)
self.cfg.update('configopts', "--enable-mpi --program-suffix={0}".format(self.cfg['mpisuffix']))
ConfigureMake.configure_step(self)
self.cfg.update("buildopts", "mdrun")
super(EB_GROMACS, self).build_step()
cmd = "%s make install-mdrun %s" % (self.cfg['preinstallopts'], self.cfg['installopts'])
(out, _) = run_cmd(cmd, log_all=True, simple=False)
else:
cmake_objdir = "build-mdrun-only"
os.mkdir(cmake_objdir)
os.chdir(cmake_objdir)
self.cfg['configopts'] = re.sub(r'-DGMX_MPI=OFF', r'', self.cfg['configopts'])
if self.cfg['mpi_numprocs'] == 0:
self.log.info("No specific number of test MPI tasks requested -- using parallelism ({0})".format(self.cfg['parallel']))
self.cfg['mpi_numprocs'] = self.cfg['parallel']
elif self.cfg['mpi_numprocs'] > self.cfg['parallel']:
self.log.warning("Number of test MPI tasks ({0}) is greater than parallelism ({1})".format(self.cfg['mpi_numprocs'], self.cfg['parallel']))
self.cfg.update('configopts', "-DGMX_MPI=ON -DGMX_THREAD_MPI=OFF -DMPIEXEC={0} -DMPIEXEC_NUMPROC_FLAG={1} -DNUMPROC={2} -DGMX_BUILD_MDRUN_ONLY=ON".format(self.cfg['mpiexec'], self.cfg['mpiexec_numproc_flag'], self.cfg['mpi_numprocs']))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@valtandor why did you include -DGMX_BUILD_MDRUN_ONLY=ON here (and also add mdrun to buildopts above for older GROMACS versions)?

I had to get rid of this in #960 because I was resulting in an incomplete installation w.r.t. MPI, e.g. libgromacs_mpi.a was missing, etc.

It seems fine to just drop this, since the MPI-able binaries & libraries are suffixed with _mpi, so nothing is overwritten?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@boegel Well, that's silly. I thought there was some reason, but I guess if it works without that option and doesn't overwrite anything serial, then there's no argument from me about not having the option.

self.log.info("Using {0} as MPI executable when testing, with numprocs flag \"{1}\" and {2} tasks".format(self.cfg['mpiexec'], self.cfg['mpiexec_numproc_flag'], self.cfg['mpi_numprocs']))

# Rebuild with MPI options
super(EB_GROMACS, self).configure_step(srcdir='..')
super(EB_GROMACS, self).build_step()
super(EB_GROMACS, self).install_step()
os.chdir('..')

self.log.info("A full regression test suite is available from the GROMACS web site")

def make_module_req_guess(self):
"""Custom library subdirectories for GROMACS."""
guesses = super(EB_GROMACS, self).make_module_req_guess()
Expand All @@ -182,25 +286,40 @@ def sanity_check_step(self):
if self.toolchain.options.get('usempi', None):
suff = '_mpi'

dirs = [os.path.join('include', 'gromacs')]

# in GROMACS v5.1, only 'gmx' binary is there
# (only) in GROMACS v5.0, other binaries are symlinks to 'gmx'
binaries = []
libnames = []
if LooseVersion(self.version) < LooseVersion('5.1'):
binaries.extend(['editconf', 'g_lie', 'genbox', 'genconf', 'mdrun'])

if LooseVersion(self.version) >= LooseVersion('5.0'):
binaries.append('gmx')

# check for a handful of binaries/libraries that should be there
if LooseVersion(self.version) < LooseVersion('5.0'):
libnames = ['gmxana', 'gmx', 'gmxpreprocess', 'md']
libnames.append('gromacs')
if self.toolchain.options.get('usempi', None):
binaries.append('mdrun{0}'.format(suff))
else:
libnames = ['gromacs']
libnames.extend(['gmxana', 'gmx', 'md'])
# I don't know when the gmxpreprocess library was introduced.
# This LooseVersion number may have to be tweaked.
if LooseVersion(self.version) > LooseVersion('3.3.3'):
libnames.append('gmxpreprocess')
if self.toolchain.options.get('usempi', None):
libnames.extend(['{0}{1}'.format(libname, suff) for libname in libnames])

libs = ['lib%s%s.a' % (libname, suff) for libname in libnames]
libs = ['lib%s.a' % libname for libname in libnames]

# I don't know when the pkgconfig directory was introduced.
# This LooseVersion number may have to be tweaked.
if LooseVersion(self.version) > LooseVersion('3.3.3'):
dirs.append(os.path.join(self.lib_subdir, "pkgconfig"))

custom_paths = {
'files': ['bin/%s%s' % (binary, suff) for binary in binaries] +
'files': ['bin/%s' % binary for binary in binaries] +
[os.path.join(self.lib_subdir, lib) for lib in libs],
'dirs': ['include/gromacs', os.path.join(self.lib_subdir, 'pkgconfig')]
'dirs': dirs
}

super(EB_GROMACS, self).sanity_check_step(custom_paths=custom_paths)