Skip to content
Snippets Groups Projects
Commit 02ee1eec authored by Lukáš Krupčík's avatar Lukáš Krupčík
Browse files

new file: easyblocks/s/siesta.py

parent 74c40acc
No related branches found
No related tags found
No related merge requests found
##
# Copyright 2009-2021 Ghent University
#
# 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://www.vscentrum.be),
# Flemish Research Foundation (FWO) (http://www.fwo.be/en)
# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
#
# https://github.com/easybuilders/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 Siesta, implemented as an easyblock
@author: Miguel Dias Costa (National University of Singapore)
@author: Ake Sandgren (Umea University)
"""
import os
import stat
import easybuild.tools.toolchain as toolchain
from distutils.version import LooseVersion
from easybuild.easyblocks.generic.configuremake import ConfigureMake
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.config import build_option
from easybuild.tools.filetools import adjust_permissions, apply_regex_substitutions
from easybuild.tools.filetools import change_dir, copy_dir, copy_file, mkdir
from easybuild.tools.modules import get_software_root, get_software_version
from easybuild.tools.run import run_cmd
from easybuild.tools.systemtools import get_shared_lib_ext
class EB_Siesta(ConfigureMake):
"""
Support for building/installing Siesta.
- avoid parallel build for older versions
"""
@staticmethod
def extra_options(extra_vars=None):
"""Define extra options for Siesta"""
extra = {
'with_transiesta': [True, "Build transiesta", CUSTOM],
'with_utils': [True, "Build all utils", CUSTOM],
}
return ConfigureMake.extra_options(extra_vars=extra)
def configure_step(self):
"""
Custom configure and build procedure for Siesta.
- There are two main builds to do, siesta and transiesta
- In addition there are multiple support tools to build
"""
start_dir = self.cfg['start_dir']
obj_dir = os.path.join(start_dir, 'Obj')
arch_make = os.path.join(obj_dir, 'arch.make')
bindir = os.path.join(start_dir, 'bin')
loose_ver = LooseVersion(self.version)
par = ''
if loose_ver >= LooseVersion('4.1'):
par = '-j %s' % self.cfg['parallel']
# enable OpenMP support if desired
env_var_suff = ''
if self.toolchain.options.get('openmp', None):
env_var_suff = '_MT'
scalapack = os.environ['LIBSCALAPACK' + env_var_suff]
blacs = os.environ['LIBSCALAPACK' + env_var_suff]
lapack = os.environ['LIBLAPACK' + env_var_suff]
blas = os.environ['LIBBLAS' + env_var_suff]
if get_software_root('imkl') or get_software_root('FFTW'):
fftw = os.environ['LIBFFT' + env_var_suff]
else:
fftw = None
regex_newlines = []
regex_subs = [
('dc_lapack.a', ''),
(r'^NETCDF_INTERFACE\s*=.*$', ''),
('libsiestaBLAS.a', ''),
('libsiestaLAPACK.a', ''),
# Needed here to allow 4.1-b1 to be built with openmp
(r"^(LDFLAGS\s*=).*$", r"\1 %s %s" % (os.environ['FCFLAGS'], os.environ['LDFLAGS'])),
]
regex_subs_gfortran = [
(r"^(FCFLAGS_free_f90\s*=.*)$", r"\1 -ffree-line-length-none"),
(r"^(FPPFLAGS_free_F90\s*=.*)$", r"\1 -ffree-line-length-none"),
]
netcdff_loc = get_software_root('netCDF-Fortran')
if netcdff_loc:
# Needed for gfortran at least
regex_newlines.append((r"^(ARFLAGS_EXTRA\s*=.*)$", r"\1\nNETCDF_INCFLAGS = -I%s/include" % netcdff_loc))
if fftw:
fft_inc, fft_lib = os.environ['FFT_INC_DIR'], os.environ['FFT_LIB_DIR']
fppflags = r"\1\nFFTW_INCFLAGS = -I%s\nFFTW_LIBS = -L%s %s" % (fft_inc, fft_lib, fftw)
regex_newlines.append((r'(FPPFLAGS\s*:?=.*)$', fppflags))
# Make a temp installdir during the build of the various parts
mkdir(bindir)
# change to actual build dir
change_dir(obj_dir)
# Populate start_dir with makefiles
run_cmd(os.path.join(start_dir, 'Src', 'obj_setup.sh'), log_all=True, simple=True, log_output=True)
if loose_ver < LooseVersion('4.1-b2'):
# MPI?
if self.toolchain.options.get('usempi', None):
self.cfg.update('configopts', '--enable-mpi')
# BLAS and LAPACK
self.cfg.update('configopts', '--with-blas="%s"' % blas)
self.cfg.update('configopts', '--with-lapack="%s"' % lapack)
# ScaLAPACK (and BLACS)
self.cfg.update('configopts', '--with-scalapack="%s"' % scalapack)
self.cfg.update('configopts', '--with-blacs="%s"' % blacs)
# NetCDF-Fortran
if netcdff_loc:
self.cfg.update('configopts', '--with-netcdf=-lnetcdff')
# Configure is run in obj_dir, configure script is in ../Src
super(EB_Siesta, self).configure_step(cmd_prefix='../Src/')
if loose_ver > LooseVersion('4.0'):
regex_subs_Makefile = [
(r'CFLAGS\)-c', r'CFLAGS) -c'),
]
apply_regex_substitutions('Makefile', regex_subs_Makefile)
if self.toolchain.comp_family() in [toolchain.GCC]:
apply_regex_substitutions(arch_make, regex_subs_gfortran)
else: # there's no configure on newer versions
if self.toolchain.comp_family() in [toolchain.INTELCOMP]:
copy_file(os.path.join(obj_dir, 'intel.make'), arch_make)
elif self.toolchain.comp_family() in [toolchain.GCC]:
copy_file(os.path.join(obj_dir, 'gfortran.make'), arch_make)
else:
raise EasyBuildError("There is currently no support for compiler: %s", self.toolchain.comp_family())
regex_subs.append((r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DF2003"))
if self.toolchain.options.get('usempi', None):
regex_subs.extend([
(r"^(CC\s*=\s*).*$", r"\1%s" % os.environ['MPICC']),
(r"^(FC\s*=\s*).*$", r"\1%s" % os.environ['MPIF90']),
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DMPI"),
])
regex_newlines.append((r"^(FPPFLAGS\s*:?=.*)$", r"\1\nMPI_INTERFACE = libmpi_f90.a\nMPI_INCLUDE = ."))
complibs = scalapack
else:
complibs = lapack
regex_subs.extend([
(r"^(LIBS\s*=).*$", r"\1 %s" % complibs),
# Needed for a couple of the utils
(r"^(FFLAGS\s*=\s*).*$", r"\1 -fPIC %s" % os.environ['FCFLAGS']),
])
regex_newlines.append((r"^(COMP_LIBS\s*=.*)$", r"\1\nWXML = libwxml.a"))
if self.toolchain.comp_family() in [toolchain.GCC]:
regex_subs.extend(regex_subs_gfortran)
if netcdff_loc:
regex_subs.extend([
(r"^(LIBS\s*=.*)$", r"\1 $(NETCDF_LIBS)"),
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DCDF -DNCDF -DNCDF_4 -DNCDF_PARALLEL $(NETCDF_INCLUDE)"),
(r"^(COMP_LIBS\s*=.*)$", r"\1 libncdf.a libfdict.a"),
])
netcdf_lib_and_inc = "NETCDF_LIBS = -lnetcdff\nNETCDF_INCLUDE = -I%s/include" % netcdff_loc
netcdf_lib_and_inc += "\nINCFLAGS = $(NETCDF_INCLUDE)"
regex_newlines.append((r"^(COMP_LIBS\s*=.*)$", r"\1\n%s" % netcdf_lib_and_inc))
xmlf90 = get_software_root('xmlf90')
if xmlf90:
regex_subs.append((r"^(XMLF90_ROOT\s*=).*$", r"\1%s" % xmlf90))
libpsml = get_software_root('libPSML')
if libpsml:
regex_subs.append((r"^(PSML_ROOT\s*=).*$.*", r"\1%s" % libpsml))
libgridxc = get_software_root('libGridXC')
if libgridxc:
regex_subs.append((r"^(GRIDXC_ROOT\s*=).*$", r"\1%s" % libgridxc))
libxc = get_software_root('libxc')
if libxc:
regex_subs.append((r"^#(LIBXC_ROOT\s*=).*$", r"\1 %s" % libxc))
elpa = get_software_root('ELPA')
if elpa:
elpa_ver = get_software_version('ELPA')
regex_subs.extend([
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DSIESTA__ELPA"),
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -I%s/include/elpa-%s/modules" % (elpa, elpa_ver)),
(r"^(LIBS\s*=.*)$", r"\1 -L%s/lib -lelpa" % elpa),
])
elsi = get_software_root('ELSI')
if elsi:
if not os.path.isfile(os.path.join(elsi, 'lib', 'libelsi.%s' % get_shared_lib_ext())):
raise EasyBuildError("This easyblock requires ELSI shared libraries instead of static")
regex_subs.extend([
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DSIESTA__ELSI"),
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -I%s/include" % elsi),
(r"^(LIBS\s*=.*)$", r"\1 $(FFTW_LIBS) -L%s/lib -lelsi" % elsi),
])
metis = get_software_root('METIS')
if metis:
regex_subs.extend([
(r"^(FPPFLAGS\s*:?=.*)$", r"\1 -DSIESTA__METIS"),
(r"^(LIBS\s*=.*)$", r"\1 -L%s/lib -lmetis" % metis),
])
apply_regex_substitutions(arch_make, regex_subs)
# individually apply substitutions that add lines
for regex_nl in regex_newlines:
apply_regex_substitutions(arch_make, [regex_nl])
run_cmd('make %s' % par, log_all=True, simple=True, log_output=True)
# Put binary in temporary install dir
copy_file(os.path.join(obj_dir, 'siesta'), bindir)
if self.cfg['with_utils']:
# Make the utils
change_dir(os.path.join(start_dir, 'Util'))
if loose_ver >= LooseVersion('4'):
# clean_all.sh might be missing executable bit...
adjust_permissions('./clean_all.sh', stat.S_IXUSR, recursive=False, relative=True)
run_cmd('./clean_all.sh', log_all=True, simple=True, log_output=True)
if loose_ver >= LooseVersion('4.1'):
regex_subs_TS = [
(r"^default:.*$", r""),
(r"^EXE\s*=.*$", r""),
(r"^(include\s*..ARCH_MAKE.*)$", r"EXE=tshs2tshs\ndefault: $(EXE)\n\1"),
(r"^(INCFLAGS.*)$", r"\1 -I%s" % obj_dir),
]
makefile = os.path.join(start_dir, 'Util', 'TS', 'tshs2tshs', 'Makefile')
apply_regex_substitutions(makefile, regex_subs_TS)
if loose_ver >= LooseVersion('4'):
# SUFFIX rules in wrong place
regex_subs_suffix = [
(r'^(\.SUFFIXES:.*)$', r''),
(r'^(include\s*\$\(ARCH_MAKE\).*)$', r'\1\n.SUFFIXES:\n.SUFFIXES: .c .f .F .o .a .f90 .F90'),
]
makefile = os.path.join(start_dir, 'Util', 'Sockets', 'Makefile')
apply_regex_substitutions(makefile, regex_subs_suffix)
makefile = os.path.join(start_dir, 'Util', 'SiestaSubroutine', 'SimpleTest', 'Src', 'Makefile')
apply_regex_substitutions(makefile, regex_subs_suffix)
regex_subs_UtilLDFLAGS = [
(r'(\$\(FC\)\s*-o\s)', r'$(FC) %s %s -o ' % (os.environ['FCFLAGS'], os.environ['LDFLAGS'])),
]
makefile = os.path.join(start_dir, 'Util', 'Optimizer', 'Makefile')
apply_regex_substitutions(makefile, regex_subs_UtilLDFLAGS)
if loose_ver >= LooseVersion('4'):
makefile = os.path.join(start_dir, 'Util', 'JobList', 'Src', 'Makefile')
apply_regex_substitutions(makefile, regex_subs_UtilLDFLAGS)
# remove clean at the end of default target
# And yes, they are re-introducing this bug.
is_ver40_to_401 = loose_ver >= LooseVersion('4.0') and loose_ver < LooseVersion('4.0.2')
if (is_ver40_to_401 or loose_ver == LooseVersion('4.1-b3')):
makefile = os.path.join(start_dir, 'Util', 'SiestaSubroutine', 'SimpleTest', 'Src', 'Makefile')
apply_regex_substitutions(makefile, [(r"simple_mpi_parallel clean", r"simple_mpi_parallel")])
makefile = os.path.join(start_dir, 'Util', 'SiestaSubroutine', 'ProtoNEB', 'Src', 'Makefile')
apply_regex_substitutions(makefile, [(r"protoNEB clean", r"protoNEB")])
# build_all.sh might be missing executable bit...
adjust_permissions('./build_all.sh', stat.S_IXUSR, recursive=False, relative=True)
run_cmd('./build_all.sh', log_all=True, simple=True, log_output=True)
# Now move all the built utils to the temp installdir
expected_utils = [
'CMLComp/ccViz',
'Contrib/APostnikov/eig2bxsf', 'Contrib/APostnikov/fmpdos',
'Contrib/APostnikov/md2axsf', 'Contrib/APostnikov/rho2xsf',
'Contrib/APostnikov/vib2xsf', 'Contrib/APostnikov/xv2xsf',
'COOP/fat', 'COOP/mprop',
'Denchar/Src/denchar',
'DensityMatrix/cdf2dm', 'DensityMatrix/dm2cdf',
'Eig2DOS/Eig2DOS',
'Gen-basis/ionplot.sh',
'Grid/cdf2grid', 'Grid/cdf2xsf', 'Grid/cdf_laplacian',
'Grid/g2c_ng', 'Grid/grid2cdf', 'Grid/grid2cube',
'Grid/grid2val', 'Grid/grid_rotate',
'Helpers/get_chem_labels',
'HSX/hs2hsx', 'HSX/hsx2hs',
'JobList/Src/countJobs', 'JobList/Src/getResults',
'JobList/Src/horizontal', 'JobList/Src/runJobs',
'Macroave/Src/macroave',
'ON/lwf2cdf',
'Optimizer/simplex', 'Optimizer/swarm',
'pdosxml/pdosxml',
'Projections/orbmol_proj',
'SiestaSubroutine/FmixMD/Src/driver',
'SiestaSubroutine/FmixMD/Src/para',
'SiestaSubroutine/FmixMD/Src/simple',
'STM/ol-stm/Src/stm', 'STM/simple-stm/plstm',
'Vibra/Src/fcbuild', 'Vibra/Src/vibra',
'WFS/readwf', 'WFS/readwfx', 'WFS/wfs2wfsx',
'WFS/wfsnc2wfsx', 'WFS/wfsx2wfs',
]
# skip broken utils in 4.1-MaX-1.0 release, hopefully will be fixed later
if self.version != '4.1-MaX-1.0':
expected_utils.extend([
'VCA/fractional', 'VCA/mixps',
])
if loose_ver >= LooseVersion('3.2'):
expected_utils.extend([
'Bands/eigfat2plot',
])
if loose_ver >= LooseVersion('4.0'):
if self.version != '4.1-MaX-1.0':
expected_utils.extend([
'SiestaSubroutine/ProtoNEB/Src/protoNEB',
'SiestaSubroutine/SimpleTest/Src/simple_pipes_parallel',
'SiestaSubroutine/SimpleTest/Src/simple_pipes_serial',
'SiestaSubroutine/SimpleTest/Src/simple_sockets_parallel',
'SiestaSubroutine/SimpleTest/Src/simple_sockets_serial',
])
expected_utils.extend([
'Sockets/f2fmaster', 'Sockets/f2fslave',
])
if self.toolchain.options.get('usempi', None):
if self.version != '4.1-MaX-1.0':
expected_utils.extend([
'SiestaSubroutine/SimpleTest/Src/simple_mpi_parallel',
'SiestaSubroutine/SimpleTest/Src/simple_mpi_serial',
])
if loose_ver < LooseVersion('4.1'):
expected_utils.append('WFS/info_wfsx')
if loose_ver >= LooseVersion('4.0'):
expected_utils.extend([
'COOP/dm_creator',
'TBTrans_rep/tbtrans',
])
else:
expected_utils.extend([
'TBTrans/tbtrans',
])
if loose_ver < LooseVersion('4.0.2'):
expected_utils.extend([
'Bands/new.gnubands',
])
else:
expected_utils.extend([
'Bands/gnubands',
])
# Need to revisit this when 4.1 is officialy released.
# This is based on b1-b3 releases
if loose_ver < LooseVersion('4.1'):
expected_utils.extend([
'Contour/grid1d', 'Contour/grid2d',
'Optical/optical', 'Optical/optical_input',
'sies2arc/sies2arc',
])
if loose_ver >= LooseVersion('4.1'):
expected_utils.extend([
'DensityMatrix/dmbs2dm', 'DensityMatrix/dmUnblock',
'Grimme/fdf2grimme',
'SpPivot/pvtsp',
'TS/TBtrans/tbtrans', 'TS/tselecs.sh',
'TS/ts2ts/ts2ts',
])
if self.version != '4.1-MaX-1.0':
expected_utils.extend([
'TS/tshs2tshs/tshs2tshs',
])
for util in expected_utils:
copy_file(os.path.join(start_dir, 'Util', util), bindir)
if self.cfg['with_transiesta']:
# Build transiesta
change_dir(obj_dir)
ts_clean_target = 'clean'
if loose_ver >= LooseVersion('4.1-b4'):
ts_clean_target += '-transiesta'
run_cmd('make %s' % ts_clean_target, log_all=True, simple=True, log_output=True)
run_cmd('make %s transiesta' % par, log_all=True, simple=True, log_output=True)
copy_file(os.path.join(obj_dir, 'transiesta'), bindir)
def build_step(self):
"""No build step for Siesta."""
pass
def test_step(self):
"""Custom test step for Siesta."""
change_dir(os.path.join(self.cfg['start_dir'], 'Obj', 'Tests'))
super(EB_Siesta, self).test_step()
def install_step(self):
"""Custom install procedure for Siesta: copy binaries."""
bindir = os.path.join(self.installdir, 'bin')
copy_dir(os.path.join(self.cfg['start_dir'], 'bin'), bindir)
def sanity_check_step(self):
"""Custom sanity check for Siesta."""
bins = ['bin/siesta']
if self.cfg['with_transiesta']:
bins.append('bin/transiesta')
if self.cfg['with_utils']:
bins.append('bin/denchar')
custom_paths = {
'files': bins,
'dirs': [],
}
custom_commands = []
if self.toolchain.options.get('usempi', None) and build_option('mpi_tests'):
# make sure Siesta was indeed built with support for running in parallel
# The "cd to builddir" is required to not contaminate the install dir with cruft from running siesta
mpi_test_cmd = "cd %s && " % self.builddir
mpi_test_cmd = mpi_test_cmd + "echo 'SystemName test' | mpirun -np 2 siesta 2>/dev/null | grep PARALLEL"
custom_commands.append(mpi_test_cmd)
super(EB_Siesta, self).sanity_check_step(custom_paths=custom_paths, custom_commands=custom_commands)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment