Select Git revision
using-gpus.md
cpmd.py 14.00 KiB
##
# Copyright 2016 Landcare Research NZ Ltd
#
# This file is part of EasyBuild,
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
# with support of Ghent University (http://ugent.be/hpc),
# the Flemish Supercomputer Centre (VSC) (https://vscentrum.be/nl/en),
# the Hercules foundation (http://www.herculesstichting.be/in_English)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# http://github.com/hpcugent/easybuild
#
# EasyBuild is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation v2.
#
# EasyBuild 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with EasyBuild. If not, see <http://www.gnu.org/licenses/>.
##
"""
EasyBuild support for building and installing CPMD, implemented as an easyblock
@author: Benjamin Roberts (Landcare Research NZ Ltd)
@author: Damian Alvarez (Forschungszentrum Juelich GmbH)
"""
#August 27, 2021 (tm)
#configuration and build step are done incorrectly
#generating the preprocessed source and object files in installdir/obj
#the pseudo-fix is to remove installdir/obj as postinstallcmds from the easyconfig file
#the documentation was missing and is now added in a somewhat weird procedure
#to the builddir and subsequently copied to the installdir/doc via postinstallcmds from the easyconfig file
#the hybrid MPI/OMP version can solely installed through the openmp toolchain option
#the cuda implementation is ignored
#
from distutils.version import LooseVersion
import glob
import os
import platform
import re
import shutil
import sys
from easybuild.easyblocks.generic.configuremake import ConfigureMake
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
import easybuild.tools.environment as env
from easybuild.tools.filetools import apply_regex_substitutions, extract_file
from easybuild.tools.modules import get_software_root, get_software_version
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import get_os_type
import easybuild.tools.toolchain as toolchain
class EB_CPMD(ConfigureMake):
"""
Support for building CPMD
"""
@staticmethod
def extra_options():
"""Custom easyconfig parameters for CPMD."""
extra_vars = {
'base_configuration': [None, "Base configuration from which to start (file name)", CUSTOM],
}
return ConfigureMake.extra_options(extra_vars)
def prepare_step(self):
super(EB_CPMD, self).prepare_step()
# create install directory and make sure it does not get cleaned up again in the install step;
# the first configure iteration already puts things in place in the install directory,
# so that shouldn't get cleaned up afterwards...
self.log.info("Creating install dir %s before starting configure-build-install iterations", self.installdir)
self.make_installdir()
self.cfg['keeppreviousinstall'] = True
def configure_step(self, cmd_prefix=''):
"""
Configure step
"""
config_file_candidates = []
for confdirname in ["configure", "CONFIGURE"]:
config_file_prefix = os.path.join(self.builddir, "CPMD", confdirname)
if os.path.isdir(config_file_prefix):
break
else:
raise EasyBuildError("No directory containing configuration files. Please review source tarball contents, and amend the EasyBlock if necessary")
# Work out a starting configuration file if one is not supplied in the easyconfig
if self.cfg['base_configuration']:
config_file_base = self.cfg['base_configuration']
else:
os_type_mappings = {
"LINUX" : "LINUX",
"DARWIN" : "MACOSX",
}
os_type = os_type_mappings[get_os_type().upper()]
machine = ""
if os_type != "MACOSX":
machine = platform.machine().upper()
config_file_base = os_type
if len(machine) > 0:
config_file_base += "-" + machine
if self.toolchain.comp_family() in [toolchain.INTELCOMP]:
config_file_base += "-INTEL"
# enable MPI support if desired
if self.toolchain.options.get('usempi', None):
config_file_base += "-MPI"
# Note that the -FFTW and -FFTW3 options are always at the end
# of the configuration file name, so this block needs to come
# last within the "else".
# Also, only version 3 or greater of FFTW is useful for CPMD.
if get_software_root('imkl') or (get_software_root('FFTW') and LooseVersion(get_software_version('FFTW')) >= LooseVersion('3.0')):
config_file_base += "-FFTW"
config_file_candidates.append(config_file_base + "3")
config_file_candidates.append(config_file_base)
selected_base_config = None
selected_full_config = None
for cfc in config_file_candidates:
self.log.info("Trying configuration file: %s", cfc)
config_file_full = os.path.join(config_file_prefix, cfc)
if os.path.isfile(config_file_full):
selected_base_config = cfc
selected_full_config = config_file_full
self.log.info("Selected %s as base configuration file", cfc)
break
if selected_base_config is None:
raise EasyBuildError("Base configuration file does not exist. Please edit base_configuration or review the CPMD easyblock.")
try:
apply_regex_substitutions(selected_full_config, [
# Better to get CC and FC from the EasyBuild environment in this instance
(r"^(\s*CC=.*)", r"#\1"),
(r"^(\s*FC=.*)", r"#\1"),
(r"^(\s*LD)=.*", r"\1='$(FC)'"),
])
except IOError as err:
raise EasyBuildError("Failed to patch %s: %s", selected_base_config, err)
if self.cfg['configure_cmd_prefix']:
if cmd_prefix:
tup = (cmd_prefix, self.cfg['configure_cmd_prefix'])
self.log.debug("Specified cmd_prefix '%s' is overruled by configure_cmd_prefix '%s'" % tup)
cmd_prefix = self.cfg['configure_cmd_prefix']
if self.cfg['tar_config_opts']:
# setting am_cv_prog_tar_ustar avoids that configure tries to figure out
# which command should be used for tarring/untarring
# am__tar and am__untar should be set to something decent (tar should work)
tar_vars = {
'am__tar': 'tar chf - "$$tardir"',
'am__untar': 'tar xf -',
'am_cv_prog_tar_ustar': 'easybuild_avoid_ustar_testing'
}
for (key, val) in tar_vars.items():
self.cfg.update('preconfigopts', "%s='%s'" % (key, val))
options = [self.cfg['configopts']]
# enable OpenMP support if desired
if self.toolchain.options.get('openmp', None) and LooseVersion(self.version) >= LooseVersion('4.0'):
options.append("-omp")
# This "option" has to come last as it's the chief argument, coming after
# all flags and so forth.
options.append(selected_base_config)
# I'm not sure when mkconfig.sh changed to configure.sh. Assuming 4.0
# for the sake of the argument.
if LooseVersion(self.version) >= LooseVersion('4.0'):
config_exe = 'configure.sh'
else:
config_exe = 'mkconfig.sh'
options.append('-BIN={0}'.format(os.path.join(self.installdir, "bin")))
options.append('>')
options.append(os.path.join(self.installdir, "Makefile"))
cmd = "%(preconfigopts)s %(cmd_prefix)s./%(config_exe)s %(prefix_opt)s%(installdir)s %(configopts)s" % {
'preconfigopts': self.cfg['preconfigopts'],
'cmd_prefix': cmd_prefix,
'config_exe': config_exe,
'prefix_opt': self.cfg['prefix_opt'],
'installdir': self.installdir,
'configopts': ' '.join(options),
}
(out, _) = run_cmd(cmd, log_all=True, simple=False)
return out
def build_step(self):
"""
Make some changes to files in order to make the build process more EasyBuild-friendly
"""
os.chdir(self.installdir)
if LooseVersion(self.version) < LooseVersion('4.0'):
os.mkdir("bin")
# Master configure script
makefile = os.path.join(self.installdir, "Makefile")
try:
apply_regex_substitutions(makefile, [
(r"^(\s*LFLAGS\s*=.*[^\w-])-L/usr/lib64/atlas/([^\w-].*)$", r"\1\2"),
(r"^(\s*LFLAGS\s*=.*[^\w-])-llapack([^\w-].*)$", r"\1\2"),
(r"^(\s*LFLAGS\s*=.*[^\w-])-lblas([^\w-].*)$", r"\1\2"),
(r"^(\s*LFLAGS\s*=.*[^\w-])-lfftw([^\w-].*)$", r"\1\2"),
])
if self.toolchain.comp_family() in [toolchain.INTELCOMP]:
preproc_flag = "-fpp"
ar_exe = "xiar -ruv"
apply_regex_substitutions(makefile, [
(r"^(\s*AR\s*=).*", r"\1 {0}".format(ar_exe))
])
if LooseVersion(self.version) < LooseVersion('4.0'):
apply_regex_substitutions(makefile, [
(r"^(\s*CFLAGS\s*=.*[^\w-])-O2([^\w-].*)$", r"\1\2"),
(r"^(\s*CFLAGS\s*=.*[^\w-])-Wall([^\w-].*)$", r"\1\2"),
(r"^(\s*CPPFLAGS\s*=.*[^\w-])-D__PGI([^\w-].*)$", r"\1\2"),
(r"^(\s*CPPFLAGS\s*=.*[^\w-])-D__GNU([^\w-].*)$", r"\1\2"),
(r"^(\s*FFLAGS\s*=.*[^\w-])-O2([^\w-].*)$", r"\1\2"),
(r"^(\s*FFLAGS\s*=.*[^\w-])-fcray-pointer([^\w-].*)$", r"\1\2"),
])
if preproc_flag is None:
preproc_flag = ''
apply_regex_substitutions(makefile, [
(r"^(\s*CPPFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('CPPFLAGS'))),
(r"^(\s*CFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('CFLAGS'))),
(r"^(\s*FFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('FFLAGS'))),
(r"^(\s*LFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('LDFLAGS'))),
# Allow to define own XFLAGS
(r"# CPPFLAGS =", r"CPPFLAGS +="),
(r"# CFLAGS =", r"CFLAGS +="),
(r"# FFLAGS =", r"FFLAGS +="),
(r"# LFLAGS =", r"LFLAGS +="),
# Add preprocessing options to FFLAGS and NOOPT_FLAG
(r"NOOPT_FLAG =", r"NOOPT_FLAG = {0}".format(preproc_flag)),
(r"FFLAGS =", r"FFLAGS = {0}".format(preproc_flag)),
])
if self.toolchain.options.get('openmp', None):
apply_regex_substitutions(makefile, [
(r"^(\s*LFLAGS\s*=.*)", r"\1 {0} {1}".format(os.getenv('LIBLAPACK_MT'), os.getenv('LIBBLAS_MT')))
])
else:
apply_regex_substitutions(makefile, [
(r"^(\s*LFLAGS\s*=.*)", r"\1 {0} {1}".format(os.getenv('LIBLAPACK'), os.getenv('LIBBLAS')))
])
apply_regex_substitutions(makefile, [
(r"^(\s*LFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('LIBFFT'))),
])
if get_software_root('imkl'):
if LooseVersion(self.version) < LooseVersion('4.0'):
apply_regex_substitutions(makefile, [
(r"(\s+)-DFFT_FFTW(\s+)", r"\1-DFFT_DEFAULT -DINTEL_MKL\2"),
])
if LooseVersion(self.version) >= LooseVersion('4.0'):
apply_regex_substitutions(makefile, [
(r"^(\s*CC\s*=.*)", r"#\1"),
(r"^(\s*FC\s*=.*)", r"#\1"),
])
except IOError as err:
raise EasyBuildError("Failed to patch %s: %s", makefile, err)
super(EB_CPMD, self).build_step()
def extract_step(self):
"""
Unpack the source files.
"""
for src in self.src:
if 'pseudo' in src['name']:
self.log.info("Not extracting %s, treating it as pseudopotentials to be installed separately" % src['name'])
elif 'pdf' in src['name']:
self.log.info("copy %(srx)s to %(self.builddir)s ")
cmd="cp %s %s " % (src['path'],self.builddir)
self.log.info(cmd)
run_cmd(cmd, log_all=True)
else:
self.log.info("Unpacking source %s" % src['name'])
srcdir = extract_file(src['path'], self.builddir, cmd=src['cmd'], extra_options=self.cfg['unpack_options'])
if srcdir:
self.src[self.src.index(src)]['finalpath'] = srcdir
else:
raise EasyBuildError("Unpacking source %s failed", src['name'])
# No need for a separate install step as the software is built in situ.
# In fact, an install step throws away the entire package.
# However, we consider the pseudopotentials and extract them in lib/
def install_step(self):
"""
Unpack the pseudopotentials if listed as source
"""
for src in self.src:
if 'pseudo' in src['name']:
self.log.info("Unpacking pseudopotentials file %s" % src['name'])
srcdir = extract_file(src['path'], '/'.join([self.installdir, 'lib']), cmd=src['cmd'], extra_options=self.cfg['unpack_options'])
if srcdir:
self.src[self.src.index(src)]['finalpath'] = srcdir
else:
raise EasyBuildError("Unpacking source %s failed", src['name'])