Abinit: a few modernizations for the package and its dependencies (#3699)

* libxc: added libs interface

* hdf5: added libs interface, added conflicts

* abinit: modernized package to use build interface

* netcdf-fortran: added libs interface

* abinit: added version 8.2.2
This commit is contained in:
Massimiliano Culpo 2017-04-07 11:18:34 +02:00 committed by GitHub
parent 030127a071
commit 10c395b2f5
5 changed files with 180 additions and 87 deletions

View file

@ -901,16 +901,12 @@ def __init__(self, spec, name, query_parameters):
) )
is_virtual = Spec.is_virtual(name) is_virtual = Spec.is_virtual(name)
self._query_to_package = QueryState( self.last_query = QueryState(
name=name, name=name,
extra_parameters=query_parameters, extra_parameters=query_parameters,
isvirtual=is_virtual isvirtual=is_virtual
) )
@property
def last_query(self):
return self._query_to_package
@key_ordering @key_ordering
class Spec(object): class Spec(object):

View file

@ -28,13 +28,15 @@
from spack import * from spack import *
class Abinit(Package): class Abinit(AutotoolsPackage):
"""ABINIT is a package whose main program allows one to find the total """ABINIT is a package whose main program allows one to find the total
energy, charge density and electronic structure of systems made of energy, charge density and electronic structure of systems made of
electrons and nuclei (molecules and periodic solids) within electrons and nuclei (molecules and periodic solids) within
Density Functional Theory (DFT), using pseudopotentials and a planewave Density Functional Theory (DFT), using pseudopotentials and a planewave
or wavelet basis. ABINIT also includes options to optimize the geometry or wavelet basis.
according to the DFT forces and stresses, or to perform molecular dynamics
ABINIT also includes options to optimize the geometry according to the
DFT forces and stresses, or to perform molecular dynamics
simulations using these forces, or to generate dynamical matrices, simulations using these forces, or to generate dynamical matrices,
Born effective charges, and dielectric tensors, based on Density-Functional Born effective charges, and dielectric tensors, based on Density-Functional
Perturbation Theory, and many more properties. Excited states can be Perturbation Theory, and many more properties. Excited states can be
@ -44,11 +46,12 @@ class Abinit(Package):
programs are provided. programs are provided.
""" """
homepage = "http://www.abinit.org" homepage = 'http://www.abinit.org'
url = "http://ftp.abinit.org/abinit-8.0.8b.tar.gz" url = 'http://ftp.abinit.org/abinit-8.0.8b.tar.gz'
version('8.2.2', '5f25250e06fdc0815c224ffd29858860')
# Versions before 8.0.8b are not supported. # Versions before 8.0.8b are not supported.
version("8.0.8b", "abc9e303bfa7f9f43f95598f87d84d5d") version('8.0.8b', 'abc9e303bfa7f9f43f95598f87d84d5d')
variant('mpi', default=True, variant('mpi', default=True,
description='Builds with MPI support. Requires MPI2+') description='Builds with MPI support. Requires MPI2+')
@ -57,7 +60,7 @@ class Abinit(Package):
variant('scalapack', default=False, variant('scalapack', default=False,
description='Enables scalapack support. Requires MPI') description='Enables scalapack support. Requires MPI')
# variant('elpa', default=False, # variant('elpa', default=False,
# description='Uses elpa instead of scalapack. Requires MPI') # description='Uses elpa instead of scalapack. Requires MPI')
# TODO: To be tested. # TODO: To be tested.
# It was working before the last `git pull` but now all tests crash. # It was working before the last `git pull` but now all tests crash.
@ -71,111 +74,108 @@ class Abinit(Package):
# Add dependencies # Add dependencies
# currently one cannot forward options to virtual packages, see #1712. # currently one cannot forward options to virtual packages, see #1712.
# depends_on("blas", when="~openmp") # depends_on('blas', when='~openmp')
# depends_on("blas+openmp", when="+openmp") # depends_on('blas+openmp', when='+openmp')
depends_on('blas') depends_on('blas')
depends_on("lapack") depends_on('lapack')
# Require MPI2+ # Require MPI2+
depends_on("mpi@2:", when="+mpi") depends_on('mpi@2:', when='+mpi')
depends_on("scalapack", when="+scalapack+mpi") depends_on('scalapack', when='+scalapack+mpi')
# depends_on("elpa", when="+elpa+mpi~openmp")
# depends_on("elpa+openmp", when="+elpa+mpi+openmp")
depends_on("fftw+float", when="~openmp") # depends_on('elpa~openmp', when='+elpa+mpi~openmp')
depends_on("fftw+float+openmp", when="+openmp") # depends_on('elpa+openmp', when='+elpa+mpi+openmp')
depends_on("netcdf-fortran", when="+hdf5") depends_on('fftw+float', when='~openmp')
depends_on("hdf5+mpi", when='+mpi+hdf5') # required for NetCDF-4 support depends_on('fftw+float+openmp', when='+openmp')
depends_on('netcdf-fortran', when='+hdf5')
depends_on('hdf5+mpi', when='+mpi+hdf5') # required for NetCDF-4 support
# pin libxc version # pin libxc version
depends_on("libxc@2.2.1") depends_on("libxc@2.2.2")
def validate(self, spec): # Cannot ask for +scalapack if it does not depend on MPI
""" conflicts('+scalapack', when='~mpi')
Checks if incompatible variants have been activated at the same time
:param spec: spec of the package # Elpa is a substitute for scalapack and needs mpi
:raises RuntimeError: in case of inconsistencies # conflicts('+elpa', when='~mpi')
""" # conflicts('+elpa', when='+scalapack')
error = 'you cannot ask for \'+{variant}\' when \'+mpi\' is not active'
if '+scalapack' in spec and '~mpi' in spec: def configure_args(self):
raise RuntimeError(error.format(variant='scalapack'))
if '+elpa' in spec and ('~mpi' in spec or '~scalapack' in spec): spec = self.spec
raise RuntimeError(error.format(variant='elpa'))
def install(self, spec, prefix): options = []
self.validate(spec)
options = ['--prefix=%s' % prefix]
oapp = options.append oapp = options.append
if '+mpi' in spec: if '+mpi' in spec:
# MPI version: # MPI version:
# let the configure script auto-detect MPI support from mpi_prefix # let the configure script auto-detect MPI support from mpi_prefix
oapp("--with-mpi-prefix=%s" % spec["mpi"].prefix) oapp('--with-mpi-prefix={0}'.format(spec['mpi'].prefix))
oapp("--enable-mpi=yes") oapp('--enable-mpi=yes')
oapp("--enable-mpi-io=yes") oapp('--enable-mpi-io=yes')
# Activate OpenMP in Abinit Fortran code. # Activate OpenMP in Abinit Fortran code.
if '+openmp' in spec: if '+openmp' in spec:
oapp('--enable-openmp=yes') oapp('--enable-openmp=yes')
# BLAS/LAPACK # BLAS/LAPACK/SCALAPACK-ELPA
linalg = spec['lapack'].libs + spec['blas'].libs
if '+scalapack' in spec: if '+scalapack' in spec:
oapp("--with-linalg-flavor=custom+scalapack") oapp('--with-linalg-flavor=custom+scalapack')
linalg = (spec['scalapack'].libs + linalg = spec['scalapack'].libs + linalg
spec['lapack'].libs + spec['blas'].libs)
# elif '+elpa' in spec: # elif '+elpa' in spec:
else: else:
oapp("--with-linalg-flavor=custom") oapp('--with-linalg-flavor=custom')
linalg = spec['lapack'].libs + spec['blas'].libs
oapp("--with-linalg-libs=%s" % linalg.ld_flags) oapp('--with-linalg-libs={0}'.format(linalg.ld_flags))
# FFTW3: use sequential or threaded version if +openmp # FFTW3: use sequential or threaded version if +openmp
fftflavor, fftlibs = "fftw3", "-lfftw3 -lfftw3f" fftflavor, fftlibs = 'fftw3', '-lfftw3 -lfftw3f'
if '+openmp' in spec: if '+openmp' in spec:
fftflavor = "fftw3-threads" fftflavor = 'fftw3-threads'
fftlibs = "-lfftw3_omp -lfftw3 -lfftw3f" fftlibs = '-lfftw3_omp -lfftw3 -lfftw3f'
options.extend([ options.extend([
"--with-fft-flavor=%s" % fftflavor, '--with-fft-flavor=%s' % fftflavor,
"--with-fft-incs=-I%s" % spec["fftw"].prefix.include, '--with-fft-incs=-I%s' % spec['fftw'].prefix.include,
"--with-fft-libs=-L%s %s" % (spec["fftw"].prefix.lib, fftlibs), '--with-fft-libs=-L%s %s' % (spec['fftw'].prefix.lib, fftlibs),
]) ])
oapp("--with-dft-flavor=atompaw+libxc") oapp('--with-dft-flavor=atompaw+libxc')
# LibXC library # LibXC library
libxc = spec['libxc:fortran']
options.extend([ options.extend([
"with_libxc_incs=-I%s" % spec["libxc"].prefix.include, 'with_libxc_incs={0}'.format(libxc.cppflags),
"with_libxc_libs=-L%s -lxcf90 -lxc" % spec["libxc"].prefix.lib, 'with_libxc_libs={0}'.format(libxc.libs.ld_flags + ' -lm')
]) ])
# Netcdf4/HDF5 # Netcdf4/HDF5
if "+hdf5" in spec: if '+hdf5' in spec:
oapp("--with-trio-flavor=netcdf") oapp('--with-trio-flavor=netcdf')
# Since version 8, Abinit started to use netcdf4 + hdf5 and we have # Since version 8, Abinit started to use netcdf4 + hdf5 and we have
# to link with -lhdf5_hl -lhdf5 # to link with the high level HDF5 library
hdf_libs = "-L%s -lhdf5_hl -lhdf5" % spec["hdf5"].prefix.lib hdf5 = spec['hdf5:hl']
netcdff = spec['netcdf-fortran:shared']
options.extend([ options.extend([
"--with-netcdf-incs=-I%s" % ( '--with-netcdf-incs={0}'.format(netcdff.cppflags),
spec["netcdf-fortran"].prefix.include), '--with-netcdf-libs={0}'.format(
"--with-netcdf-libs=-L%s -lnetcdff -lnetcdf %s" % ( netcdff.libs.ld_flags + ' ' + hdf5.libs.ld_flags
spec["netcdf-fortran"].prefix.lib, hdf_libs), ),
]) ])
else: else:
# In Spack we do our best to avoid building any internally provided # In Spack we do our best to avoid building any internally provided
# dependencies, such as netcdf3 in this case. # dependencies, such as netcdf3 in this case.
oapp("--with-trio-flavor=none") oapp('--with-trio-flavor=none')
configure(*options) return options
make()
# make("check") def check(self):
# make("tests_in") """This method is called after the build phase if tests have been
make("install") explicitly activated by user.
"""
make('check')
make('tests_in')

View file

@ -70,23 +70,82 @@ class Hdf5(AutotoolsPackage):
depends_on('szip', when='+szip') depends_on('szip', when='+szip')
depends_on('zlib@1.1.2:') depends_on('zlib@1.1.2:')
@run_before('configure') # According to ./configure --help thread-safe capabilities are:
def validate(self): # "Not compatible with the high-level library, Fortran, or C++ wrappers."
""" # (taken from hdf5@1.10.0patch1)
Checks if incompatible variants have been activated at the same time conflicts('+threadsafe', when='+cxx')
conflicts('+threadsafe', when='+fortran')
:param spec: spec of the package @property
:raises RuntimeError: in case of inconsistencies def libs(self):
"""Hdf5 can be queried for the following parameters:
- "hl": high-level interface
- "cxx": C++ APIs
- "fortran": fortran APIs
:return: list of matching libraries
""" """
query_parameters = self.spec.last_query.extra_parameters
shared = '+shared' in self.spec
# This map contains a translation from query_parameters
# to the libraries needed
query2libraries = {
tuple(): ['libhdf5'],
('cxx', 'fortran', 'hl'): [
'libhdf5hl_fortran',
'libhdf5_hl_cpp',
'libhdf5_hl',
'libhdf5_fortran',
'libhdf5',
],
('cxx', 'hl'): [
'libhdf5_hl_cpp',
'libhdf5_hl',
'libhdf5',
],
('fortran', 'hl'): [
'libhdf5hl_fortran',
'libhdf5_hl',
'libhdf5_fortran',
'libhdf5',
],
('hl',): [
'libhdf5_hl',
'libhdf5',
],
('cxx', 'fortran'): [
'libhdf5_fortran',
'libhdf5_cpp',
'libhdf5',
],
('cxx',): [
'libhdf5_cpp',
'libhdf5',
],
('fortran',): [
'libhdf5_fortran',
'libhdf5',
]
}
# Turn the query into the appropriate key
key = tuple(sorted(query_parameters))
libraries = query2libraries[key]
return find_libraries(
libraries, root=self.prefix, shared=shared, recurse=True
)
@run_before('configure')
def fortran_check(self):
spec = self.spec spec = self.spec
if '+fortran' in spec and not self.compiler.fc: if '+fortran' in spec and not self.compiler.fc:
msg = 'cannot build a fortran variant without a fortran compiler' msg = 'cannot build a fortran variant without a fortran compiler'
raise RuntimeError(msg) raise RuntimeError(msg)
if '+threadsafe' in spec and ('+cxx' in spec or '+fortran' in spec):
msg = 'cannot use variant +threadsafe with either +cxx or +fortran'
raise RuntimeError(msg)
def configure_args(self): def configure_args(self):
spec = self.spec spec = self.spec
# Handle compilation after spec validation # Handle compilation after spec validation
@ -156,11 +215,9 @@ def configure_args(self):
return ["--with-zlib=%s" % spec['zlib'].prefix] + extra_args return ["--with-zlib=%s" % spec['zlib'].prefix] + extra_args
def configure(self, spec, prefix): @run_after('configure')
# Run the default autotools package configure def patch_postdeps(self):
super(Hdf5, self).configure(spec, prefix) if '@:1.8.14' in self.spec:
if '@:1.8.14' in spec:
# On Ubuntu14, HDF5 1.8.12 (and maybe other versions) # On Ubuntu14, HDF5 1.8.12 (and maybe other versions)
# mysteriously end up with "-l -l" in the postdeps in the # mysteriously end up with "-l -l" in the postdeps in the
# libtool script. Patch this by removing the spurious -l's. # libtool script. Patch this by removing the spurious -l's.

View file

@ -36,6 +36,33 @@ class Libxc(Package):
version('2.2.2', 'd9f90a0d6e36df6c1312b6422280f2ec') version('2.2.2', 'd9f90a0d6e36df6c1312b6422280f2ec')
version('2.2.1', '38dc3a067524baf4f8521d5bb1cd0b8f') version('2.2.1', '38dc3a067524baf4f8521d5bb1cd0b8f')
@property
def libs(self):
"""Libxc can be queried for the following parameters:
- "static": returns the static library version of libxc
(by default the shared version is returned)
:return: list of matching libraries
"""
query_parameters = self.spec.last_query.extra_parameters
libraries = ['libxc']
# Libxc installs both shared and static libraries.
# If a client ask for static explicitly then return
# the static libraries
shared = False if 'static' in query_parameters else True
# Libxc has a fortran90 interface: give clients the
# possibility to query for it
if 'fortran' in query_parameters:
libraries = ['libxcf90'] + libraries
return find_libraries(
libraries, root=self.prefix, shared=shared, recurse=True
)
def install(self, spec, prefix): def install(self, spec, prefix):
# Optimizations for the Intel compiler, suggested by CP2K # Optimizations for the Intel compiler, suggested by CP2K
optflags = '-O2' optflags = '-O2'

View file

@ -35,3 +35,16 @@ class NetcdfFortran(AutotoolsPackage):
version('4.4.3', 'bfd4ae23a34635b273d3eb0d91cbde9e') version('4.4.3', 'bfd4ae23a34635b273d3eb0d91cbde9e')
depends_on('netcdf') depends_on('netcdf')
@property
def libs(self):
libraries = ['libnetcdff']
# This package installs both shared and static libraries. Permit
# clients to query which one they want.
query_parameters = self.spec.last_query.extra_parameters
shared = 'shared' in query_parameters
return find_libraries(
libraries, root=self.prefix, shared=shared, recurse=True
)