-
Notifications
You must be signed in to change notification settings - Fork 284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add easyblock for RAxML #2180
base: develop
Are you sure you want to change the base?
add easyblock for RAxML #2180
Changes from all commits
0e8b180
5b40659
f7afebd
e2626a8
71d922e
e5f5114
8b33807
7466ce8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,194 @@ | ||||||
## | ||||||
# Copyright 2020-2023 Vrije Universiteit Brussel | ||||||
# | ||||||
# 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 <http://www.gnu.org/licenses/>. | ||||||
## | ||||||
""" | ||||||
EasyBuild support for building and installing RAxML, implemented as an easyblock | ||||||
|
||||||
@author: Alex Domingo (Vrije Universiteit Brussel) | ||||||
""" | ||||||
import os | ||||||
|
||||||
from easybuild.easyblocks.generic.makecp import MakeCp | ||||||
from easybuild.framework.easyconfig import CUSTOM | ||||||
from easybuild.tools.build_log import EasyBuildError, print_warning | ||||||
from easybuild.tools.config import build_option | ||||||
from easybuild.tools.systemtools import get_cpu_features, HAVE_ARCHSPEC | ||||||
from easybuild.tools.toolchain.compiler import OPTARCH_GENERIC | ||||||
|
||||||
RAXML_BINARY_NAME = "raxmlHPC" | ||||||
# Supported instruction sets ordered by priority (high to low) and related CPU features | ||||||
RAXML_INSTRUCT_SETS = ["AVX2", "AVX", "SSE3"] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about AVX-512? If there's a reason no to include that, add a comment on it? Also, out of curiosity, does it also support NEON (for |
||||||
RAXML_CPU_FEATURES = { | ||||||
"SSE3": ["sse3", "see4_1", "sse4_2"], | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. typo:
Suggested change
|
||||||
"AVX": ["avx", "avx1.0"], | ||||||
"AVX2": ["avx2"], | ||||||
} | ||||||
# Supported parallelization features grouped in non-MPI and MPI | ||||||
RAXML_PARALLEL_FEATURES = { | ||||||
"nompi": [None, "PTHREADS"], | ||||||
"mpi": ["MPI", "HYBRID"], | ||||||
} | ||||||
|
||||||
|
||||||
class EB_RAxML(MakeCp): | ||||||
"""Support for building and installing RAxML.""" | ||||||
|
||||||
@staticmethod | ||||||
def extra_options(extra_vars=None): | ||||||
"""Change default values of options""" | ||||||
extra = MakeCp.extra_options() | ||||||
# files_to_copy is not mandatory | ||||||
extra["files_to_copy"][2] = CUSTOM | ||||||
return extra | ||||||
|
||||||
def __init__(self, *args, **kwargs): | ||||||
"""RAxML easyblock constructor, define class variables.""" | ||||||
super(EB_RAxML, self).__init__(*args, **kwargs) | ||||||
|
||||||
def filter_optarch_features(support_features): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this a function outside of the easyblock? Same for others below. Advantage is that tests can be added easily to CI for these, and code becomes quite a bit easier to parse for humans |
||||||
""" | ||||||
Filter list of supported CPU instruction sets based on optarch string | ||||||
""" | ||||||
if not isinstance(support_features, list): | ||||||
support_features = list(support_features) | ||||||
unfilter_features = [feat.upper() for feat in support_features] | ||||||
|
||||||
optarch = build_option("optarch") or self.toolchain.options.get("optarch", "") | ||||||
# optarch is a dictionary with settings per compiler family | ||||||
if isinstance(optarch, dict): | ||||||
comp_fam = self.toolchain.comp_family() | ||||||
optarch = optarch.get(comp_fam, "") | ||||||
# convert boolean optarch settings to string equivalents | ||||||
if optarch is False: | ||||||
optarch = OPTARCH_GENERIC | ||||||
elif optarch is True: | ||||||
optarch = "" | ||||||
optarch = optarch.upper() | ||||||
|
||||||
# check for generic build | ||||||
if optarch == OPTARCH_GENERIC: | ||||||
self.log.debug("Building generic RAxML per optarch setting: %s", optarch) | ||||||
return [None] | ||||||
# check for supported labels in optarch (by given priority order) | ||||||
for instruct_set in unfilter_features: | ||||||
if instruct_set in optarch: | ||||||
dbg_msg = "Restricting supported features in RAxML to '%s' per optarch: %s" | ||||||
self.log.debug(dbg_msg, instruct_set, optarch) | ||||||
return [instruct_set] | ||||||
# anything else is unsupported | ||||||
if optarch: | ||||||
print_warning("Unsupported 'optarch' configuration setting for RAxML, ignoring: %s" % optarch) | ||||||
|
||||||
return support_features | ||||||
|
||||||
def has_cpu_feature(feature): | ||||||
""" | ||||||
Check if host CPU architecture has given feature | ||||||
""" | ||||||
if HAVE_ARCHSPEC: | ||||||
import archspec.cpu | ||||||
|
||||||
host = archspec.cpu.host() | ||||||
return feature.lower() in host | ||||||
|
||||||
try: | ||||||
return any(f in get_cpu_features() for f in RAXML_CPU_FEATURES[feature]) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be nicer with a single There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this function should be moved into framework, it's pretty generic... Same for |
||||||
except KeyError: | ||||||
raise EasyBuildError("Unknown CPU feature level for RAxML: %s", feature) | ||||||
|
||||||
def list_filename_variants(main_features, extra_features, prefix, suffix, divider): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should also go outside of easyblock, maybe rename to |
||||||
""" | ||||||
Returns list of RAxML filenames for the combination of all given features | ||||||
""" | ||||||
if not isinstance(main_features, list): | ||||||
main_features = list(main_features) | ||||||
if not isinstance(extra_features, list): | ||||||
extra_features = list(extra_features) | ||||||
|
||||||
# Permutations of features | ||||||
all_features = [(mf, xf) for mf in main_features for xf in extra_features] | ||||||
|
||||||
# Prepend/Append prefix/suffix | ||||||
file_variants = [(prefix,) + variant + (suffix,) for variant in all_features] | ||||||
file_variants = [divider.join([segment for segment in variant if segment]) for variant in file_variants] | ||||||
|
||||||
return file_variants | ||||||
|
||||||
# Filter target CPU instructions sets according to optarch | ||||||
cpu_features = filter_optarch_features(RAXML_INSTRUCT_SETS) | ||||||
|
||||||
if len(cpu_features) > 1: | ||||||
# unrestricted optimization settings, set optimization level for host micro-architecture | ||||||
cpu_features = [feat for feat in cpu_features if has_cpu_feature(feat)] | ||||||
dbg_msg = "Enabling the following CPU optimizations for RAxML by autodetection: %s" | ||||||
self.log.debug(dbg_msg, ", ".join(cpu_features)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Make this |
||||||
# add generic build | ||||||
cpu_features.append(None) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you clarify (in the comment) why this is done? |
||||||
|
||||||
# Set parallelization level of RAxML for current toolchain | ||||||
parallel_features = RAXML_PARALLEL_FEATURES["nompi"] | ||||||
if self.toolchain.options.get("usempi", None): | ||||||
parallel_features.extend(RAXML_PARALLEL_FEATURES["mpi"]) | ||||||
|
||||||
# List of builds to carry out | ||||||
self.target_makefiles = list_filename_variants(cpu_features, parallel_features, "Makefile", "gcc", ".") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is always using |
||||||
self.target_bins = list_filename_variants(parallel_features, cpu_features, RAXML_BINARY_NAME, None, "-") | ||||||
|
||||||
def build_step(self): | ||||||
"""Build all binaries of RAxML compatible with host CPU architecture""" | ||||||
|
||||||
# Compiler is manually set through 'buildopts' | ||||||
compiler = os.getenv("CC") | ||||||
compiler_nompi = compiler | ||||||
if self.toolchain.options.get("usempi", None): | ||||||
compiler_nompi = os.getenv("CC_SEQ") | ||||||
|
||||||
# Build selected RAxML makefiles | ||||||
user_buildopts = self.cfg["buildopts"] | ||||||
|
||||||
for mf in self.target_makefiles: | ||||||
cc_opt = compiler | ||||||
if not any(feature in mf for feature in RAXML_PARALLEL_FEATURES["mpi"]): | ||||||
cc_opt = compiler_nompi | ||||||
self.cfg["buildopts"] = '-f %s CC="%s" %s' % (mf, cc_opt, user_buildopts) | ||||||
self.log.debug("Building RAxML makefile with %s: %s", cc_opt, mf) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
super(EB_RAxML, self).build_step() | ||||||
|
||||||
def install_step(self): | ||||||
"""Copy files into installation directory""" | ||||||
|
||||||
self.cfg["files_to_copy"] = [ | ||||||
(self.target_bins, "bin"), | ||||||
(["README", "manual", "usefulScripts"], "share"), | ||||||
] | ||||||
super(EB_RAxML, self).install_step() | ||||||
|
||||||
def sanity_check_step(self): | ||||||
"""Custom sanity check for RAxML.""" | ||||||
|
||||||
custom_paths = { | ||||||
"files": [os.path.join("bin", x) for x in self.target_bins], | ||||||
"dirs": ["share/manual", "share/usefulScripts"], | ||||||
} | ||||||
super(EB_RAxML, self).sanity_check_step(custom_paths=custom_paths) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also include a custom sanity check command like |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure
RAXML_BINARY_NAME
is useful, since it's only used in one place currently?