Skip to content
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

Configure UM via models #139

Merged
merged 17 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions packages/um/model/vn13/rose-app.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Based on [email protected]/rose-stem/app/fcm_make_um/rose-app.conf
#meta=um-fcm-make/vn13.0

[env]
COUPLER=none
DR_HOOK=false
casim_rev=
casim_sources=
compile_atmos=preprocess-atmos build-atmos
!!compile_createbc=preprocess-createbc build-createbc
!!compile_crmstyle_coarse_grid=preprocess-crmstyle_coarse_grid build-crmstyle_coarse_grid
!!compile_pptoanc=preprocess-pptoanc build-pptoanc
compile_recon=preprocess-recon build-recon
!!compile_scm=preprocess-scm build-scm
!!compile_sstpert_lib=preprocess-sstpert_lib build-sstpert_lib
!!compile_wafccb_lib=preprocess-wafccb_lib build-wafccb_lib
config_revision=
config_root_path=fcm:um.xm_tr
config_type=atmos
eccodes=true
extract=extract
fcflags_overrides=
gwd_ussp_precision=double
jules_rev=
jules_sources=
land_surface_model=jules
ldflags_overrides_prefix=
ldflags_overrides_suffix=
ls_precipitation_precision=double
mirror=mirror
mpp_version=1C
netcdf=true
openmp=true
optimisation_level=safe
platagnostic=false
platform_config_dir=nci-x86-ifort
portio_version=2A
prebuild=
!!recon_mpi=parallel
shumlib_rev=
shumlib_sources=
socrates_rev=
socrates_sources=
stash_version=1A
thread_utils=false
timer_version=3A
ukca_rev=
ukca_sources=
um_rev=
um_sources=
File renamed without changes.
49 changes: 49 additions & 0 deletions packages/um/model/vn13p5-rns/rose-app.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#meta=um-fcm-make/vn13.5

[env]
COUPLER=none
DR_HOOK=false
casim_rev=um13.5
casim_sources=fcm:casim.xm/branches/dev/paulfield/vn1.3_drop_agg_limit_and_switch@11187
compile_atmos=preprocess-atmos build-atmos
!!compile_createbc=preprocess-createbc build-createbc
!!compile_crmstyle_coarse_grid=preprocess-crmstyle_coarse_grid build-crmstyle_coarse_grid
!!compile_pptoanc=preprocess-pptoanc build-pptoanc
compile_recon=preprocess-recon build-recon
!!compile_scm=preprocess-scm build-scm
!!compile_sstpert_lib=preprocess-sstpert_lib build-sstpert_lib
!!compile_wafccb_lib=preprocess-wafccb_lib build-wafccb_lib
[email protected]
config_root_path=fcm:um.xm_tr
config_type=atmos
eccodes=true
extract=extract
fcflags_overrides=
gwd_ussp_precision=double
jules_rev=um13.5
jules_sources=
land_surface_model=jules
ldflags_overrides_prefix=
ldflags_overrides_suffix=
ls_precipitation_precision=double
mirror=mirror
mpp_version=1C
netcdf=true
openmp=true
optimisation_level=safe
platagnostic=false
platform_config_dir=nci-x86-ifort
portio_version=2A
prebuild=
!!recon_mpi=parallel
shumlib_rev=um13.5
shumlib_sources=
socrates_rev=um13.5
socrates_sources=
stash_version=1A
thread_utils=false
timer_version=3A
ukca_rev=um13.5
ukca_sources=
um_rev=vn13.5
um_sources=fcm:um.xm/branches/dev/paulfield/vn13.5_casim_package_switches@124474
217 changes: 186 additions & 31 deletions packages/um/package.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import configparser
from spack.package import *
import llnl.util.tty as tty

