diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index db1cc110..254e91f4 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,7 +1,8 @@ # Contributors -| GitHub user | Real Name | Affiliation | Date | -| --------------- | ---------------- | ----------- | ---------- | -| yaswant | Yaswant Pradhan | Met Office | 2026-06-30 | -| doucla | Douglas Clark | UKCEH | 2026-07-03 | -| james-bruten-mo | James Bruten | Met Office | 2026-07-09 | +| GitHub user | Real Name | Affiliation | Date | +| --------------- | ---------------- | -------------------------------- | ---------- | +| yaswant | Yaswant Pradhan | Met Office | 2026-06-30 | +| doucla | Douglas Clark | UKCEH | 2026-07-03 | +| james-bruten-mo | James Bruten | Met Office | 2026-07-09 | +| hiker | Joerg Henrichs | Bureau of Meteorology, Australia | 2026-08-18 | diff --git a/fab/fab_jules.py b/fab/fab_jules.py new file mode 100755 index 00000000..6c5a2318 --- /dev/null +++ b/fab/fab_jules.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +############################################################################## +# (c) Crown copyright Met Office. All rights reserved. +# For further details please refer to the file COPYRIGHT +# which you should have received as part of this distribution +############################################################################## + +''' +This module contains a BAF-based build script for Jules. +''' + +import argparse +import logging +from pathlib import Path +import sys +from typing import cast, Iterable, Optional, Union + + +from fab.api import (Category, FabBase, fcm_export, Exclude, find_source_files, + git_checkout, Include, root_inc_files) + +logger = logging.getLogger(__name__) + + +class JulesBuild(FabBase): + ''' + A class to build Jules using BAF as base class. + + :param str name: name of the build. + ''' + + def __init__(self, name: str): + ''' + Build Jules using Fab. + + :name: The base name to use for the project directory + ''' + # If the sources need to be checked out or not + self._checkout = False + + # The revision number to check out (if requested) + self._revision = None + + # The root of the jules repository. Defined in + # handle_command_line_options + self._root = None + + super().__init__(name) + + def define_command_line_options( + self, + parser: Optional[argparse.ArgumentParser] = None + ) -> argparse.ArgumentParser: + ''' + This adds a revision option to the command line options inherited + from the base class. + + :param Optional[argparse.ArgumentParser] parser: a pre-defined + argument parser. If not, a new instance will be created. + :returns: the argument parser with the jules specific options added. + :rtype :py:class:`argparse.ArgumentParser` + ''' + + parser = super().define_command_line_options(parser) + parser = cast(argparse.ArgumentParser, parser) + parser.add_argument( + "--revision", "-r", type=str, default="vn7.8", + help="Sets the Jules revision to checkout (only used if" + "--checkout is used). Defaults to 'vn7.8'.") + parser.add_argument( + "--checkout", default=False, action="store_true", + help="If specified, will checkout jules from git or svn, " + "otherwise this script is excepted to be in a cloned " + "git version.") + parser.add_argument( + "--rivers", default=False, action="store_true", + help="If specified, the stand-alone rivers binary will be " + "compiled.") + parser.add_argument( + "--ascii-out", default=False, action="store_true", + help="If specified, NetCDF will be disabled and output " + "will be in ASCII instead.") + return parser + + def handle_command_line_options(self, + parser: argparse.ArgumentParser) -> None: + ''' + Grab the requested (or default) arguments for checkout and Jules + revision to use and store it in an attribute. Do consistency checks + to make sure a revision is only specified if also a checkout is + requested. + + :param parser: the argument parser. + + :raises ValueError: if a revision is specified, but no checkout + is requested. + ''' + + super().handle_command_line_options(parser) + self._checkout = self.args.checkout + self._revision = self.args.revision + self._ascii_out = self.args.ascii_out + self._rivers = self.args.rivers + if self._rivers: + self.set_root_symbol("river") + + if not self._checkout and "--revision" in sys.argv[1:]: + raise ValueError(f"You specified revision '{self._revision}', " + f"but did not request a checkout.") + + def define_project_name(self, name: str) -> str: + ''' + This method adds version number, ascii output (if selected), and + MPI and OpenMP information to the project name, so these different + binaries can be distinguished. + + :returns: the project directory name to use. + ''' + if self._rivers: + name = name + "-rivers" + if self._revision: + name = name + f"-{self._revision}" + if self.args.ascii_out: + name = name + "-ascii" + if self.args.mpi: + name = name + "-mpi" + if self.args.openmp: + name = name + "-openmp" + return super().define_project_name(name) + + def grab_files_step(self) -> None: + ''' + Extracts all the required Jules source files from the repositories. + + :raises RuntimeError: if no checkout is required, but expected Jules + directory (rose-meta/jules-shared) does not exist, indicating + an invalid directory structure. + ''' + # If no checkout was requested, make sure we have the expected + # repository structure. + if not self._checkout: + # Get the root directory of this Jules: + self._root = Path(__file__).resolve().parents[1] + jules_shared = self._root / "rose-meta" / "jules-shared" + if not jules_shared.exists(): + raise RuntimeError(f"The expected directory '{jules_shared}' " + f"does not exist.") + return + + # Try to grab sources from GitHub, fallback to FCM if that fails + + try: + git_checkout( + self.config, + src="git@github.com:MetOffice/jules", + revision=self._revision, + dst_label="jules.git", + ) + self._root = (self.config.project_workspace + / "source" / "jules.git") + except Exception as e: + logging.warning(f"git_checkout failed: {e}, " + f"falling back to fcm_export") + # We export the whole svn repository, to be consistent with + # using either a local checkout or git checkout + fcm_export( + self.config, + src="fcm:jules.xm_tr", + revision=self._revision, + dst_label="jules.svn", + ) + self._root = self.config.project_workspace / "source" / "jules.svn" + + def find_source_files_step( + self, + path_filters: Optional[Iterable[Union[Exclude, Include]]] = None): + ''' + Finds all the Jules sources files to analyse. + + :param path_filters: optional list of path filters to be passed to + Fab find_source_files, default is None. + ''' + if path_filters: + local_filters = path_filters.copy() + else: + local_filters = [] + local_filters.extend([ + Exclude("src/control/um/"), + Exclude("src/initialisation/um/"), + Exclude("src/params/shared/cable_maths_constants_mod.F90"), + ] + ) + if self._rivers: + # The order is important, the last include/exclude statement + # takes precedence. So exclude standalone must come before + # including init_initial_mod + local_filters.extend([ + Exclude("src/control/standalone/jules.F90"), + Exclude("src/initialisation/standalone/"), + Include("src/initialisation/standalone/init_initial_mod.F90"), + Include("src/initialisation/standalone/initial_conditions/" + "jules_initial_mod.F90"), + Include("src/initialisation/standalone/init_output_mod.F90"), + Include("src/initialisation/standalone/init_rivers.F90"), + Include("src/initialisation/standalone/init_time_mod.F90"), + Include("src/initialisation/standalone/init_drive_mod.F90"), + Include("src/initialisation/standalone/" + "init_model_environment_mod.F90"), + Include("src/initialisation/standalone/grid/" + "fill_model_grid_arrays_mod.F90"), + Include("src/initialisation/standalone/grid/" + "init_input_grid_mod.F90"), + Include("src/initialisation/standalone/grid/" + "init_river_out_grid_mod.F90"), + Include("src/initialisation/standalone/grid/" + "init_latlon_mod.F90"), + Include("src/initialisation/standalone/grid/" + "init_land_frac_mod.F90"), + Include("src/initialisation/standalone/grid/" + "init_model_grid_mod.F90"), + Include("src/initialisation/standalone/rivers-standalone/" + "ancillaries"), + Include("src/initialisation/standalone/ancillaries/" + "init_rivers_props_mod.F90"), + Include("src/initialisation/standalone/ancillaries/" + "ancil_namelist_mod.F90"), + Include("src/initialisation/standalone/ancillaries/" + "init_ancillaries_coupling_mod.F90"), + Include("src/initialisation/standalone/ancillaries/" + "init_rivers_process_data_mod.F90"), + Include("src/initialisation/standalone/ancillaries/" + "jules_overbank_props_mod.F90"), + Include("src/initialisation/standalone/ancillaries/" + "jules_rivers_props_mod.F90"), + Exclude("src/initialisation/shared/" + "check_compatible_options_mod.F90"), + Exclude("src/control/lfric/check_unavailable_options_mod.F90"), + Exclude("src/io/dump/read_dump_mod.F90"), + Exclude("src/io/dump/write_dump_mod.F90"), + ]) + else: + local_filters.extend([ + Exclude("src/control/rivers-standalone/"), + Exclude("src/io/rivers-standalone"), + Exclude("src/initialisation/rivers-standalone/"), + Exclude("src/control/lfric/check_unavailable_options_mod.F90"), + ]) + + find_source_files(self.config, + source_root=self._root / "src", + path_filters=local_filters) + + # Add the utility files as required + # --------------------------------- + utils = self._root / "utils" + # For now assume dr hook is always disabled, so use dummy + find_source_files(self.config, + source_root=utils / "drhook_dummy") + if not self.config.mpi: + find_source_files(self.config, + source_root=utils / "mpi_dummy") + if self._ascii_out: + find_source_files(self.config, + source_root=utils / "netcdf_dummy") + + # move inc files to the root for easy tool use + root_inc_files(self.config) + + def define_preprocessor_flags_step(self) -> None: + ''' + Defines the preprocessor flags. + ''' + super().define_preprocessor_flags_step() + + # Define compiler-specific preprocessing flag + tb = self.config.tool_box + fortran = tb.get_tool(Category.FORTRAN_COMPILER) + pre_fortran = tb.get_tool(Category.FORTRAN_PREPROCESSOR) + if fortran.suite == "gnu": + pre_fortran.add_flags("-DGNU_FORTRAN") + elif fortran.suite in ["intel-classic", "intel-llvm"]: + pre_fortran.add_flags("-DINTEL_FORTRAN") + elif fortran.suite in ["cray"]: + pre_fortran.add_flags("-DCRAY_FORTRAN") + + flags = ["-I$output"] + if not self.config.mpi: + flags.append("-DMPI_DUMMY") + if self._ascii_out: + flags.append("-DNCDF_DUMMY") + + self.add_preprocessor_flags(flags) + + def get_linker_flags(self) -> list[str]: + ''' + Base class for setting linker flags. + :returns: list of flags for the linker. + ''' + libs = [] + if not self._ascii_out: + libs.extend(["netcdf"]) + return libs + + +if __name__ == "__main__": + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + + class NoFabFilter(logging.Filter): + '''A dummy class that disables all Fab noise. + ''' + def filter(self, record): + return not record.name.startswith("fab") + + # root_logger = logging.getLogger() + # root_logger.setLevel(logging.DEBUG) + last_resort_handler = logging.lastResort + last_resort_handler.setLevel(logging.DEBUG) + + fab_logger = logging.getLogger("fab") + fab_logger.setLevel(logging.DEBUG) + fab_handlers = fab_logger.handlers + # fab_handlers[0].addFilter(NoFabFilter()) + + jb = JulesBuild("jules") + jb.build() diff --git a/fab/nf_config.py b/fab/nf_config.py new file mode 100644 index 00000000..4d09c30e --- /dev/null +++ b/fab/nf_config.py @@ -0,0 +1,37 @@ +############################################################################## +# (c) Crown copyright Met Office. All rights reserved. +# For further details please refer to the file COPYRIGHT +# which you should have received as part of this distribution +############################################################################## + +"""This file contains the class to interface with NetCDF's nf-config script. +""" + +from typing import List + +from fab.tools.category import Category +from fab.tools.tool import Tool + + +class NfConfig(Tool): + '''This class interfaces with NetCDF's nf-config tool. It is not added + to the ToolRepository, it is intended for site-specific configurations + to make it easier to query for NetCDF settings. + ''' + + def __init__(self): + super().__init__("nf-config", "nf-config", Category.MISC) + + def get_compiler_flags(self) -> List[str]: + """ + :returns: the compilation flags to use for NetCDF. + """ + flags = self.run(additional_parameters=["--fflags"]) + return flags.split() + + def get_linker_flags(self) -> List[str]: + """ + :returns: the linker flags to use for NetCDF. + """ + flags = self.run(additional_parameters=["--flibs"]) + return flags.split() diff --git a/fab/site_specific/default/config.py b/fab/site_specific/default/config.py new file mode 100644 index 00000000..f0da4820 --- /dev/null +++ b/fab/site_specific/default/config.py @@ -0,0 +1,144 @@ +#! /usr/bin/env python3 + + +''' +This module contains the default Baf configuration class. +''' + +import argparse +from typing import List + +from fab.api import BuildConfig, ProfileFlags + +from site_specific.default.setup_script_cray import setup_script_cray +from site_specific.default.setup_script_gnu import setup_script_gnu +from site_specific.default.setup_script_intel_classic import ( + setup_script_intel_classic) +from site_specific.default.setup_script_intel_llvm import ( + setup_script_intel_llvm) +from site_specific.default.setup_script_nvidia import setup_script_nvidia + + +class Config: + ''' + This class is the default Configuration object for Baf builds. + It provides several callbacks which will be called from the build + scripts to allow site-specific customisations. + ''' + + def __init__(self): + self._args = None + + @property + def args(self) -> argparse.Namespace: + ''' + :returns argparse.Namespace: the command line options specified by + the user. + ''' + return self._args + + def get_valid_profiles(self) -> List[str]: + ''' + Determines the list of all allowed compiler profiles. The first + entry in this list is the default profile to be used. This method + can be overwritten by site configs to add or modify the supported + profiles. + + :returns List[str]: list of all supported compiler profiles. + ''' + return ["debug", "normal", "fast"] + + def update_toolbox(self, build_config: BuildConfig) -> None: + ''' + Set the default compiler flags for the various compiler + that are supported. + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + # First create the default compiler profiles + # Define a base profile, which contains the common + # compilation flags. This 'base' is not accessible to + # the user, so it's not part of the profile list. + ProfileFlags.define_profile("base") + for profile in self.get_valid_profiles(): + ProfileFlags.define_profile(profile, inherit_from="base") + + self.setup_intel_classic(build_config) + self.setup_intel_llvm(build_config) + self.setup_gnu(build_config) + self.setup_nvidia(build_config) + self.setup_cray(build_config) + + def handle_command_line_options(self, args: argparse.Namespace) -> None: + ''' + Additional callback function executed once all command line + options have been added. This is for example used to add + Vernier profiling flags, which are site-specific. + + :param argparse.Namespace args: the command line options added in + the site configs + ''' + # Keep a copy of the args, so they can be used when + # initialising compilers + self._args = args + + def setup_cray(self, build_config: BuildConfig) -> None: + ''' + This method sets up the Cray compiler and linker flags. + For now call an external function, since it is expected that + this configuration can be very lengthy (once we support + compiler modes). + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + setup_script_cray(build_config, self.args) + + def setup_gnu(self, build_config: BuildConfig) -> None: + ''' + This method sets up the Gnu compiler and linker flags. + For now call an external function, since it is expected that + this configuration can be very lengthy (once we support + compiler modes). + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + setup_script_gnu(build_config, self.args) + + def setup_intel_classic(self, build_config: BuildConfig) -> None: + ''' + This method sets up the Intel classic compiler and linker flags. + For now call an external function, since it is expected that + this configuration can be very lengthy (once we support + compiler modes). + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + setup_script_intel_classic(build_config, self.args) + + def setup_intel_llvm(self, build_config: BuildConfig) -> None: + ''' + This method sets up the Intel LLVM compiler and linker flags. + For now call an external function, since it is expected that + this configuration can be very lengthy (once we support + compiler modes). + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + setup_script_intel_llvm(build_config, self.args) + + def setup_nvidia(self, build_config: BuildConfig) -> None: + ''' + This method sets up the Nvidia compiler and linker flags. + For now call an external function, since it is expected that + this configuration can be very lengthy (once we support + compiler modes). + + :param build_config: the Fab build configuration instance + :type build_config: :py:class:`fab.BuildConfig` + ''' + setup_script_nvidia(build_config, self.args) diff --git a/fab/site_specific/default/setup_script_cray.py b/fab/site_specific/default/setup_script_cray.py new file mode 100644 index 00000000..fb8adbf4 --- /dev/null +++ b/fab/site_specific/default/setup_script_cray.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +''' +This file contains a function that sets the default flags for the Cray +compilers and linkers in the ToolRepository. + +This function gets called from the default site-specific config file +''' + +import argparse +from typing import cast + +from fab.api import (BuildConfig, Category, ContainFlags, Compiler, Linker, + ToolRepository) + +from nf_config import NfConfig + + +def setup_script_cray(build_config: BuildConfig, args: argparse.Namespace): + # pylint: disable=unused-argument + ''' + Defines the default flags for ftn. + + :param build_config: the Fab build config instance from which + required parameters can be taken. + :type build_config: :py:class:`fab.BuildConfig` + :param argparse.Namespace args: all command line options + ''' + + tr = ToolRepository() + ftn = tr.get_tool(Category.FORTRAN_COMPILER, "crayftn-ftn") + ftn = cast(Compiler, ftn) + + if not ftn.is_available: + return + + # The base flags + # ============== + flags = ['-M E7208,E7212', # Var used before defined + '-hlist=ad', '-hfp0', '-hflex_mp=intolerant', + '-dw', '-ec', '-eI', '-em', '-en'] + + ftn.add_flags(flags, "base") + + # File override to cut down compile times - compiler version: cce/12.0.1 + if (12, 0, 1) <= ftn.get_version() < (12, 1): + # Setting this for all modes + ftn.add_flags(ContainFlags("/model_interface_mod.", + ['-O0', '-Ovector0', '-hfp0', + '-hflex_mp=strict', '-hipa0']), + "base") + ftn.add_flags(ContainFlags("/init_prescribed_data.", ['-O1']), + "base") + + # Debug + # ===== + ftn.add_flags(["-g"], "debug") + + # Normal + # ====== + + # Production + # ========== + ftn.add_flags(["-O3"], "production") + + # Set up the linker + # ================= + linker = tr.get_tool(Category.LINKER, "linker-crayftn-ftn") + linker = cast(Linker, linker) + + # As default, use nf-config to set NetCDF linker flags. If it's not + # available (or not working properly), the site-specific setup must + # add netcdf definitions. + nf_config = NfConfig() + if nf_config.is_available: + linker.add_lib_flags("netcdf", nf_config.get_linker_flags()) diff --git a/fab/site_specific/default/setup_script_gnu.py b/fab/site_specific/default/setup_script_gnu.py new file mode 100644 index 00000000..743f1c44 --- /dev/null +++ b/fab/site_specific/default/setup_script_gnu.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 + +'''This file contains a function that sets the default flags for all +GNU based compilers in the ToolRepository. + +This function gets called from the default site-specific config file +''' + +import argparse + +from typing import cast +from fab.api import BuildConfig, Category, ContainFlags, Linker, ToolRepository + +from nf_config import NfConfig + + +def setup_script_gnu(build_config: BuildConfig, args: argparse.Namespace): + # pylint: disable=unused-argument + '''Defines the default flags for all GNU compilers. + + :para build_config: the build config from which required parameters + can be taken. + :param args: all command line options + ''' + + tr = ToolRepository() + gfortran = tr.get_tool(Category.FORTRAN_COMPILER, "gfortran") + + if not gfortran.is_available: + gfortran = tr.get_tool(Category.FORTRAN_COMPILER, "mpif90-gfortran") + if not gfortran.is_available: + return + + # The base flags + # ============== + gfortran.add_flags( + ['-std=f2003', '-fall-intrinsics', '-fmax-identifier-length=63', + '-ffree-line-length-132', '-fimplicit-none', + '-Warray-bounds'], + "base") + + if gfortran.get_version() >= (10, 0): + # Required for certain MPI versions (since gfortran version 10) + gfortran.add_flags( + ContainFlags("/read_dump_mod", "-fallow-argument-mismatch"), + "base") + + # Debug + # ===== + gfortran.add_flags(['-g', '-pg', '-ffpe-trap=invalid,zero,overflow', + '-fbacktrace', '-Wall', '-Wextra'], "debug") + + # Normal + # ====== + gfortran.add_flags(["-Werror"], "normal") + + if gfortran.get_version() >= (10, 0): + # The -Werror flags turns the warning for argument mismatch back into + # an error, since this flag comes after -fallow-argument-mismatch + # (because "normal" in inherits from "base"). So, in case of normal + # compilation mode we need to disable -Werror for this one file: + gfortran.add_flags( + ContainFlags("/read_dump_mod", "-Wno-error"), + "normal") + # Fast + # ==== + gfortran.add_flags(["-O3"], "fast") + + # Set up the linker + # ================= + linker = tr.get_tool(Category.LINKER, f"linker-{gfortran.name}") + linker = cast(Linker, linker) + + # As default, use nf-config to set NetCDF linker flags. If it's not + # available (or not working properly), the site-specific setup must + # add netcdf definitions. + nf_config = NfConfig() + if nf_config.is_available: + linker.add_lib_flags("netcdf", nf_config.get_linker_flags()) diff --git a/fab/site_specific/default/setup_script_intel_classic.py b/fab/site_specific/default/setup_script_intel_classic.py new file mode 100644 index 00000000..63df1a6f --- /dev/null +++ b/fab/site_specific/default/setup_script_intel_classic.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +'''This file contains a function that sets the default flags for all +Intel classic based compilers in the ToolRepository (ifort, icc). + +This function gets called from the default site-specific config file +''' + +import argparse +from typing import cast + +from fab.api import BuildConfig, Category, Compiler, Linker, ToolRepository + +from nf_config import NfConfig + + +def setup_script_intel_classic(build_config: BuildConfig, + args: argparse.Namespace): + # pylint: disable=unused-argument, too-many-locals + '''Defines the default flags for all Intel classic compilers. + + :para build_config: the build config from which required parameters + can be taken. + :param args: all command line options + ''' + + tr = ToolRepository() + ifort = tr.get_tool(Category.FORTRAN_COMPILER, "ifort") + ifort = cast(Compiler, ifort) + + if not ifort.is_available: + # This can happen if ifort is not in path (in spack environments). + # To support this common use case, see if mpif90-ifort is available, + # and initialise this otherwise. + ifort = tr.get_tool(Category.FORTRAN_COMPILER, "mpif90-ifort") + ifort = cast(Compiler, ifort) + if not ifort.is_available: + # Since some flags depends on version, the code below requires + # that the intel compiler actually works. + return + + # The base flags + # ============== + # The following flags will be applied to all modes: + common = ['-heap-arrays', '-std03', '-fpscomp', 'logicals', + '-traceback', + '-assume nosource_include,protect_parens', + '-fp-model', 'precise', '-no-vec'] + ifort.add_flags(common, "base") + + # Debug + # ===== + debug = ['-g', '-C', '-check', 'noarg_temp_created', '-fpe0', '-ftz', + '-ftrapuv', '-init=arrays'] + ifort.add_flags(debug, "debug") + + # Normal + # ====== + + # Fast + # ==== + ifort.add_flags(["-O3"], "fast") + + # Set up the linker + # ================= + # This will implicitly affect all ifort based linkers, e.g. + # linker-mpif90-ifort will use these flags as well. + linker = tr.get_tool(Category.LINKER, "linker-ifort") + linker = cast(Linker, linker) + + # As default, use nf-config to set NetCDF linker flags. If it's not + # available (or not working properly), the site-specific setup must + # add netcdf definitions. + nf_config = NfConfig() + if nf_config.is_available: + linker.add_lib_flags("netcdf", nf_config.get_linker_flags()) diff --git a/fab/site_specific/default/setup_script_intel_llvm.py b/fab/site_specific/default/setup_script_intel_llvm.py new file mode 100644 index 00000000..a283aca3 --- /dev/null +++ b/fab/site_specific/default/setup_script_intel_llvm.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +'''This file contains a function that sets the default flags for all +Intel classic based compilers in the ToolRepository (ifx, icx). + +This function gets called from the default site-specific config file +''' + +import argparse +from typing import cast + +from fab.api import BuildConfig, Category, Compiler, Linker, ToolRepository + +from nf_config import NfConfig + + +def setup_script_intel_llvm(build_config: BuildConfig, + args: argparse.Namespace): + # pylint: disable=unused-argument, too-many-locals + '''Defines the default flags for all Intel llvm compilers. + + :para build_config: the build config from which required parameters + can be taken. + :param args: all command line options + ''' + + tr = ToolRepository() + ifx = tr.get_tool(Category.FORTRAN_COMPILER, "ifx") + ifx = cast(Compiler, ifx) + + if not ifx.is_available: + # This can happen if ifx is not in path (in spack environments). + # To support this common use case, see if mpif90-ifx is available, + # and initialise this otherwise. + ifx = tr.get_tool(Category.FORTRAN_COMPILER, "mpif90-ifx") + ifx = cast(Compiler, ifx) + if not ifx.is_available: + # Since some flags depends on version, the code below requires + # that the intel compiler actually works. + return + + # The base flags + # ============== + # The following flags will be applied to all modes: + common = ['-heap-arrays', '-std03', '-fpscomp', 'logicals', + '-traceback', + '-assume nosource_include,protect_parens', + '-fp-model', 'precise', '-no-vec'] + ifx.add_flags(common, "base") + + # Debug + # ===== + debug = ['-g', '-C', '-check', 'noarg_temp_created', '-fpe0', '-ftz', + '-ftrapuv', '-init=arrays'] + ifx.add_flags(debug, "debug") + + # Normal + # ====== + + # Fast + # ==== + ifx.add_flags(["-O3"], "fast") + + # Set up the linker + # ================= + # This will implicitly affect all ifx based linkers, e.g. + # linker-mpif90-ifx will use these flags as well. + linker = tr.get_tool(Category.LINKER, "linker-ifx") + linker = cast(Linker, linker) + + # As default, use nf-config to set NetCDF linker flags. If it's not + # available (or not working properly), the site-specific setup must + # add netcdf definitions. + nf_config = NfConfig() + if nf_config.is_available: + linker.add_lib_flags("netcdf", nf_config.get_linker_flags()) diff --git a/fab/site_specific/default/setup_script_nvidia.py b/fab/site_specific/default/setup_script_nvidia.py new file mode 100644 index 00000000..f583d4ad --- /dev/null +++ b/fab/site_specific/default/setup_script_nvidia.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python3 + +'''This file contains a function that sets the default flags for the NVIDIA +compilers in the ToolRepository. + +This function gets called from the default site-specific config file +''' + +import argparse +from typing import cast + +from fab.api import BuildConfig, Category, Compiler, Linker, ToolRepository + + +def setup_script_nvidia(build_config: BuildConfig, args: argparse.Namespace): + # pylint: disable=unused-argument + '''Defines the default flags for nvfortran. + + :param build_config: the build config from which required parameters + can be taken. + :param args: all command line options + ''' + + tr = ToolRepository() + nvfortran = tr.get_tool(Category.FORTRAN_COMPILER, "nvfortran") + nvfortran = cast(Compiler, nvfortran) + + if not nvfortran.is_available: + nvfortran = tr.get_tool(Category.FORTRAN_COMPILER, "mpif90-nvfortran") + nvfortran = cast(Compiler, nvfortran) + if not nvfortran.is_available: + return + + # The base flags + # ============== + flags = ["-Mextend", # 132 characters line length + "-g", "-traceback", + "-r8", # Default 8 bytes reals + "-O0", # No optimisations + ] + + lib_flags = ["-c++libs"] + + # Handle accelerator options: + if args.openacc or args.openmp: + host = args.host.lower() + else: + # Neither openacc nor openmp specified + host = "" + + if args.openacc: + if host == "gpu": + flags.extend(["-acc=gpu", "-gpu=managed"]) + lib_flags.extend(["-aclibs", "-cuda"]) + else: + # CPU + flags.extend(["-acc=cpu"]) + elif args.openmp: + if host == "gpu": + flags.extend(["-mp=gpu", "-gpu=managed"]) + lib_flags.append("-cuda") + else: + # OpenMP on CPU, that's already handled by Fab + pass + + nvfortran.add_flags(flags, "base") + + # Full debug + # ========== + nvfortran.add_flags(["-O0", "-fp-model=strict"], "full-debug") + + # Fast debug + # ========== + nvfortran.add_flags(["-O2", "-fp-model=strict"], "fast-debug") + + # Production + # ========== + nvfortran.add_flags(["-O4"], "production") + + # Set up the linker + # ================= + # This will implicitly affect all nvfortran based linkers, e.g. + # linker-mpif90-nvfortran will use these flags as well. + linker = tr.get_tool(Category.LINKER, "linker-nvfortran") + linker = cast(Linker, linker) + + # ATM we don't use a shell when running a tool, and as such + # we can't directly use "$()" as parameter. So query these values using + # Fab's shell tool (doesn't really matter which shell we get, so just + # ask for the default): + shell = tr.get_default(Category.SHELL) + try: + # We must remove the trailing new line, and create a list: + nc_flibs = shell.run(additional_parameters=["-c", "nf-config --flibs"], + capture_output=True).strip().split() + except RuntimeError: + nc_flibs = [] + + linker.add_lib_flags("netcdf", nc_flibs) + linker.add_lib_flags("yaxt", ["-lyaxt", "-lyaxt_c"]) + linker.add_lib_flags("xios", ["-lxios"]) + linker.add_lib_flags("hdf5", ["-lhdf5"]) + + # Always link with C++ libs + linker.add_post_lib_flags(lib_flags)