Skip to content
Snippets Groups Projects
Select Git revision
  • 64744532df56dc48ce1cf61c970f4f79e634779c
  • main default protected
  • instances/2025_05
  • instances/2024_11
4 results

using-gpus.md

Blame
  • cpmd.py 14.00 KiB
    ##
    # Copyright 2016 Landcare Research NZ Ltd
    #
    # 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://vscentrum.be/nl/en),
    # the Hercules foundation (http://www.herculesstichting.be/in_English)
    # and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en).
    #
    # http://github.com/hpcugent/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 CPMD, implemented as an easyblock
    
    @author: Benjamin Roberts (Landcare Research NZ Ltd)
    @author: Damian Alvarez (Forschungszentrum Juelich GmbH)
    """
    
    
    #August 27, 2021 (tm)
    #configuration and build step are done incorrectly
    #generating the preprocessed source and object files in installdir/obj
    #the pseudo-fix is to remove installdir/obj as postinstallcmds from the easyconfig file
    #the documentation was missing and is now added in a somewhat weird procedure
    #to the builddir and subsequently copied to the installdir/doc via postinstallcmds from the easyconfig file
    #the hybrid MPI/OMP version can solely installed through the openmp toolchain option
    #the cuda implementation is ignored
    #
    
    from distutils.version import LooseVersion
    import glob
    import os
    import platform
    import re
    import shutil
    import sys
    
    from easybuild.easyblocks.generic.configuremake import ConfigureMake
    from easybuild.framework.easyconfig import CUSTOM
    from easybuild.tools.build_log import EasyBuildError
    import easybuild.tools.environment as env
    from easybuild.tools.filetools import apply_regex_substitutions, extract_file
    from easybuild.tools.modules import get_software_root, get_software_version
    from easybuild.tools.run import run_cmd
    from easybuild.tools.systemtools import get_os_type
    import easybuild.tools.toolchain as toolchain
    
    class EB_CPMD(ConfigureMake):
        """
        Support for building CPMD
        """
    
        @staticmethod
        def extra_options():
            """Custom easyconfig parameters for CPMD."""
            extra_vars = {
                'base_configuration': [None, "Base configuration from which to start (file name)", CUSTOM],
            }
            return ConfigureMake.extra_options(extra_vars)
    
        def prepare_step(self):
            super(EB_CPMD, self).prepare_step()
    
            # create install directory and make sure it does not get cleaned up again in the install step;
            # the first configure iteration already puts things in place in the install directory,
            # so that shouldn't get cleaned up afterwards...
            self.log.info("Creating install dir %s before starting configure-build-install iterations", self.installdir)
            self.make_installdir()
            self.cfg['keeppreviousinstall'] = True
    
        def configure_step(self, cmd_prefix=''):
            """
            Configure step
            """
    
            config_file_candidates = []
    
            for confdirname in ["configure", "CONFIGURE"]:
                config_file_prefix = os.path.join(self.builddir, "CPMD", confdirname)
                if os.path.isdir(config_file_prefix):
                    break
            else:
                raise EasyBuildError("No directory containing configuration files. Please review source tarball contents, and amend the EasyBlock if necessary")
    
            # Work out a starting configuration file if one is not supplied in the easyconfig
            if self.cfg['base_configuration']:
                config_file_base = self.cfg['base_configuration']
            else:
                os_type_mappings = {
                    "LINUX"  : "LINUX",
                    "DARWIN" : "MACOSX",
                }
                os_type = os_type_mappings[get_os_type().upper()]
                machine = ""
                if os_type != "MACOSX":
                    machine = platform.machine().upper()
           
                config_file_base = os_type
                if len(machine) > 0:
                    config_file_base += "-" + machine
                
                if self.toolchain.comp_family() in [toolchain.INTELCOMP]:
                    config_file_base += "-INTEL"
    
                # enable MPI support if desired
                if self.toolchain.options.get('usempi', None):
                    config_file_base += "-MPI"
               
                # Note that the -FFTW and -FFTW3 options are always at the end
                # of the configuration file name, so this block needs to come
                # last within the "else".
                # Also, only version 3 or greater of FFTW is useful for CPMD.
                if get_software_root('imkl') or (get_software_root('FFTW') and LooseVersion(get_software_version('FFTW')) >= LooseVersion('3.0')):
                    config_file_base += "-FFTW"
                    config_file_candidates.append(config_file_base + "3")
    
            config_file_candidates.append(config_file_base)
    
            selected_base_config = None
            selected_full_config = None
            for cfc in config_file_candidates:
                self.log.info("Trying configuration file: %s", cfc)
                config_file_full = os.path.join(config_file_prefix, cfc)
                if os.path.isfile(config_file_full):
                    selected_base_config = cfc
                    selected_full_config = config_file_full
                    self.log.info("Selected %s as base configuration file", cfc)
                    break
    
            if selected_base_config is None:
                raise EasyBuildError("Base configuration file does not exist. Please edit base_configuration or review the CPMD easyblock.")
    
            try:
                apply_regex_substitutions(selected_full_config, [
                    # Better to get CC and FC from the EasyBuild environment in this instance
                    (r"^(\s*CC=.*)", r"#\1"),
                    (r"^(\s*FC=.*)", r"#\1"),
                    (r"^(\s*LD)=.*", r"\1='$(FC)'"),
                ])
            except IOError as err:
                raise EasyBuildError("Failed to patch %s: %s", selected_base_config, err)
    
            if self.cfg['configure_cmd_prefix']:
                if cmd_prefix:
                    tup = (cmd_prefix, self.cfg['configure_cmd_prefix'])
                    self.log.debug("Specified cmd_prefix '%s' is overruled by configure_cmd_prefix '%s'" % tup)
                cmd_prefix = self.cfg['configure_cmd_prefix']
    
            if self.cfg['tar_config_opts']:
                # setting am_cv_prog_tar_ustar avoids that configure tries to figure out
                # which command should be used for tarring/untarring
                # am__tar and am__untar should be set to something decent (tar should work)
                tar_vars = {
                    'am__tar': 'tar chf - "$$tardir"',
                    'am__untar': 'tar xf -',
                    'am_cv_prog_tar_ustar': 'easybuild_avoid_ustar_testing'
                }
                for (key, val) in tar_vars.items():
                    self.cfg.update('preconfigopts', "%s='%s'" % (key, val))
    
            options = [self.cfg['configopts']]
    
            # enable OpenMP support if desired
            if self.toolchain.options.get('openmp', None) and LooseVersion(self.version) >= LooseVersion('4.0'):
                options.append("-omp")
    
            # This "option" has to come last as it's the chief argument, coming after
            # all flags and so forth.
            options.append(selected_base_config)
    
            # I'm not sure when mkconfig.sh changed to configure.sh. Assuming 4.0
            # for the sake of the argument.
            if LooseVersion(self.version) >= LooseVersion('4.0'):
                config_exe = 'configure.sh'
            else:
                config_exe = 'mkconfig.sh'
                options.append('-BIN={0}'.format(os.path.join(self.installdir, "bin")))
                options.append('>')
                options.append(os.path.join(self.installdir, "Makefile"))
    
            cmd = "%(preconfigopts)s %(cmd_prefix)s./%(config_exe)s %(prefix_opt)s%(installdir)s %(configopts)s" % {
                'preconfigopts': self.cfg['preconfigopts'],
                'cmd_prefix': cmd_prefix,
                'config_exe': config_exe,
                'prefix_opt': self.cfg['prefix_opt'],
                'installdir': self.installdir,
                'configopts': ' '.join(options),
            }
    
            (out, _) = run_cmd(cmd, log_all=True, simple=False)
    
            return out
    
        def build_step(self):
    
            """
            Make some changes to files in order to make the build process more EasyBuild-friendly
            """
            os.chdir(self.installdir)
            if LooseVersion(self.version) < LooseVersion('4.0'):
                os.mkdir("bin")
            # Master configure script
            makefile = os.path.join(self.installdir, "Makefile")
            try:
                apply_regex_substitutions(makefile, [
                    (r"^(\s*LFLAGS\s*=.*[^\w-])-L/usr/lib64/atlas/([^\w-].*)$", r"\1\2"),
                    (r"^(\s*LFLAGS\s*=.*[^\w-])-llapack([^\w-].*)$", r"\1\2"),
                    (r"^(\s*LFLAGS\s*=.*[^\w-])-lblas([^\w-].*)$", r"\1\2"),
                    (r"^(\s*LFLAGS\s*=.*[^\w-])-lfftw([^\w-].*)$", r"\1\2"),
                ])
                if self.toolchain.comp_family() in [toolchain.INTELCOMP]:
                    preproc_flag = "-fpp"
                    ar_exe = "xiar -ruv"
                    apply_regex_substitutions(makefile, [
                        (r"^(\s*AR\s*=).*", r"\1 {0}".format(ar_exe))
                    ])
                    if LooseVersion(self.version) < LooseVersion('4.0'):
                        apply_regex_substitutions(makefile, [
                            (r"^(\s*CFLAGS\s*=.*[^\w-])-O2([^\w-].*)$", r"\1\2"),
                            (r"^(\s*CFLAGS\s*=.*[^\w-])-Wall([^\w-].*)$", r"\1\2"),
                            (r"^(\s*CPPFLAGS\s*=.*[^\w-])-D__PGI([^\w-].*)$", r"\1\2"),
                            (r"^(\s*CPPFLAGS\s*=.*[^\w-])-D__GNU([^\w-].*)$", r"\1\2"),
                            (r"^(\s*FFLAGS\s*=.*[^\w-])-O2([^\w-].*)$", r"\1\2"),
                            (r"^(\s*FFLAGS\s*=.*[^\w-])-fcray-pointer([^\w-].*)$", r"\1\2"),
                        ])
    
                if preproc_flag is None:
                    preproc_flag = ''
    
                apply_regex_substitutions(makefile, [
                    (r"^(\s*CPPFLAGS\s*=.*)", r"\1 {0}".format(os.getenv('CPPFLAGS'))),
                    (r"^(\s*CFLAGS\s*=.*)",   r"\1 {0}".format(os.getenv('CFLAGS'))),
                    (r"^(\s*FFLAGS\s*=.*)",   r"\1 {0}".format(os.getenv('FFLAGS'))),
                    (r"^(\s*LFLAGS\s*=.*)",   r"\1 {0}".format(os.getenv('LDFLAGS'))),
    
                    # Allow to define own XFLAGS
                    (r"# CPPFLAGS =", r"CPPFLAGS +="),
                    (r"# CFLAGS =", r"CFLAGS +="),
                    (r"# FFLAGS =", r"FFLAGS +="),
                    (r"# LFLAGS =", r"LFLAGS +="),
                    
                    # Add preprocessing options to FFLAGS and NOOPT_FLAG
                    (r"NOOPT_FLAG =", r"NOOPT_FLAG = {0}".format(preproc_flag)),
                    (r"FFLAGS =", r"FFLAGS = {0}".format(preproc_flag)),
    
                ])
                if self.toolchain.options.get('openmp', None):
                    apply_regex_substitutions(makefile, [
                        (r"^(\s*LFLAGS\s*=.*)",   r"\1 {0} {1}".format(os.getenv('LIBLAPACK_MT'), os.getenv('LIBBLAS_MT')))
                    ])
                else:
                    apply_regex_substitutions(makefile, [
                        (r"^(\s*LFLAGS\s*=.*)",   r"\1 {0} {1}".format(os.getenv('LIBLAPACK'), os.getenv('LIBBLAS')))
                    ])
                apply_regex_substitutions(makefile, [
                    (r"^(\s*LFLAGS\s*=.*)",   r"\1 {0}".format(os.getenv('LIBFFT'))),
                ])
    
                if get_software_root('imkl'):
                    if LooseVersion(self.version) < LooseVersion('4.0'):
                        apply_regex_substitutions(makefile, [
                            (r"(\s+)-DFFT_FFTW(\s+)", r"\1-DFFT_DEFAULT -DINTEL_MKL\2"),
                        ])
                if LooseVersion(self.version) >= LooseVersion('4.0'):
                    apply_regex_substitutions(makefile, [
                        (r"^(\s*CC\s*=.*)",       r"#\1"),
                        (r"^(\s*FC\s*=.*)",       r"#\1"),
                    ])
            except IOError as err:
                raise EasyBuildError("Failed to patch %s: %s", makefile, err)
    
            super(EB_CPMD, self).build_step()
    
        def extract_step(self):
            """
            Unpack the source files.
            """
            for src in self.src:
                if 'pseudo' in src['name']:
                    self.log.info("Not extracting %s, treating it as pseudopotentials to be installed separately" % src['name'])
                elif 'pdf' in src['name']:
                    self.log.info("copy %(srx)s to %(self.builddir)s ")
                    cmd="cp %s %s " % (src['path'],self.builddir) 
                    self.log.info(cmd)
                    run_cmd(cmd, log_all=True)
                else:
                    self.log.info("Unpacking source %s" % src['name'])
                    srcdir = extract_file(src['path'], self.builddir, cmd=src['cmd'], extra_options=self.cfg['unpack_options'])
                    if srcdir:
                        self.src[self.src.index(src)]['finalpath'] = srcdir
                    else:
                        raise EasyBuildError("Unpacking source %s failed", src['name'])
    
        # No need for a separate install step as the software is built in situ.
        # In fact, an install step throws away the entire package.
        # However, we consider the pseudopotentials and extract them in lib/
        def install_step(self):
            """
            Unpack the pseudopotentials if listed as source
            """
            for src in self.src:
                if 'pseudo' in src['name']:
                    self.log.info("Unpacking pseudopotentials file %s" % src['name'])
                    srcdir = extract_file(src['path'], '/'.join([self.installdir, 'lib']), cmd=src['cmd'], extra_options=self.cfg['unpack_options'])
                    if srcdir:
                        self.src[self.src.index(src)]['finalpath'] = srcdir
                    else:
                        raise EasyBuildError("Unpacking source %s failed", src['name'])