diff --git a/packages/um/model/vn13/rose-app.conf b/packages/um/model/vn13/rose-app.conf new file mode 100644 index 0000000..6404d1a --- /dev/null +++ b/packages/um/model/vn13/rose-app.conf @@ -0,0 +1,50 @@ +# Based on um.xm_tr@vn13.0/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= diff --git a/packages/um/rose-app.conf b/packages/um/model/vn13p0-rns/rose-app.conf similarity index 100% rename from packages/um/rose-app.conf rename to packages/um/model/vn13p0-rns/rose-app.conf diff --git a/packages/um/model/vn13p5-rns/rose-app.conf b/packages/um/model/vn13p5-rns/rose-app.conf new file mode 100644 index 0000000..1ec5b2e --- /dev/null +++ b/packages/um/model/vn13p5-rns/rose-app.conf @@ -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 +config_revision=@vn13.5 +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 diff --git a/packages/um/package.py b/packages/um/package.py index 47a7d18..3ff5af0 100644 --- a/packages/um/package.py +++ b/packages/um/package.py @@ -8,6 +8,7 @@ import configparser from spack.package import * +import llnl.util.tty as tty class Um(Package): """ @@ -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("gcom@7.8", when="@:13.0", type=("build", "link")) @@ -43,17 +118,16 @@ class Um(Package): depends_on("gcom@8.1", when="@13.3", type=("build", "link")) depends_on("gcom@8.2", when="@13.4", type=("build", "link")) depends_on("gcom@8.3:", when="@13.5:", type=("build", "link")) - depends_on("eccodes +fortran +netcdf", type=("build", "link", "run")) - depends_on("netcdf-fortran@4.5.2", type=("build", "link", "run")) + depends_on("eccodes +fortran +netcdf", type=("build", "link", "run"), + when="+eccodes") + depends_on("netcdf-fortran@4.5.2", 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"}, @@ -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, @@ -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):