diff --git a/easybuild/easyblocks/q/quantumespresso.py b/easybuild/easyblocks/q/quantumespresso.py
index 563890072a6..7d86dfabeb8 100644
--- a/easybuild/easyblocks/q/quantumespresso.py
+++ b/easybuild/easyblocks/q/quantumespresso.py
@@ -367,6 +367,12 @@ def _adjust_compiler_flags(self, comp_fam):
def configure_step(self):
"""Custom configuration procedure for Quantum ESPRESSO."""
+ if LooseVersion(self.version) >= LooseVersion("7.3.1"):
+ raise EasyBuildError(
+ "QuantumESPRESSO 7.3.1 and later are not supported with the this easyblock (ConfigureMake), " +
+ "use the EB_QuantumESPRESSOcmake (CMakeMake) easyblock instead."
+ )
+
# compose list of DFLAGS (flag, value, keep_stuff)
# for guidelines, see include/defs.h.README in sources
self.dflags = []
diff --git a/easybuild/easyblocks/q/quantumespressocmake.py b/easybuild/easyblocks/q/quantumespressocmake.py
new file mode 100644
index 00000000000..70a5df031bc
--- /dev/null
+++ b/easybuild/easyblocks/q/quantumespressocmake.py
@@ -0,0 +1,405 @@
+##
+# Copyright 2009-2023 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 .
+##
+"""
+EasyBuild support for Quantum ESPRESSO, implemented as an easyblock
+
+@author: Davide Grassano (CECAM, EPFL)
+"""
+import os
+import re
+import shutil
+
+import easybuild.tools.environment as env
+from easybuild.framework.easyconfig import CUSTOM
+from easybuild.tools import LooseVersion
+from easybuild.tools.build_log import EasyBuildError
+from easybuild.tools.modules import get_software_root
+from easybuild.tools.run import run_cmd
+
+from easybuild.easyblocks.generic.cmakemake import CMakeMake
+
+
+class EB_QuantumESPRESSOcmake(CMakeMake):
+ """Support for building and installing Quantum ESPRESSO."""
+
+ TEST_SUITE_DIR = 'test-suite'
+ SUBMODULES = [
+ 'lapack',
+ 'mbd',
+ 'devxlib',
+ 'fox',
+ 'd3q',
+ 'qe-gipaw',
+ 'pw2qmcpack',
+ 'wannier90'
+ ]
+
+ @staticmethod
+ def extra_options():
+ """Custom easyconfig parameters for Quantum ESPRESSO."""
+ extra_vars = {
+ 'with_cuda': [False, 'Enable CUDA support', CUSTOM],
+ 'with_scalapack': [True, 'Enable ScaLAPACK support', CUSTOM],
+ 'with_fox': [False, 'Enable FoX support', CUSTOM],
+ 'with_gipaw': [True, 'Enable GIPAW support', CUSTOM],
+ 'with_d3q': [False, 'Enable D3Q support', CUSTOM],
+ 'with_qmcpack': [False, 'Enable QMCPACK support', CUSTOM],
+ 'test_suite_nprocs': [1, 'Number of processors to use for the test suite', CUSTOM],
+ 'test_suite_allow_failures': [
+ [],
+ 'List of test suite targets that are allowed to fail (name can partially match)',
+ CUSTOM
+ ],
+ 'test_suite_threshold': [
+ 0.97,
+ 'Threshold for test suite success rate (does count also allowed failures)',
+ CUSTOM
+ ],
+ 'test_suite_max_failed': [0, 'Maximum number of failing tests (does not count allowed failures)', CUSTOM],
+ }
+ return CMakeMake.extra_options(extra_vars)
+
+ def __init__(self, *args, **kwargs):
+ """Add extra config options specific to Quantum ESPRESSO."""
+ super(EB_QuantumESPRESSOcmake, self).__init__(*args, **kwargs)
+
+ self.install_subdir = 'qe-%s' % self.version
+
+ self.check_bins = []
+
+ def _add_toolchains_opts(self):
+ """Enable toolchain options for Quantum ESPRESSO."""
+ self._add_mpi()
+ self._add_openmp()
+ self._add_cuda()
+
+ def _add_libraries(self):
+ """Enable external libraries for Quantum ESPRESSO."""
+ self._add_scalapack()
+ self._add_fox()
+ self._add_hdf5()
+ self._add_libxc()
+ self._add_elpa()
+
+ def _add_plugins(self):
+ """Enable plugins for Quantum ESPRESSO."""
+ plugins = []
+ if self.cfg.get('with_gipaw', False):
+ plugins += self._add_gipaw()
+ if self.cfg.get('with_d3q', False):
+ plugins += self._add_d3q()
+ if self.cfg.get('with_qmcpack', False):
+ plugins += self._add_qmcpack()
+ if plugins:
+ self.cfg.update('configopts', '-DQE_ENABLE_PLUGINS="%s"' % ';'.join(plugins))
+
+ def _add_mpi(self):
+ """Enable MPI for Quantum ESPRESSO."""
+ if self.toolchain.options.get('usempi', False):
+ self.cfg.update('configopts', '-DQE_ENABLE_MPI=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_MPI=OFF')
+
+ def _add_openmp(self):
+ """Enable OpenMP for Quantum ESPRESSO."""
+ if self.toolchain.options.get('openmp', False):
+ self.cfg.update('configopts', '-DQE_ENABLE_OPENMP=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_OPENMP=OFF')
+
+ def _add_cuda(self):
+ """Enable CUDA for Quantum ESPRESSO."""
+ if self.cfg.get('with_cuda', False):
+ self.cfg.update('configopts', '-DQE_ENABLE_CUDA=ON')
+ self.cfg.update('configopts', '-DQE_ENABLE_OPENACC=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_CUDA=OFF')
+ self.cfg.update('configopts', '-DQE_ENABLE_OPENACC=OFF')
+
+ def _add_scalapack(self):
+ """Enable ScaLAPACK for Quantum ESPRESSO."""
+ if self.cfg.get('with_scalapack', False):
+ if not self.toolchain.options.get('usempi', False):
+ raise EasyBuildError('ScaLAPACK support requires MPI')
+ self.cfg.update('configopts', '-DQE_ENABLE_SCALAPACK=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_SCALAPACK=OFF')
+
+ def _add_fox(self):
+ """Enable FoX for Quantum ESPRESSO."""
+ if self.cfg.get('with_fox', False):
+ self.cfg.update('configopts', '-DQE_ENABLE_FOX=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_FOX=OFF')
+
+ def _add_hdf5(self):
+ """Enable HDF5 for Quantum ESPRESSO."""
+ if get_software_root('HDF5'):
+ self.cfg.update('configopts', '-DQE_ENABLE_HDF5=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_HDF5=OFF')
+
+ def _add_libxc(self):
+ """Enable LibXC for Quantum ESPRESSO."""
+ if get_software_root('libxc'):
+ self.cfg.update('configopts', '-DQE_ENABLE_LIBXC=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_LIBXC=OFF')
+
+ def _add_elpa(self):
+ """Enable ELPA for Quantum ESPRESSO."""
+ if get_software_root('ELPA'):
+ if not self.cfg.get('with_scalapack', False):
+ raise EasyBuildError('ELPA support requires ScaLAPACK')
+ if LooseVersion(self.version) == LooseVersion('7.3') and self.toolchain.options.get('openmp', False):
+ raise EasyBuildError('QE 7.3 with cmake does not support ELPA with OpenMP')
+ self.cfg.update('configopts', '-DQE_ENABLE_ELPA=ON')
+ else:
+ self.cfg.update('configopts', '-DQE_ENABLE_ELPA=OFF')
+
+ def _add_gipaw(self):
+ """Enable GIPAW for Quantum ESPRESSO."""
+ if LooseVersion(self.version) == LooseVersion('7.3.1'):
+ # See issue: https://github.com/dceresoli/qe-gipaw/issues/19
+ raise EasyBuildError('GIPAW will fail to compile in QE 7.3.1')
+ res = ['gipaw']
+ self.check_bins += ['gipaw.x']
+ return res
+
+ def _add_d3q(self):
+ """Enable D3Q for Quantum ESPRESSO."""
+ if LooseVersion(self.version) <= LooseVersion('7.3.1'):
+ # See issues:
+ # https://gitlab.com/QEF/q-e/-/issues/666
+ # https://github.com/anharmonic/d3q/issues/13
+ if not os.path.exists(os.path.join(self.builddir, self.install_subdir, 'external', 'd3q', '.git')):
+ raise EasyBuildError(
+ 'D3Q compilation will fail for QE 7.3 and 7.3.1 without submodule downloaded via' +
+ 'sources in easyconfig.'
+ )
+ if not self.toolchain.options.get('usempi', False):
+ raise EasyBuildError('D3Q support requires MPI enabled')
+ res = ['d3q']
+ self.check_bins += [
+ 'd3_asr3.x', 'd3_db.x', 'd3_import_shengbte.x', 'd3_interpolate2.x', 'd3_lw.x', 'd3_q2r.x',
+ 'd3_qha.x', 'd3_qq2rr.x', 'd3q.x', 'd3_r2q.x', 'd3_recenter.x', 'd3_rmzeu.x', 'd3_sparse.x',
+ 'd3_sqom.x', 'd3_tk.x',
+ ]
+ return res
+
+ def _add_qmcpack(self):
+ """Enable QMCPACK for Quantum ESPRESSO."""
+ res = ['pw2qmcpack']
+ self.check_bins += ['pw2qmcpack.x']
+ return res
+
+ def _copy_submodule_dirs(self):
+ """Copy submodule dirs downloaded by EB into XXX/external"""
+ for submod in self.SUBMODULES:
+ src = os.path.join(self.builddir, submod)
+ dst = os.path.join(self.builddir, self.install_subdir, 'external', submod)
+
+ if os.path.exists(src):
+ self.log.info('Copying submodule %s into %s' % (submod, dst))
+ # Remove empty directories and replace them with the downloaded submodule
+ if os.path.exists(dst):
+ shutil.rmtree(dst)
+ shutil.move(src, dst)
+
+ # Trick QE to think that the submodule is already installed in case `keep_git_dir` is not used in
+ # the easyconfig file
+ gitf = os.path.join(dst, '.git')
+ if not os.path.exists(gitf):
+ os.mkdir(gitf)
+ else:
+ self.log.warning('Submodule %s not found at %s' % (submod, src))
+
+ def configure_step(self):
+ """Custom configuration procedure for Quantum ESPRESSO."""
+
+ if LooseVersion(self.version) < LooseVersion('7.3'):
+ raise EasyBuildError('EB QuantumEspresso with cmake is implemented for versions >= 7.3')
+
+ # Needs to be before other functions that could check existance of .git for submodules to
+ # make compatibility checks
+ self._copy_submodule_dirs()
+
+ self._add_toolchains_opts()
+ self._add_libraries()
+ self._add_plugins()
+
+ # Enable/configure test suite
+ self._test_nprocs = self.cfg.get('test_suite_nprocs', 1)
+ self.cfg.update('configopts', '-DQE_ENABLE_TEST=ON')
+ self.cfg.update('configopts', '-DTESTCODE_NPROCS=%d' % self._test_nprocs)
+
+ # Change format of timings to seconds only (from d/h/m/s)
+ self.cfg.update('configopts', '-DQE_CLOCK_SECONDS=ON')
+
+ if LooseVersion(self.version) <= LooseVersion('7.3.1'):
+ # Needed to avoid a `DSO missing from command line` linking error
+ # https://gitlab.com/QEF/q-e/-/issues/667
+ if self.cfg.get('build_shared_libs', False):
+ ldflags = os.getenv('LDFLAGS', '')
+ ldflags += ' -Wl,--copy-dt-needed-entries '
+ env.setvar('LDFLAGS', ldflags)
+
+ super(EB_QuantumESPRESSOcmake, self).configure_step()
+
+ def test_step(self):
+ """
+ Test the compilation using Quantum ESPRESSO's test suite.
+ ctest -j NCONCURRENT (NCONCURRENT = max (1, PARALLEL / NPROCS))
+ """
+
+ thr = self.cfg.get('test_suite_threshold', 0.97)
+ concurrent = max(1, self.cfg.get('parallel', 1) // self._test_nprocs)
+ allow_fail = self.cfg.get('test_suite_allow_failures', [])
+
+ cmd = ' '.join([
+ 'ctest',
+ '-j%d' % concurrent,
+ '--output-on-failure',
+ ])
+
+ (out, _) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False)
+
+ # Example output:
+ # 74% tests passed, 124 tests failed out of 481
+ rgx = r'^ *(?P\d+)% tests passed, +(?P\d+) +tests failed out of +(?P\d+)'
+ mch = re.search(rgx, out, re.MULTILINE)
+ if not mch:
+ raise EasyBuildError('Failed to parse test suite output')
+
+ perc = int(mch.group('perc')) / 100
+ num_fail = int(mch.group('failed'))
+ total = int(mch.group('total'))
+ passed = total - num_fail
+ failures = [] # list of tests that failed, to be logged at the end
+
+ # Example output for reported failures:
+ # 635/635 Test #570: system--epw_wfpt-correctness ......................................***Failed 3.52 sec
+ self.log.debug('Test suite output:')
+ self.log.debug(out)
+ for line in out.splitlines():
+ if '***Failed' in line:
+ for allowed in allow_fail:
+ if allowed in line:
+ self.log.info('Ignoring failure: %s' % line)
+ break
+ else:
+ failures.append(line)
+ self.log.warning(line)
+
+ # Allow for flaky tests (eg too strict thresholds on results for structure relaxation)
+ num_fail = len(failures)
+ num_fail_thr = self.cfg.get('test_suite_max_failed', 0)
+ self.log.info('Total tests passed %d out of %d (%.2f%%)' % (passed, total, perc * 100))
+ if failures:
+ self.log.warning('The following tests failed (and are not ignored):')
+ for failure in failures:
+ self.log.warning('| ' + failure)
+ if perc < thr:
+ raise EasyBuildError(
+ 'Test suite failed with less than %.2f %% (%.2f) success rate' % (thr * 100, perc * 100)
+ )
+ if num_fail > num_fail_thr:
+ raise EasyBuildError(
+ 'Test suite failed with %d non-ignored failures (%d failures permitted)' % (num_fail, num_fail_thr)
+ )
+
+ return out
+
+ def sanity_check_step(self):
+ """Custom sanity check for Quantum ESPRESSO."""
+
+ targets = self.cfg['buildopts'].split()
+
+ # Condition for all targets being build 'make' or 'make all_currents'
+ all_cond = len(targets) == 0 or 'all_currents' in targets
+ pwall_cond = 'pwall' in targets
+
+ # Standard binaries
+ if all_cond or 'cp' in targets:
+ self.check_bins += ['cp.x', 'cppp.x', 'manycp.x', 'wfdd.x']
+
+ if all_cond or 'epw' in targets:
+ self.check_bins += ['epw.x']
+
+ if all_cond or 'gwl' in targets:
+ self.check_bins += [
+ 'abcoeff_to_eps.x', 'bse_main.x', 'graph.x', 'gww_fit.x', 'gww.x', 'head.x', 'memory_pw4gww.x',
+ 'pw4gww.x', 'simple_bse.x', 'simple_ip.x', 'simple.x'
+ ]
+
+ if all_cond or 'hp' in targets:
+ self.check_bins += ['hp.x']
+
+ if all_cond or 'ld1' in targets:
+ self.check_bins += ['ld1.x']
+
+ if all_cond or pwall_cond or 'neb' in targets:
+ self.check_bins += ['neb.x', 'path_interpolation.x']
+
+ if all_cond or pwall_cond or 'ph' in targets:
+ self.check_bins += [
+ 'alpha2f.x', 'dynmat.x', 'fd_ef.x', 'fd.x', 'lambda.x', 'phcg.x', 'postahc.x', 'q2r.x', 'dvscf_q2r.x',
+ 'epa.x', 'fd_ifc.x', 'fqha.x', 'matdyn.x', 'ph.x', 'q2qstar.x'
+ ]
+
+ if all_cond or pwall_cond or 'pp' in targets:
+ self.check_bins += [
+ 'average.x', 'dos_sp.x', 'ef.x', 'fermi_int_0.x', 'fermi_proj.x', 'fs.x', 'molecularpdos.x',
+ 'pawplot.x', 'plotband.x', 'plotrho.x', 'ppacf.x', 'pp.x', 'pw2bgw.x', 'pw2gt.x', 'pw2wannier90.x',
+ 'wannier_ham.x', 'wfck2r.x', 'bands.x', 'dos.x', 'epsilon.x', 'fermi_int_1.x', 'fermi_velocity.x',
+ 'initial_state.x', 'open_grid.x', 'plan_avg.x', 'plotproj.x', 'pmw.x', 'pprism.x', 'projwfc.x',
+ 'pw2critic.x', 'pw2gw.x', 'sumpdos.x', 'wannier_plot.x'
+ ]
+
+ if all_cond or pwall_cond or 'pw' in targets:
+ self.check_bins += [
+ 'cell2ibrav.x', 'ev.x', 'ibrav2cell.x', 'kpoints.x', 'pwi2xsf.x', 'pw.x', 'scan_ibrav.x'
+ ]
+
+ if all_cond or pwall_cond or 'pwcond' in targets:
+ self.check_bins += ['pwcond.x']
+
+ if all_cond or 'tddfpt' in targets:
+ self.check_bins += [
+ 'turbo_davidson.x', 'turbo_eels.x', 'turbo_lanczos.x', 'turbo_magnon.x', 'turbo_spectrum.x'
+ ]
+
+ if all_cond or 'upf' in targets:
+ self.check_bins += ['upfconv.x', 'virtual_v2.x']
+
+ if all_cond or 'xspectra' in targets:
+ self.check_bins += ['molecularnexafs.x', 'spectra_correction.x', 'xspectra.x']
+
+ custom_paths = {
+ 'files': [os.path.join('bin', x) for x in self.check_bins],
+ 'dirs': []
+ }
+
+ super(EB_QuantumESPRESSOcmake, self).sanity_check_step(custom_paths=custom_paths)