# Copyright 2013-2024 Lawrence Livermore National Security, LLC and other
# Spack Project Developers. See the top-level COPYRIGHT file for details.
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from spack.package import *
class Berkeleygw(MakefilePackage):
"""BerkeleyGW is a many-body perturbation theory code for excited states,
using the GW method and the GW plus Bethe-Salpeter equation (GW-BSE) method
to solve respectively for quasiparticle excitations and optical properties of
materials."""
homepage = "https://berkeleygw.org"
maintainers("migueldiascosta")
version(
"3.0.1",
sha256="7d8c2cc1ee679afb48efbdd676689d4d537226b50e13a049dbcb052aaaf3654f",
url="https://berkeley.box.com/shared/static/m1dgnhiemo47lhxczrn6si71bwxoxor8.gz",
expand=False,
)
version(
"3.0",
sha256="ab411acead5e979fd42b8d298dbb0a12ce152e7be9eee0bb87e9e5a06a638e2a",
url="https://berkeley.box.com/shared/static/lp6hj4kxr459l5a6t05qfuzl2ucyo03q.gz",
expand=False,
)
version(
"2.1",
sha256="31f3b643dd937350c3866338321d675d4a1b1f54c730b43ad74ae67e75a9e6f2",
url="https://berkeley.box.com/shared/static/ze3azi5vlyw7hpwvl9i5f82kaiid6g0x.gz",
expand=False,
)
# For parallel computing support, enable +mpi. It uses MPI and ScaLAPACK
# which are inter-dependent in the berkeleygw code(they need each other):
# https://github.com/spack/spack/pull/33948#issuecomment-1323805817
variant("mpi", default=True, description="Build with MPI and ScaLAPACK support")
variant("elpa", default=True, description="Build with ELPA support")
variant("python", default=True, description="Build with Python support")
variant("openmp", default=True, description="Build with OpenMP support")
variant("hdf5", default=True, description="Builds with HDF5 support")
variant("debug", default=False, description="Builds with DEBUG flag")
variant("verbose", default=False, description="Builds with VERBOSE flag")
depends_on("blas")
depends_on("lapack")
depends_on("mpi", when="+mpi")
depends_on("scalapack", when="+mpi")
depends_on("hdf5+fortran+hl", when="+hdf5~mpi")
depends_on("hdf5+fortran+hl+mpi", when="+hdf5+mpi")
depends_on("elpa+openmp", when="+elpa+openmp")
depends_on("elpa~openmp", when="+elpa~openmp")
depends_on("fftw-api@3")
with when("+openmp"):
depends_on("acfl threads=openmp", when="^[virtuals=fftw-api] acfl")
depends_on("amdfftw+openmp", when="^[virtuals=fftw-api] amdfftw")
depends_on("armpl-gcc threads=openmp", when="^[virtuals=fftw-api] armpl-gcc")
depends_on("cray-fftw+openmp", when="^[virtuals=fftw-api] cray-fftw")
depends_on("fftw+openmp", when="^[virtuals=fftw-api] fftw")
depends_on("fujitsu-fftw+openmp", when="^[virtuals=fftw-api] fujitsu-fftw")
depends_on("intel-mkl threads=openmp", when="^[virtuals=fftw-api] intel-mkl")
depends_on("intel-oneapi-mkl threads=openmp", when="^[virtuals=fftw-api] intel-oneapi-mkl")
depends_on(
"intel-parallel-studio threads=openmp",
when="^[virtuals=fftw-api] intel-parallel-studio",
)
with when("~openmp"):
depends_on("acfl threads=none", when="^[virtuals=fftw-api] acfl")
depends_on("amdfftw~openmp", when="^[virtuals=fftw-api] amdfftw")
depends_on("armpl-gcc threads=none", when="^[virtuals=fftw-api] armpl-gcc")
depends_on("cray-fftw~openmp", when="^[virtuals=fftw-api] cray-fftw")
depends_on("fftw~openmp", when="^[virtuals=fftw-api] fftw")
depends_on("fujitsu-fftw~openmp", when="^[virtuals=fftw-api] fujitsu-fftw")
depends_on("intel-mkl threads=none", when="^[virtuals=fftw-api] intel-mkl")
depends_on("intel-oneapi-mkl threads=none", when="^[virtuals=fftw-api] intel-oneapi-mkl")
depends_on(
"intel-parallel-studio threads=none", when="^[virtuals=fftw-api] intel-parallel-studio"
)
# in order to run the installed python scripts
depends_on("python", type=("build", "run"), when="+python")
depends_on("py-numpy", type=("build", "run"), when="+python")
depends_on("py-setuptools", type=("build", "run"), when="+python")
depends_on("py-h5py", type=("build", "run"), when="+hdf5+python")
depends_on("perl", type="test")
conflicts("+elpa", when="~mpi", msg="elpa is a parallel library and needs MPI support")
# Force openmp propagation on some providers of blas / fftw-api
with when("+openmp"):
depends_on("fftw+openmp", when="^fftw")
depends_on("amdfftw+openmp", when="^amdfftw")
depends_on("openblas threads=openmp", when="^openblas")
depends_on("amdblis threads=openmp", when="^amdblis")
parallel = False
def edit(self, spec, prefix):
# archive is a tar file, despite the .gz expension
tar = which("tar")
tar("-x", "-f", self.stage.archive_file, "--strip-components=1")
# get generic arch.mk template
if "+mpi" in spec:
copy(join_path(self.stage.source_path, "config", "generic.mpi.linux.mk"), "arch.mk")
else:
copy(join_path(self.stage.source_path, "config", "generic.serial.linux.mk"), "arch.mk")
if self.version == Version("2.1"):
# don't try to install missing file
filter_file("install manual.html", "#install manual.html", "Makefile")
# don't rebuild in the install and test steps
filter_file("install: all", "install:", "Makefile")
filter_file("check: all", "check:", "Makefile")
# use parallelization in tests
filter_file(
r"cd testsuite \&\& \$\(MAKE\) check$",
"cd testsuite && export BGW_TEST_MPI_NPROCS=2 OMP_NUM_THREADS=2 \
SAVETESTDIRS=yes TEMPDIRPATH=%s && \
$(MAKE) check-parallel"
% join_path(self.build_directory, "tmp"),
"Makefile",
)
# remove stack ulimit in order to run openmp tests
filter_file(
r"function run_testsuite\(\) {",
"function run_testsuite() {\nulimit -s unlimited",
"testsuite/run_testsuite.sh",
)
# slightly raise tolerance of some tests
si_epm_tests = ["Si", "Si_cplx_spin"]
if self.version >= Version("3.0"):
si_epm_tests.append("Si_hdf5")
for test in si_epm_tests:
filter_file(
"Precision : 6e-15",
"Precision : 7e-15",
join_path("testsuite", "Si-EPM", test + ".test"),
)
for test in ["Si_subspace", "Si_subspace_cplx", "Si_subspace_cplx_spin"]:
filter_file(
"Precision : 6e-15",
"Precision : 7e-15",
join_path("testsuite", "Si-EPM_subspace", test + ".test"),
)
filter_file("Precision : 8e-15", "Precision : 9e-15", "testsuite/GaAs-EPM/GaAs.test")
def build(self, spec, prefix):
buildopts = []
paraflags = []
if "+mpi" in spec:
paraflags.append("-DMPI")
# We need to copy fflags in case we append to it (#34019):
fflags = spec.compiler_flags["fflags"][:]
if "+openmp" in spec:
paraflags.append("-DOMP")
fflags.append(self.compiler.openmp_flag)
if "+mpi" in spec:
buildopts.append("C_PARAFLAG=-DPARA")
buildopts.append("PARAFLAG=%s" % " ".join(paraflags))
debugflag = ""
if "+debug" in spec:
debugflag += "-DDEBUG "
if "+verbose" in spec:
debugflag += "-DVERBOSE "
buildopts.append("DEBUGFLAG=%s" % debugflag)
if "+mpi" in spec:
buildopts.append("LINK=%s" % spec["mpi"].mpifc)
buildopts.append("C_LINK=%s" % spec["mpi"].mpicxx)
else:
buildopts.append("LINK=%s" % spack_fc)
buildopts.append("C_LINK=%s" % spack_cxx)
buildopts.append("FOPTS=%s" % " ".join(fflags))
buildopts.append("C_OPTS=%s" % " ".join(spec.compiler_flags["cflags"]))
mathflags = []
mathflags.append("-DUSEFFTW3")
buildopts.append("FFTWINCLUDE=%s" % spec["fftw-api"].prefix.include)
fftwspec = spec["fftw-api:openmp" if "+openmp" in spec else "fftw-api"]
buildopts.append("FFTWLIB=%s" % fftwspec.libs.ld_flags)
buildopts.append("LAPACKLIB=%s" % spec["lapack"].libs.ld_flags)
if "+mpi" in spec:
mathflags.append("-DUSESCALAPACK")
buildopts.append("SCALAPACKLIB=%s" % spec["scalapack"].libs.ld_flags)
if spec.satisfies("%intel"):
buildopts.append("COMPFLAG=-DINTEL")
buildopts.append("MOD_OPT=-module ")
buildopts.append("FCPP=cpp -C -P -ffreestanding")
if "+mpi" in spec:
buildopts.append("F90free=%s -free" % spec["mpi"].mpifc)
buildopts.append("C_COMP=%s" % spec["mpi"].mpicc)
buildopts.append("CC_COMP=%s" % spec["mpi"].mpicxx)
buildopts.append("BLACSDIR=%s" % spec["scalapack"].libs)
buildopts.append("BLACS=%s" % spec["scalapack"].libs.ld_flags)
else:
buildopts.append("F90free=%s -free" % spack_fc)
buildopts.append("C_COMP=%s" % spack_cc)
buildopts.append("CC_COMP=%s" % spack_cxx)
buildopts.append("FOPTS=%s" % " ".join(fflags))
elif spec.satisfies("%gcc"):
c_flags = "-std=c99"
cxx_flags = "-std=c++0x"
f90_flags = "-ffree-form -ffree-line-length-none -fno-second-underscore"
if spec.satisfies("%gcc@10:"):
c_flags += " -fcommon"
cxx_flags += " -fcommon"
f90_flags += " -fallow-argument-mismatch"
buildopts.append("COMPFLAG=-DGNU")
buildopts.append("MOD_OPT=-J ")
buildopts.append("FCPP=cpp -C -nostdinc")
if "+mpi" in spec:
buildopts.append("F90free=%s %s" % (spec["mpi"].mpifc, f90_flags))
buildopts.append("C_COMP=%s %s" % (spec["mpi"].mpicc, c_flags))
buildopts.append("CC_COMP=%s %s" % (spec["mpi"].mpicxx, cxx_flags))
else:
buildopts.append("F90free=%s %s" % (spack_fc, f90_flags))
buildopts.append("C_COMP=%s %s" % (spack_cc, c_flags))
buildopts.append("CC_COMP=%s %s" % (spack_cxx, cxx_flags))
buildopts.append("FOPTS=%s" % " ".join(fflags))
elif spec.satisfies("%fj"):
c_flags = "-std=c99"
cxx_flags = "-std=c++0x"
f90_flags = "-Free"
buildopts.append("COMPFLAG=")
buildopts.append("MOD_OPT=-module ")
buildopts.append("FCPP=cpp -C -nostdinc")
if "+mpi" in spec:
buildopts.append("F90free=%s %s" % (spec["mpi"].mpifc, f90_flags))
buildopts.append("C_COMP=%s %s" % (spec["mpi"].mpicc, c_flags))
buildopts.append("CC_COMP=%s %s" % (spec["mpi"].mpicxx, cxx_flags))
else:
buildopts.append("F90free=%s %s" % (spack_fc, f90_flags))
buildopts.append("C_COMP=%s %s" % (spack_cc, c_flags))
buildopts.append("CC_COMP=%s %s" % (spack_cxx, cxx_flags))
buildopts.append("FOPTS=-Kfast -Knotemparraystack %s" % " ".join(fflags))
else:
raise InstallError(
"Spack does not yet have support for building "
"BerkeleyGW with compiler %s" % spec.compiler
)
if "+hdf5" in spec:
mathflags.append("-DHDF5")
buildopts.append("HDF5INCLUDE=%s" % spec["hdf5"].prefix.include)
buildopts.append("HDF5LIB=%s" % spec["hdf5:hl,fortran"].libs.ld_flags)
if "+elpa" in spec:
mathflags.append("-DUSEELPA")
elpa = spec["elpa"]
if "+openmp" in spec:
elpa_suffix = "_openmp"
else:
elpa_suffix = ""
elpa_incdir = elpa.headers.directories[0]
elpa_libs = join_path(
elpa.libs.directories[0], "libelpa%s.%s" % (elpa_suffix, dso_suffix)
)
buildopts.append("ELPALIB=%s" % elpa_libs)
buildopts.append("ELPAINCLUDE=%s" % join_path(elpa_incdir, "modules"))
buildopts.append("MATHFLAG=%s" % " ".join(mathflags))
make("all-flavors", *buildopts)
def install(self, spec, prefix):
make("install", "INSTDIR=%s" % prefix)