class Um(Package):
"""
Expand All @@ -17,24 +18,98 @@ class Um(Package):
homepage = "https://code.metoffice.gov.uk/trac/um"
svn = "file:///g/data/ki32/mosrs/um/main/trunk"

# See 'fcm kp fcm:um.xm' for release versions
version("13.0", revision=111272, preferred=True)
version("13.1", revision=114076)
version("13.2", revision=116723)
version("13.3", revision=118802)
version("13.4", revision=120750)
version("13.5", revision=123226)
version("13.6", revision=124981)
# See 'fcm kp fcm:um.xm' for release versions.
_revision = {
"13.0": 111272,
"13.1": 114076,
"13.2": 116723,
"13.3": 118802,
"13.4": 120750,
"13.5": 123226,
"13.6": 124981}
_max_minor = 6
version("13.0", revision=_revision["13.0"], preferred=True)
for v in range(1, 1 + _max_minor):
_version = f"13.{v}"
version(_version, revision=_revision[_version])

maintainers("penguian")

variant("optim", default="safe", description="Optimization level",
values=("debug", "high", "rigorous", "safe"), multi=False)
variant("platform", default="nci-x86-ifort", description="Site platform",
values=("nci-x86-ifort", "vm-x86-gnu"), multi=False)
variant("model", default="vn13", description="Model configuration.",
values=("vn13", "vn13p0-rns", "vn13p5-rns"), multi=False)

# Bool variants have their default value set to True here.
_bool_variants = (
"eccodes",
"netcdf")
for var in _bool_variants:
variant(var, default=True, description=var)

# Off/on variants have 3-value "none" "off", "on" logic.
_off_on_variants = (
"openmp",
"platagnostic",
"thread_utils")
for var in _off_on_variants:
variant(var, default="none", description=var,
values=("none", "off", "on"), multi=False)

# String variants have their default values set to "none" here.
# The real default is set by the model.

# Revision variants.
_rev_variants = (
"casim_rev",
"jules_rev",
"shumlib_rev",
"socrates_rev",
"ukca_rev")

# Other string variants.
_other_variants = (
"casim_sources",
"compile_atmos",
"compile_createbc",
"compile_crmstyle_coarse_grid",
"compile_pptoanc",
"compile_recon",
"compile_scm",
"compile_sstpert_lib",
"compile_wafccb_lib",
"config_revision",
"config_root_path",
"config_type",
"COUPLER",
"extract",
"fcflags_overrides",
"gwd_ussp_precision",
"jules_sources",
"land_surface_model",
"ldflags_overrides_prefix",
"ldflags_overrides_suffix",
"ls_precipitation_precision",
"mirror",
"mpp_version",
"optimisation_level",
"platform_config_dir",
"portio_version",
"prebuild",
"recon_mpi",
"shumlib_sources",
"socrates_sources",
"stash_version",
"timer_version",
"ukca_sources",
"um_sources")
_str_variants = _rev_variants + _other_variants

for var in _str_variants:
variant(var, default="none", description=var, values="*", multi=False)

# The 'site=nci-gadi' variant of fcm defines the keywords
# used by the FCM configuration of UM.
depends_on("fcm site=nci-gadi", type="build")

depends_on("fcm", type="build")
# For GCOM versions, see
# https://code.metoffice.gov.uk/trac/gcom/wiki/Gcom_meto_installed_versions
depends_on("[email protected]", when="@:13.0", type=("build", "link"))
Expand All @@ -43,17 +118,16 @@ class Um(Package):
depends_on("[email protected]", when="@13.3", type=("build", "link"))
depends_on("[email protected]", when="@13.4", type=("build", "link"))
depends_on("[email protected]:", when="@13.5:", type=("build", "link"))
depends_on("eccodes +fortran +netcdf", type=("build", "link", "run"))
depends_on("[email protected]", type=("build", "link", "run"))
depends_on("eccodes +fortran +netcdf", type=("build", "link", "run"),
when="+eccodes")
depends_on("[email protected]", type=("build", "link", "run"),
when="+netcdf")

phases = ["build", "install"]

# The dependency name and the ld_flags from
# the FCM config for each library configured via FCM.
_lib_cfg = {
"DR_HOOK": {
"dep_name": "drhook",
"fcm_ld_flags": "-ldrhook"},
"eccodes": {
"dep_name": "eccodes",
"fcm_ld_flags": "-leccodes_f90 -leccodes"},
Expand All @@ -62,6 +136,15 @@ class Um(Package):
"fcm_ld_flags": "-lnetcdff -lnetcdf"}}


def _config_file_path(self, model):
"""
Return the pathname of the Rose app config file
corresponding to model.
"""
return join_path(
self.package_dir, "model", model, "rose-app.conf")


def _get_linker_args(self, spec, fcm_libname):
"""
Return the linker flags corresponding to fcm_libname,
Expand All @@ -83,36 +166,108 @@ def setup_build_environment(self, env):
"""
Set environment variables to their required values.
"""

