diff --git a/env/merlin/all/packages/cp2k/package.py b/env/merlin/all/packages/cp2k/package.py new file mode 100644 index 0000000..bf65eed --- /dev/null +++ b/env/merlin/all/packages/cp2k/package.py @@ -0,0 +1,792 @@ +# Copyright 2013-2022 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) +import copy +import os +import os.path + +import spack.util.environment +from spack.package import * + + +class Cp2k(MakefilePackage, CudaPackage): + """CP2K is a quantum chemistry and solid state physics software package + that can perform atomistic simulations of solid state, liquid, molecular, + periodic, material, crystal, and biological systems + """ + + homepage = "https://www.cp2k.org" + url = "https://github.com/cp2k/cp2k/releases/download/v3.0.0/cp2k-3.0.tar.bz2" + git = "https://github.com/cp2k/cp2k.git" + list_url = "https://github.com/cp2k/cp2k/releases" + + maintainers = ["dev-zero"] + + version("2022.1", sha256="2c34f1a7972973c62d471cd35856f444f11ab22f2ff930f6ead20f3454fd228b") + version("9.1", sha256="fedb4c684a98ad857cd49b69a3ae51a73f85a9c36e9cb63e3b02320c74454ce6") + version("8.2", sha256="2e24768720efed1a5a4a58e83e2aca502cd8b95544c21695eb0de71ed652f20a") + version("8.1", sha256="7f37aead120730234a60b2989d0547ae5e5498d93b1e9b5eb548c041ee8e7772") + version("7.1", sha256="ccd711a09a426145440e666310dd01cc5772ab103493c4ae6a3470898cd0addb") + version("6.1", sha256="af803558e0a6b9e9d9ce8a3ab955ba32bacd179922455424e061c82c9fefa34b") + version("5.1", sha256="e23613b593354fa82e0b8410e17d94c607a0b8c6d9b5d843528403ab09904412") + version("4.1", sha256="4a3e4a101d8a35ebd80a9e9ecb02697fb8256364f1eccdbe4e5a85d31fe21343") + version("3.0", sha256="1acfacef643141045b7cbade7006f9b7538476d861eeecd9658c9e468dc61151") + version("master", branch="master", submodules="True") + + variant("mpi", default=True, description="Enable MPI support") + variant("openmp", default=True, description="Enable OpenMP support") + variant( + "smm", + default="libxsmm", + values=("libxsmm", "libsmm", "blas"), + description="Library for small matrix multiplications", + ) + variant("plumed", default=False, description="Enable PLUMED support") + variant( + "libint", default=True, description="Use libint, required for HFX (and possibly others)" + ) + variant("libxc", default=True, description="Support additional functionals via libxc") + variant( + "pexsi", + default=False, + description=("Enable the alternative PEXSI method" "for density matrix evaluation"), + ) + variant( + "elpa", default=False, description="Enable optimised diagonalisation routines from ELPA" + ) + variant( + "sirius", + default=False, + description=("Enable planewave electronic structure" " calculations via SIRIUS"), + ) + variant("cosma", default=False, description="Use COSMA for p?gemm") + variant( + "libvori", + default=False, + description=("Enable support for Voronoi integration" " and BQB compression"), + ) + variant("spglib", default=False, description="Enable support for spglib") + + with when("+cuda"): + variant( + "cuda_arch_35_k20x", + default=False, + description=( + "CP2K (resp. DBCSR) has specific parameter sets for" + " different GPU models. Enable this when building" + " with cuda_arch=35 for a K20x instead of a K40" + ), + ) + variant( + "cuda_fft", + default=False, + description=("Use CUDA also for FFTs in the PW part of CP2K"), + ) + variant( + "cuda_blas", + default=False, + when="@:7", # req in CP2K v8+ + description=("Use CUBLAS for general matrix operations in DBCSR"), + ) + + HFX_LMAX_RANGE = range(4, 8) + + variant( + "lmax", + description="Maximum supported angular momentum (HFX and others)", + default="5", + values=[str(x) for x in HFX_LMAX_RANGE], + multi=False, + ) + + depends_on("python", type="build") + depends_on("python@3:", when="@8:", type="build") + + depends_on("blas") + depends_on("lapack") + depends_on("fftw-api@3") + + # 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("cray-fftw+openmp", when="^cray-fftw") + depends_on("openblas threads=openmp", when="^openblas") + # The Cray compiler wrappers will automatically add libsci_mp with + # -fopenmp. Since CP2K unconditionally links blas/lapack/scalapack + # we have to be consistent. + depends_on("cray-libsci+openmp", when="^cray-libsci") + + with when("smm=libxsmm"): + depends_on("libxsmm@1.17:~header-only", when="@9.1:") + # require libxsmm-1.11+ since 1.10 can leak file descriptors in Fortran + depends_on("libxsmm@1.11:~header-only", when="@:8.9") + # use pkg-config (support added in libxsmm-1.10) to link to libxsmm + depends_on("pkgconfig", type="build") + # please set variants: smm=blas by configuring packages.yaml or install + # cp2k with option smm=blas on aarch64 + conflicts("target=aarch64:", msg="libxsmm is not available on arm") + + with when("+libint"): + # ... and in CP2K 7.0+ for linking to libint2 + depends_on("pkgconfig", type="build", when="@7.0:") + # libint & libxc are always statically linked + depends_on("libint@1.1.4:1.2", when="@3.0:6.9") + for lmax in HFX_LMAX_RANGE: + # libint2 can be linked dynamically again + depends_on( + "libint@2.6.0:+fortran tune=cp2k-lmax-{0}".format(lmax), + when="@7.0: lmax={0}".format(lmax), + ) + + with when("+libxc"): + depends_on("pkgconfig", type="build", when="@7.0:") + depends_on("libxc@2.2.2:3", when="@:5", type="build") + depends_on("libxc@4.0.3:4", when="@6.0:6.9", type="build") + depends_on("libxc@4.0.3:4", when="@7.0:8.1") + depends_on("libxc@5.1.3:5.1", when="@8.2:8") + depends_on("libxc@5.1.7:5.1", when="@9:") + + with when("+mpi"): + depends_on("mpi@2:") + depends_on("scalapack") + + with when("+cosma"): + depends_on("cosma+scalapack") + depends_on("cosma@2.5.1:", when="@9:") + depends_on("cosma+cuda", when="+cuda") + conflicts("~mpi") + # COSMA support was introduced in 8+ + conflicts("@:7") + + with when("+elpa"): + conflicts("~mpi", msg="elpa requires MPI") + depends_on("elpa+openmp", when="+openmp") + depends_on("elpa~openmp", when="~openmp") + depends_on("elpa@2011.12:2016.13", when="@:5") + depends_on("elpa@2011.12:2017.11", when="@6.0:6") + depends_on("elpa@2018.05:2020.11.001", when="@7.0:8.2") + depends_on("elpa@2021.05:", when="@8.3:") + depends_on("elpa@2021.11.001:", when="@9.1:") + + with when("+plumed"): + depends_on("plumed+shared") + depends_on("plumed+mpi", when="+mpi") + depends_on("plumed~mpi", when="~mpi") + + # while we link statically against PEXSI, its own deps may be linked in + # dynamically, therefore can't set this as pure build-type dependency. + with when("+pexsi"): + conflicts("~mpi", msg="pexsi requires MPI") + depends_on("pexsi+fortran@0.9.0:0.9", when="@:4") + depends_on("pexsi+fortran@0.10.0:", when="@5.0:") + + # only OpenMP should be consistently used, all other common things + # like ELPA, SCALAPACK are independent and Spack will ensure that + # a consistent/compatible combination is pulled into the dependency graph. + with when("+sirius"): + depends_on("sirius+fortran+shared") + depends_on("sirius+openmp", when="+openmp") + depends_on("sirius~openmp", when="~openmp") + depends_on("sirius@:6", when="@:7") + depends_on("sirius@7.0.0:7.0", when="@8:8.2") + depends_on("sirius@7.2", when="@8.3:8.9") + depends_on("sirius@7.3:", when="@9.1") + conflicts("~mpi") + # sirius support was introduced in 7+ + conflicts("@:6") + + with when("+libvori"): + depends_on("libvori@201219:", when="@8.1", type="build") + depends_on("libvori@210412:", when="@8.2:", type="build") + # libvori support was introduced in 8+ + conflicts("@:7") + + # the bundled libcusmm uses numpy in the parameter prediction (v7+) + # which is written using Python 3 + depends_on("py-numpy", when="@7:+cuda", type="build") + depends_on("python@3.6:", when="@7:+cuda", type="build") + + depends_on("spglib", when="+spglib") + + # Apparently cp2k@4.1 needs an "experimental" version of libwannier.a + # which is only available contacting the developer directly. See INSTALL + # in the stage of cp2k@4.1 + depends_on("wannier90", when="@3.0+mpi", type="build") + + # CP2K needs compiler specific compilation flags, e.g. optflags + conflicts("%apple-clang") + conflicts("%clang") + conflicts("%nag") + + conflicts("~openmp", when="@8:", msg="Building without OpenMP is not supported in CP2K 8+") + + # We only support specific cuda_archs for which we have parameter files + # for optimal kernels. Note that we don't override the cuda_archs property + # from the parent class, since the parent class defines constraints for all + # versions. Instead just mark all unsupported cuda archs as conflicting. + dbcsr_cuda_archs = ("35", "37", "60", "70") + cuda_msg = "cp2k only supports cuda_arch {0}".format(dbcsr_cuda_archs) + + for arch in CudaPackage.cuda_arch_values: + if arch not in dbcsr_cuda_archs: + conflicts("+cuda", when="cuda_arch={0}".format(arch), msg=cuda_msg) + + conflicts("+cuda", when="cuda_arch=none", msg=cuda_msg) + + # Fix 2- and 3-center integral calls to libint + patch( + "https://github.com/cp2k/cp2k/commit/5eaf864ed2bd21fb1b05a9173bb77a815ad4deda.patch?full_index=1", + sha256="3617abb877812c4b933f601438c70f95e21c6161bea177277b1d4125fd1c0bf9", + when="@8.2", + ) + + @property + def makefile_architecture(self): + return "{0.architecture}-{0.compiler.name}".format(self.spec) + + @property + def makefile_version(self): + return "{prefix}{suffix}".format( + prefix="p" if "+mpi" in self.spec else "s", + suffix="smp" if "+openmp" in self.spec else "opt", + ) + + @property + def makefile(self): + makefile_basename = ".".join([self.makefile_architecture, self.makefile_version]) + return join_path("arch", makefile_basename) + + @property + def archive_files(self): + return [join_path(self.stage.source_path, self.makefile)] + + def edit(self, spec, prefix): + pkgconf = which("pkg-config") + + if "^fftw" in spec: + fftw = spec["fftw:openmp" if "+openmp" in spec else "fftw"] + fftw_header_dir = fftw.headers.directories[0] + elif "^amdfftw" in spec: + fftw = spec["amdfftw:openmp" if "+openmp" in spec else "amdfftw"] + fftw_header_dir = fftw.headers.directories[0] + elif "^intel-mkl" in spec: + fftw = spec["intel-mkl"] + fftw_header_dir = fftw.headers.directories[0] + "/fftw" + elif "^intel-oneapi-mkl" in spec: + fftw = spec["intel-oneapi-mkl"] + fftw_header_dir = fftw.headers.directories[0] + "/fftw" + elif "^intel-parallel-studio+mkl" in spec: + fftw = spec["intel-parallel-studio"] + fftw_header_dir = "" + for incdir in [join_path(f, "fftw") for f in fftw.headers.directories]: + if os.path.exists(incdir): + fftw_header_dir = incdir + break + elif "^cray-fftw" in spec: + fftw = spec["cray-fftw"] + fftw_header_dir = fftw.headers.directories[0] + + optimization_flags = { + "gcc": [ + "-O2", + "-funroll-loops", + "-ftree-vectorize", + ], + "intel": [ + "-O2", + "-pc64", + "-unroll", + ], + "oneapi": [ + "-O2", + "-pc64", + "-unroll", + ], + "pgi": ["-fast"], + "nvhpc": ["-fast"], + "cce": ["-O2"], + "xl": ["-O3"], + "aocc": ["-O1"], + } + + dflags = ["-DNDEBUG"] + cppflags = [ + "-D__FFTW3", + "-I{0}".format(fftw_header_dir), + ] + + if "^mpi@3:" in spec: + cppflags.append("-D__MPI_VERSION=3") + elif "^mpi@2:" in spec: + cppflags.append("-D__MPI_VERSION=2") + + cflags = optimization_flags[self.spec.compiler.name][:] + cxxflags = optimization_flags[self.spec.compiler.name][:] + fcflags = optimization_flags[self.spec.compiler.name][:] + nvflags = ["-O3"] + ldflags = [] + libs = [] + gpuver = "" + + if "%intel" or "%oneapi" in spec: + cflags.append("-fp-model precise") + cxxflags.append("-fp-model precise") + fcflags += [ + "-fp-model precise", + "-heap-arrays 64", + "-g", + "-traceback", + ] + elif "%gcc" in spec: + fcflags += [ + "-ffree-form", + "-ffree-line-length-none", + "-ggdb", # make sure we get proper Fortran backtraces + ] + elif "%aocc" in spec: + fcflags += [ + "-ffree-form", + "-Mbackslash", + ] + elif "%pgi" in spec or "%nvhpc" in spec: + fcflags += ["-Mfreeform", "-Mextend"] + elif "%cce" in spec: + fcflags += ["-emf", "-ffree", "-hflex_mp=strict"] + elif "%xl" in spec: + fcflags += ["-qpreprocess", "-qstrict", "-q64"] + ldflags += ["-Wl,--allow-multiple-definition"] + + if "%gcc@10: +mpi" in spec and spec["mpi"].name in ["mpich", "cray-mpich"]: + fcflags += [ + "-fallow-argument-mismatch" + ] # https://github.com/pmodels/mpich/issues/4300 + + if "+openmp" in spec: + cflags.append(self.compiler.openmp_flag) + cxxflags.append(self.compiler.openmp_flag) + fcflags.append(self.compiler.openmp_flag) + ldflags.append(self.compiler.openmp_flag) + nvflags.append('-Xcompiler="{0}"'.format(self.compiler.openmp_flag)) + elif "%cce" in spec: # Cray enables OpenMP by default + cflags += ["-hnoomp"] + cxxflags += ["-hnoomp"] + fcflags += ["-hnoomp"] + ldflags += ["-hnoomp"] + + if "@7:" in spec: # recent versions of CP2K use C++14 CUDA code + cxxflags.append(self.compiler.cxx14_flag) + nvflags.append(self.compiler.cxx14_flag) + + ldflags.append(fftw.libs.search_flags) + + if "superlu-dist@4.3" in spec: + ldflags.insert(0, "-Wl,--allow-multiple-definition") + + if "+plumed" in self.spec: + dflags.extend(["-D__PLUMED2"]) + cppflags.extend(["-D__PLUMED2"]) + libs.extend( + [join_path(self.spec["plumed"].prefix.lib, "libplumed.{0}".format(dso_suffix))] + ) + + cc = spack_cc if "~mpi" in spec else spec["mpi"].mpicc + cxx = spack_cxx if "~mpi" in spec else spec["mpi"].mpicxx + fc = spack_fc if "~mpi" in spec else spec["mpi"].mpifc + + # Intel + if "%intel" or "%oneapi" in spec: + cppflags.extend( + [ + "-D__INTEL", + "-D__HAS_ISO_C_BINDING", + "-D__USE_CP2K_TRACE", + ] + ) + fcflags.extend(["-diag-disable 8290,8291,10010,10212,11060", "-free", "-fpp"]) + + # FFTW, LAPACK, BLAS + lapack = spec["lapack"].libs + blas = spec["blas"].libs + ldflags.append((lapack + blas).search_flags) + libs.extend([str(x) for x in (fftw.libs, lapack, blas)]) + + if any( + p in spec for p in ("^intel-mkl", "^intel-parallel-studio+mkl", "^intel-oneapi-mkl") + ): + cppflags += ["-D__MKL"] + elif "^accelerate" in spec: + cppflags += ["-D__ACCELERATE"] + + if "+cosma" in spec: + # add before ScaLAPACK to override the p?gemm symbols + cosma = spec["cosma"].libs + ldflags.append(cosma.search_flags) + libs.extend(cosma) + + # MPI + if "+mpi" in spec: + cppflags.extend(["-D__parallel", "-D__SCALAPACK"]) + + if "^intel-oneapi-mpi" in spec: + mpi = [join_path(spec["intel-oneapi-mpi"].libs.directories[0], "libmpi.so")] + else: + mpi = spec["mpi:cxx"].libs + + # while intel-mkl has a mpi variant and adds the scalapack + # libs to its libs, intel-oneapi-mkl does not. + if "^intel-oneapi-mkl" in spec: + mpi_impl = "openmpi" if "^openmpi" in spec else "intelmpi" + scalapack = [ + join_path( + spec["intel-oneapi-mkl"].libs.directories[0], "libmkl_scalapack_lp64.so" + ), + join_path( + spec["intel-oneapi-mkl"].libs.directories[0], + "libmkl_blacs_{0}_lp64.so".format(mpi_impl), + ), + ] + else: + scalapack = spec["scalapack"].libs + ldflags.append(scalapack.search_flags) + + libs.extend(scalapack) + libs.extend(mpi) + libs.extend(self.compiler.stdcxx_libs) + + if "wannier90" in spec: + cppflags.append("-D__WANNIER90") + wannier = join_path(spec["wannier90"].libs.directories[0], "libwannier.a") + libs.append(wannier) + + if "+libint" in spec: + cppflags += ["-D__LIBINT"] + + if "@:6.9" in spec: + cppflags += [ + "-D__LIBINT_MAX_AM=6", + "-D__LIBDERIV_MAX_AM1=5", + ] + + # libint-1.x.y has to be linked statically to work around + # inconsistencies in its Fortran interface definition + # (short-int vs int) which otherwise causes segfaults at + # runtime due to wrong offsets into the shared library + # symbols. + libs.extend( + [ + join_path(spec["libint"].libs.directories[0], "libderiv.a"), + join_path(spec["libint"].libs.directories[0], "libint.a"), + ] + ) + else: + fcflags += pkgconf("--cflags", "libint2", output=str).split() + libs += pkgconf("--libs", "libint2", output=str).split() + + if "+libxc" in spec: + cppflags += ["-D__LIBXC"] + + if "@:6.9" in spec: + libxc = spec["libxc:fortran,static"] + cppflags += [libxc.headers.cpp_flags] + ldflags.append(libxc.libs.search_flags) + libs.append(str(libxc.libs)) + else: + fcflags += pkgconf("--cflags", "libxcf03", output=str).split() + # some Fortran functions seem to be direct wrappers of the + # C functions such that we get a direct dependency on them, + # requiring `-lxc` to be present in addition to `-lxcf03` + libs += pkgconf("--libs", "libxcf03", "libxc", output=str).split() + + if "+pexsi" in spec: + cppflags.append("-D__LIBPEXSI") + fcflags.append("-I" + join_path(spec["pexsi"].prefix, "fortran")) + libs.extend( + [ + join_path(spec["pexsi"].libs.directories[0], "libpexsi.a"), + join_path(spec["superlu-dist"].libs.directories[0], "libsuperlu_dist.a"), + join_path( + spec["parmetis"].libs.directories[0], "libparmetis.{0}".format(dso_suffix) + ), + join_path( + spec["metis"].libs.directories[0], "libmetis.{0}".format(dso_suffix) + ), + ] + ) + + if "+elpa" in spec: + elpa = spec["elpa"] + elpa_suffix = "_openmp" if "+openmp" in elpa else "" + elpa_incdir = elpa.headers.directories[0] + + fcflags += ["-I{0}".format(join_path(elpa_incdir, "modules"))] + + # Currently AOCC support only static libraries of ELPA + if "%aocc" in spec: + libs.append( + join_path( + elpa.prefix.lib, ("libelpa{elpa_suffix}.a".format(elpa_suffix=elpa_suffix)) + ) + ) + else: + libs.append( + join_path( + elpa.libs.directories[0], + ( + "libelpa{elpa_suffix}.{dso_suffix}".format( + elpa_suffix=elpa_suffix, dso_suffix=dso_suffix + ) + ), + ) + ) + + if spec.satisfies("@:4"): + if elpa.satisfies("@:2014.5"): + cppflags.append("-D__ELPA") + elif elpa.satisfies("@2014.6:2015.10"): + cppflags.append("-D__ELPA2") + else: + cppflags.append("-D__ELPA3") + else: + cppflags.append( + "-D__ELPA={0}{1:02d}".format(elpa.version[0], int(elpa.version[1])) + ) + fcflags += ["-I{0}".format(join_path(elpa_incdir, "elpa"))] + + if "+cuda" in spec and "+cuda" in elpa: + cppflags += ["-D__ELPA_NVIDIA_GPU"] + + if spec.satisfies("+sirius"): + sirius = spec["sirius"] + cppflags.append("-D__SIRIUS") + fcflags += ["-I{0}".format(sirius.prefix.include.sirius)] + libs += list(sirius.libs) + + if spec.satisfies("+cuda"): + libs += [ + "-L{}".format(spec["cuda"].libs.directories[0]), + "-L{}/stubs".format(spec["cuda"].libs.directories[0]), + "-lcuda", + "-lcudart", + "-lnvrtc", + "-lstdc++", + ] + + if spec.satisfies("@9:"): + acc_compiler_var = "OFFLOAD_CC" + acc_flags_var = "OFFLOAD_FLAGS" + cppflags += [ + "-D__DBCSR_ACC", + "-D__GRID_CUDA", + "-DOFFLOAD_TARGET=cuda", + ] + libs += ["-lcublas"] + else: + acc_compiler_var = "NVCC" + acc_flags_var = "NVFLAGS" + cppflags += ["-D__ACC"] + if spec.satisfies("+cuda_blas"): + cppflags += ["-D__DBCSR_ACC=2"] + libs += ["-lcublas"] + else: + cppflags += ["-D__DBCSR_ACC"] + + if spec.satisfies("+cuda_fft"): + cppflags += ["-D__PW_CUDA"] + libs += ["-lcufft", "-lcublas"] + + cuda_arch = spec.variants["cuda_arch"].value[0] + if cuda_arch: + gpuver = { + "35": "K40", + "37": "K80", + "60": "P100", + "70": "V100", + }[cuda_arch] + + if cuda_arch == "35" and spec.satisfies("+cuda_arch_35_k20x"): + gpuver = "K20X" + + if "smm=libsmm" in spec: + lib_dir = join_path("lib", self.makefile_architecture, self.makefile_version) + mkdirp(lib_dir) + try: + copy(env["LIBSMM_PATH"], join_path(lib_dir, "libsmm.a")) + except KeyError: + raise KeyError( + "Point environment variable LIBSMM_PATH to " + "the absolute path of the libsmm.a file" + ) + except IOError: + raise IOError( + "The file LIBSMM_PATH pointed to does not " + "exist. Note that it must be absolute path." + ) + cppflags.extend( + [ + "-D__HAS_smm_dnn", + "-D__HAS_smm_vec", + ] + ) + libs.append("-lsmm") + + elif "smm=libxsmm" in spec: + cppflags += ["-D__LIBXSMM"] + cppflags += pkgconf("--cflags-only-other", "libxsmmf", output=str).split() + fcflags += pkgconf("--cflags-only-I", "libxsmmf", output=str).split() + libs += pkgconf("--libs", "libxsmmf", output=str).split() + + if "+libvori" in spec: + cppflags += ["-D__LIBVORI"] + libvori = spec["libvori"].libs + ldflags += [libvori.search_flags] + libs += libvori + libs += ["-lstdc++"] + + if "+spglib" in spec: + cppflags += ["-D__SPGLIB"] + spglib = spec["spglib"].libs + ldflags += [spglib.search_flags] + libs += spglib + + dflags.extend(cppflags) + cflags.extend(cppflags) + cxxflags.extend(cppflags) + fcflags.extend(cppflags) + nvflags.extend(cppflags) + + with open(self.makefile, "w") as mkf: + if "+plumed" in spec: + mkf.write( + "# include Plumed.inc as recommended by" + "PLUMED to include libraries and flags" + ) + mkf.write("include {0}\n".format(spec["plumed"].package.plumed_inc)) + + mkf.write("\n# COMPILER, LINKER, TOOLS\n\n") + mkf.write( + "FC = {0}\n" "CC = {1}\n" "CXX = {2}\n" "LD = {3}\n".format(fc, cc, cxx, fc) + ) + + if "%intel" in spec: + intel_bin_dir = ancestor(self.compiler.cc) + # CPP is a commented command in Intel arch of CP2K + # This is the hack through which cp2k developers avoid doing : + # + # ${CPP} .F > .f90 + # + # and use `-fpp` instead + mkf.write("CPP = # {0} -P\n".format(spack_cc)) + mkf.write("AR = {0}/xiar -r\n".format(intel_bin_dir)) + else: + mkf.write("CPP = # {0} -E\n".format(spack_cc)) + mkf.write("AR = ar -r\n") + + if "+cuda" in spec: + mkf.write( + "{0} = {1}\n".format( + acc_compiler_var, join_path(spec["cuda"].prefix, "bin", "nvcc") + ) + ) + + # Write compiler flags to file + def fflags(var, lst): + return "{0} = {1}\n\n".format(var, " \\\n\t".join(lst)) + + mkf.write("\n# FLAGS & LIBRARIES\n") + mkf.write(fflags("DFLAGS", dflags)) + mkf.write(fflags("CPPFLAGS", cppflags)) + mkf.write(fflags("CFLAGS", cflags)) + mkf.write(fflags("CXXFLAGS", cxxflags)) + if "+cuda" in spec: + mkf.write(fflags(acc_flags_var, nvflags)) + mkf.write(fflags("FCFLAGS", fcflags)) + mkf.write(fflags("LDFLAGS", ldflags)) + mkf.write(fflags("LIBS", libs)) + + if "%intel" or "%oneapi" in spec: + mkf.write(fflags("LDFLAGS_C", ldflags + ["-nofor-main"])) + + mkf.write("# CP2K-specific flags\n\n") + mkf.write("GPUVER = {0}\n".format(gpuver)) + mkf.write("DATA_DIR = {0}\n".format(self.prefix.share.data)) + + @property + def build_directory(self): + build_dir = self.stage.source_path + + if self.spec.satisfies("@:6"): + # prior to version 7.1 was the Makefile located in makefiles/ + build_dir = join_path(build_dir, "makefiles") + + return build_dir + + @property + def build_targets(self): + return [ + "ARCH={0}".format(self.makefile_architecture), + "VERSION={0}".format(self.makefile_version), + ] + + def build(self, spec, prefix): + if "+cuda" in spec and len(spec.variants["cuda_arch"].value) > 1: + raise InstallError("cp2k supports only one cuda_arch at a time") + + # Apparently the Makefile bases its paths on PWD + # so we need to set PWD = self.build_directory + with spack.util.environment.set_env(PWD=self.build_directory): + super(Cp2k, self).build(spec, prefix) + + with working_dir(self.build_directory): + make("libcp2k", *self.build_targets) + + def install(self, spec, prefix): + exe_dir = join_path("exe", self.makefile_architecture) + lib_dir = join_path("lib", self.makefile_architecture, self.makefile_version) + + install_tree(exe_dir, self.prefix.bin) + install_tree("data", self.prefix.share.data) + install_tree(lib_dir, self.prefix.lib) + + mkdirp(self.prefix.include) + install("src/start/libcp2k.h", join_path(self.prefix.include, "libcp2k.h")) + + @run_after("install") + def fix_package_config(self): + """ + Default build procedure generates libcp2k.pc with invalid paths, + because they are collected from temporary directory. + + Ignoring invalid paths, most library-related switches are correct + except for fftw and openblas. + + This procedure is appending two missing switches (tested with GROMACS 2022.2 + CP2K). + + In case such approach causes issues in the future, it might be necessary + to generate and override entire libcp2k.pc. + """ + if self.spec.satisfies("@9.1:"): + with open(join_path(self.prefix.lib.pkgconfig, "libcp2k.pc"), "r+") as handle: + content = handle.read().rstrip() + + content += " " + self.spec["blas"].libs.ld_flags + content += " " + self.spec["lapack"].libs.ld_flags + content += " " + self.spec["fftw-api"].libs.ld_flags + + if "^fftw+openmp" in self.spec: + content += " -lfftw3_omp" + + content += "\n" + + handle.seek(0) + handle.write(content) + + def check(self): + data_dir = join_path(self.stage.source_path, "data") + + # CP2K < 7 still uses $PWD to detect the current working dir + # and Makefile is in a subdir, account for both facts here: + with spack.util.environment.set_env(CP2K_DATA_DIR=data_dir, PWD=self.build_directory): + with working_dir(self.build_directory): + make("test", *self.build_targets) diff --git a/env/merlin/all/repo.yaml b/env/merlin/all/repo.yaml new file mode 100644 index 0000000..05b510a --- /dev/null +++ b/env/merlin/all/repo.yaml @@ -0,0 +1,2 @@ +repo: + namespace: all \ No newline at end of file diff --git a/env/merlin/all/spack.yaml b/env/merlin/all/spack.yaml index 6bf9d57..3f0757c 100644 --- a/env/merlin/all/spack.yaml +++ b/env/merlin/all/spack.yaml @@ -150,9 +150,11 @@ spack: - xmlto - xz include: - - sysconfigs/config.yaml - - sysconfigs/compilers.yaml - - sysconfigs/packages.yaml + - sysconfigs/config.yaml + - sysconfigs/compilers.yaml + - sysconfigs/packages.yaml + repos: + - . #>>> modules.yaml modules: Batchsystem: