Skip to content

Commit

Permalink
Configure UM via models (#139)
Browse files Browse the repository at this point in the history
* Configure UM via models
* Rename the atmos model to default
* Adjust variant types and defaults
* Fix Python formatting
* Bool variants always override the model
* Remove unused import. Fix warning messages.
* Add a space to warning messages.
* Ensure that keys are case sensitive. Improve matching between model and spec.
* Rename the models to something more version specific
* Move platform_config_dir default from model to package. Improve messages.
* Remove a trailing space.
* Add model vn13p5-rns for UM vn13.5 version of Regional Nesting Suite.
* Add v13p5-rns model. Tidy up versions. Improve depends_on. Improve comments.
* Address pull request conversation items
* Fix indentation
* Simplify _rev_variants substitution.
* Fix a spelling error
  • Loading branch information
penguian authored Sep 20, 2024
1 parent 411e65b commit 3e1b31f
Show file tree
Hide file tree
Showing 4 changed files with 285 additions and 31 deletions.
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

# 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

0 comments on commit 3e1b31f

Please sign in to comment.