def check_model_vs_spec(model, config_env, var, spec_value):
"""
Check whether the value spec_value for the variant var
agrees with the model's configured value config_env[var],
and produce an appropriate warning or debug message.
"""
if var not in config_env:
tty.warn(
f"The {model} model does not specify {var}. "
f"The value {spec_value} will be used.")
else:
model_value = config_env[var]
if model_value != "" and model_value != spec_value:
tty.warn(
f"The {model} model sets {var}={model_value} but "
f"the spec sets {var}={spec_value}. "
f"The value {spec_value} will be used.")


spec = self.spec
env.prepend_path("PATH", spec["fcm"].prefix.bin)

# Define CPATH for dependencies that need include files.
ideps = ["eccodes", "gcom", "netcdf-fortran"]
incs = [spec[d].prefix.include for d in ideps]
for ipath in incs:
env.prepend_path("CPATH", ipath)

# The gcom library does not contain shared objects and
# therefore must be statically linked.
env.prepend_path("LIBRARY_PATH", spec["gcom"].prefix.lib)

# Use rose-app.conf to set config options.
config = configparser.ConfigParser()
config.read(join_path(self.package_dir, "rose-app.conf"))
# Ensure that keys are case sensitive.
# https://docs.python.org/3/library/configparser.html#customizing-parser-behaviour
config.optionxform = lambda option: option
model = spec.variants["model"].value
config.read(self._config_file_path(model))

# Modify the config as per points 8 and 9 of
# https://metomi.github.io/rose/2019.01.8/html/api/configuration/rose-configuration-format.html
config_env = dict()
for key in config["env"]:
if len(key) > 0 and key[0] != '!':
value = config["env"][key].replace("\n=", "\n")
env.set(key, value)

# Override some specific environment variables
env.set("optimisation_level", spec.variants["optim"].value)
env.set("platform_config_dir", spec.variants["platform"].value)
env.set("um_rev", f"vn{spec.version}")
components = ["casim", "jules", "shumlib", "socrates", "ukca"]
for comp in components:
env.set(f"{comp}_rev", f"um{spec.version}")
config_env[key] = config["env"][key].replace("\n=", "\n")

# Override the model UM revision based on the spec UM version.
key = "um_rev"
spec_um_rev = f"vn{spec.version}"
check_model_vs_spec(model, config_env, key, spec_um_rev)
config_env[key] = spec_um_rev

# Set DR_HOOK="false" until this package is available.
config_env["DR_HOOK"] = "false"

# Override those environment variables where a bool variant is specified.
bool_to_str = lambda b: "true" if b else "false"
for var in self._bool_variants:
spec_str_value = bool_to_str(spec.variants[var].value)
check_model_vs_spec(model, config_env, var, spec_str_value)
config_env[var] = spec_str_value

# Override those environment variables where an off/on variant is specified.
off_on_to_str = lambda off_on: "true" if off_on == "on" else "false"
for var in self._off_on_variants:
spec_value = spec.variants[var].value
if spec_value != "none":
spec_str_value = off_on_to_str(spec_value)
check_model_vs_spec(model, config_env, var, spec_str_value)
config_env[var] = spec_str_value

# Override those environment variables where a revision variant is specified.
# If the variant is left unspecified, and the model does not specify a revision,
# then use a component revision based on the spec UM version.
for var in self._rev_variants:
spec_value = spec.variants[var].value
if spec_value != "none":
check_model_vs_spec(model, config_env, var, spec_value)
config_env[var] = spec_value
elif var not in config_env or config_env[var] == "":
config_env[var] = f"um{spec.version}"

# Override those environment variables where any other string variant is specified.
for var in self._other_variants:
spec_value = spec.variants[var].value
if spec_value != "none":
check_model_vs_spec(model, config_env, var, spec_value)
config_env[var] = spec_value

# Get the linker arguments for some dependencies.
for fcm_libname in ["eccodes", "netcdf"]:
linker_args = self._get_linker_args(spec, fcm_libname)
env.set(f"ldflags_{fcm_libname}_on", linker_args)
config_env[f"ldflags_{fcm_libname}_on"] = linker_args
penguian marked this conversation as resolved.
Show resolved Hide resolved

# Set environment variables based on config_env.
for key in config_env:
tty.info(f"{key}={config_env[key]}")
env.set(key, config_env[key])

# Add the location of the FCM executable to PATH.
env.prepend_path("PATH", spec["fcm"].prefix.bin)


def _build_dir(self):
Expand Down
Loading