From 2a7949b8602eed0e4c65280ab2aedb8055ab9f32 Mon Sep 17 00:00:00 2001 From: dougiesquire Date: Thu, 8 May 2025 22:00:25 +1000 Subject: [PATCH 1/6] mom5: update SPR for new CMake build system Dropped support for access-esm1.5 --- packages/mom5/package.py | 580 +++------------------------------------ 1 file changed, 42 insertions(+), 538 deletions(-) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index 04d0ccb2..ddd280a5 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -1,89 +1,65 @@ # Copyright Spack Project Developers. See COPYRIGHT file for details. # -# Copyright ACCESS-NRI -# # SPDX-License-Identifier: (Apache-2.0 OR MIT) -from spack.package import install, join_path, mkdirp +from spack.package import * from spack.version.version_types import GitVersion, StandardVersion -# https://spack.readthedocs.io/en/latest/build_systems/makefilepackage.html -class Mom5(MakefilePackage): +class Mom5(CMakePackage): """MOM is a numerical ocean model based on the hydrostatic primitive equations.""" homepage = "https://www.access-nri.org.au" git = "https://github.com/ACCESS-NRI/mom5.git" - submodules = True - maintainers("harshula", "penguian") + maintainers("dougiesquire", "harshula") + + # https://github.com/ACCESS-NRI/MOM5#LGPL-3.0-1-ov-file + license("LGPL-3.0-only", checked_by="dougiesquire") - version("access-om2", branch="master", preferred=True) + version("mom_solo", branch="master", preferred=True) + version("mom_sis", branch="master") + version("access-om2", branch="master") version("legacy-access-om2-bgc", branch="master") - version("access-esm1.5", branch="access-esm1.5") version("access-esm1.6", branch="master") - variant("restart_repro", default=True, description="Reproducible restart build.") + variant("build_type", default="RelWithDebInfo", + description="CMake build type", + values=("Debug", "Release", "RelWithDebInfo") + ) + variant("deterministic", default=False, description="Deterministic build") - with when("@access-esm1.6,access-om2,legacy-access-om2-bgc"): - variant("deterministic", - default=False, - description="Deterministic build.") - variant("optimisation_report", - default=False, - description="Generate optimisation reports.") + depends_on("cmake@3.18:", type="build") + depends_on("mpi") + depends_on("netcdf-c@4.7.4:") + depends_on("netcdf-fortran@4.5.2:") - with when("@access-om2,legacy-access-om2-bgc"): - depends_on("netcdf-c@4.7.4:") - depends_on("netcdf-fortran@4.5.2:") - # Depend on virtual package "mpi". - depends_on("mpi") + with when("@access-om2,access-esm1.6,legacy-access-om2-bgc"): depends_on("datetime-fortran") - depends_on("oasis3-mct+deterministic", when="+deterministic") - depends_on("oasis3-mct~deterministic", when="~deterministic") depends_on("libaccessom2+deterministic", when="+deterministic") depends_on("libaccessom2~deterministic", when="~deterministic") - - # NOTE: Spack will also match "access-om2-legacy-bgc" here, that's why - # it has been renamed to "legacy-access-om2-bgc". - with when("@access-om2"): + depends_on("oasis3-mct+deterministic", when="+deterministic") + depends_on("oasis3-mct~deterministic", when="~deterministic") depends_on("access-fms") depends_on("access-generic-tracers") - # access-esm1.5 and access-esm1.6 - with when("@access-esm1.5:access-esm1.6"): - depends_on("netcdf-c@4.7.1:") - depends_on("netcdf-fortran@4.5.1:") - # Depend on "openmpi". - depends_on("openmpi") - - with when("@access-esm1.5"): - depends_on("oasis3-mct@access-esm1.5") - - with when("@access-esm1.6"): - depends_on("oasis3-mct@access-esm1.5+deterministic", when="+deterministic") - depends_on("oasis3-mct@access-esm1.5~deterministic", when="~deterministic") - depends_on("access-fms") - depends_on("access-generic-tracers") + root_cmakelists_dir = "cmake/" - phases = ["setup", "edit", "build", "install"] + phases = ["setup", "cmake", "build", "install"] - # NOTE: The keys in the __builds variable are required to check whether + # NOTE: The keys in the __types variable are required to check whether # a valid version was passed in by the user. - __builds = { - "access-om2": {"type": "ACCESS-OM", "gtracers": True}, - "legacy-access-om2-bgc": {"type": "ACCESS-OM-BGC", "gtracers": False}, - "access-esm1.5": {"type": "ACCESS-CM", "gtracers": False}, - "access-esm1.6": {"type": "ACCESS-ESM", "gtracers": True} + __types = { + "mom_solo": "MOM5_SOLO", + "mom_sis": "MOM5_SIS", + "access-om2": "MOM5_ACCESS_OM", + "access-esm1.6": "MOM5_ACCESS_ESM", + "legacy-access-om2-bgc": "MOM5_ACCESS_OM_BGC" } __version = "INVALID" - __platform = "spack" def url_for_version(self, version): return "https://github.com/ACCESS-NRI/mom5/tarball/{0}".format(version) - - # NOTE: This functionality will hopefully be implemented in the Spack core - # in the future. Till then, this approach can be used in other SPRs - # where this functionality is required. + def setup(self, spec, prefix): if isinstance(self.version, GitVersion): @@ -94,492 +70,20 @@ def setup(self, spec, prefix): print("ERROR: version=" + self.version.string) raise ValueError - # The rest of the checks are only required if a __builds member + # The rest of the checks are only required if a __types member # variable exists - if self.__version not in self.__builds.keys(): + if self.__version not in self.__types.keys(): print("ERROR: The version must be selected from: " + - ", ".join(self.__builds.keys())) + ", ".join(self.__types.keys())) raise ValueError print("INFO: version=" + self.__version + - " type=" + self.__builds[self.__version]["type"] + - " gtracers=" + str(self.__builds[self.__version]["gtracers"])) - - def edit(self, spec, prefix): - - srcdir = self.stage.source_path - makeinc_path = join_path(srcdir, "bin", "mkmf.template.spack") - config = {} - - # NOTE: The order of the libraries matters during the linking step! - if self.__version in ["access-esm1.5", "access-esm1.6"]: - istr = " ".join([ - join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1"), - join_path((spec["oasis3-mct"].headers).cpp_flags, "mct")]) - ideps = ["netcdf-fortran"] - ldeps = ["oasis3-mct", "netcdf-c", "netcdf-fortran"] - FFLAGS_OPT = "-O3 -debug minimal -xCORE-AVX512 -align array64byte" - CFLAGS_OPT = "-O2 -debug minimal -no-vec" - else: - istr = join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1") - ideps = ["oasis3-mct", "libaccessom2", "netcdf-fortran"] - # NOTE: datetime-fortran is a dependency of libaccessom2. - ldeps = ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"] - - # TODO: https://github.com/ACCESS-NRI/ACCESS-OM/issues/12 - FFLAGS_OPT = "-g3 -O2 -xCORE-AVX2 -debug all -check none -traceback" - CFLAGS_OPT = "-O2 -debug minimal -xCORE-AVX2" - if self.spec.satisfies("+deterministic"): - FFLAGS_OPT = "-g0 -O0 -xCORE-AVX2 -debug none -check none" - CFLAGS_OPT = "-O0 -debug none -xCORE-AVX2" - print("INFO: +deterministic applied") - - if self.__builds[self.__version]["gtracers"]: - ideps.extend(["access-fms", "access-generic-tracers"]) - ldeps.extend(["access-fms", "access-generic-tracers"]) - - incs = " ".join([istr] + [(spec[d].headers).cpp_flags for d in ideps]) - libs = " ".join([(spec[d].libs).ld_flags for d in ldeps]) - - # Copied from bin/mkmf.template.ubuntu - config["gcc"] = f""" -FC = mpifort -CC = gcc -LD = $(FC) -######### -# flags # -######### -DEBUG = -REPRO = -VERBOSE = -OPENMP = - -MAKEFLAGS += --jobs=$(shell grep '^processor' /proc/cpuinfo | wc -l) - -FPPFLAGS := - -FFLAGS := -fcray-pointer -fdefault-real-8 -ffree-line-length-none -fno-range-check -Waliasing -Wampersand -Warray-bounds -Wcharacter-truncation -Wconversion -Wline-truncation -Wintrinsics-std -Wsurprising -Wno-tabs -Wunderflow -Wunused-parameter -Wintrinsic-shadow -Wno-align-commons -fallow-argument-mismatch -fallow-invalid-boz -FFLAGS += {incs} -FFLAGS += -DGFORTRAN - -# -FFLAGS_OPT = -O2 -FFLAGS_REPRO = -FFLAGS_DEBUG = -O0 -g -W -fbounds-check -FFLAGS_OPENMP = -fopenmp -FFLAGS_VERBOSE = - -CFLAGS := -D__IFC {incs} -CFLAGS += $(shell nc-config --cflags) -CFLAGS_OPT = -O2 -CFLAGS_OPENMP = -fopenmp -CFLAGS_DEBUG = -O0 -g - -# Optional Testing compile flags. Mutually exclusive from DEBUG, REPRO, and OPT -# *_TEST will match the production if no new option(s) is(are) to be tested. -FFLAGS_TEST = -O2 -CFLAGS_TEST = -O2 - -LDFLAGS := -LDFLAGS_OPENMP := -fopenmp -LDFLAGS_VERBOSE := - -ifneq ($(REPRO),) -CFLAGS += $(CFLAGS_REPRO) -FFLAGS += $(FFLAGS_REPRO) -endif -ifneq ($(DEBUG),) -CFLAGS += $(CFLAGS_DEBUG) -FFLAGS += $(FFLAGS_DEBUG) -else ifneq ($(TEST),) -CFLAGS += $(CFLAGS_TEST) -FFLAGS += $(FFLAGS_TEST) -else -CFLAGS += $(CFLAGS_OPT) -FFLAGS += $(FFLAGS_OPT) -endif - -ifneq ($(OPENMP),) -CFLAGS += $(CFLAGS_OPENMP) -FFLAGS += $(FFLAGS_OPENMP) -LDFLAGS += $(LDFLAGS_OPENMP) -endif - -ifneq ($(VERBOSE),) -CFLAGS += $(CFLAGS_VERBOSE) -FFLAGS += $(FFLAGS_VERBOSE) -LDFLAGS += $(LDFLAGS_VERBOSE) -endif - -ifeq ($(NETCDF),3) - # add the use_LARGEFILE cppdef - ifneq ($(findstring -Duse_netCDF,$(CPPDEFS)),) - CPPDEFS += -Duse_LARGEFILE - endif -endif - -LIBS := {libs} -LDFLAGS += $(LIBS) -""" - - # Copied from bin/mkmf.template.nci - if self.__version in ["access-esm1.5", "access-esm1.6"]: - config["intel"] = f""" -ifeq ($(VTRACE), yes) - FC = mpifort-vt - LD = mpifort-vt -else - FC = mpifort - LD = mpifort -endif - -CC = mpicc - -REPRO = -VERBOSE = -OPT = on - -MAKEFLAGS += --jobs=4 - -INCLUDE = {incs} - -FPPFLAGS := -fpp -Wp,-w $(INCLUDE) -FFLAGS := -fno-alias -safe-cray-ptr -fpe0 -ftz -assume byterecl -i4 -r8 -traceback -nowarn -check noarg_temp_created -assume buffered_io -convert big_endian -FFLAGS_OPT = {FFLAGS_OPT} -FFLAGS_DEBUG = -g -O0 -debug all -check -check noarg_temp_created -check nopointer -warn -warn noerrors -ftrapuv -FFLAGS_REPRO = -O2 -debug minimal -no-vec -fp-model precise -FFLAGS_VERBOSE = -v -V -what - -CFLAGS := -D__IFC $(INCLUDE) -CFLAGS_OPT = {CFLAGS_OPT} -CFLAGS_DEBUG = -O0 -g -ftrapuv -traceback - -LDFLAGS := -LDFLAGS_VERBOSE := -Wl,-V,--verbose,-cref,-M - -ifneq ($(REPRO),) -CFLAGS += $(CFLAGS_REPRO) -FFLAGS += $(FFLAGS_REPRO) -endif - -ifneq ($(DEBUG),) -CFLAGS += $(CFLAGS_DEBUG) -FFLAGS += $(FFLAGS_DEBUG) -else -CFLAGS += $(CFLAGS_OPT) -FFLAGS += $(FFLAGS_OPT) -endif - -ifneq ($(VERBOSE),) -CFLAGS += $(CFLAGS_VERBOSE) -FFLAGS += $(FFLAGS_VERBOSE) -LDFLAGS += $(LDFLAGS_VERBOSE) -endif - -LIBS := {libs} - -LDFLAGS += $(LIBS) -""" - else: - config["intel"] = f""" -ifeq ($(VTRACE), yes) - FC := mpifort-vt - LD := mpifort-vt -else - FC := mpifort - LD := mpifort -endif - -CC := mpicc - -VERBOSE := -OPT := on - -MAKEFLAGS += -j - -INCLUDE := {incs} - -FPPFLAGS := -fpp -Wp,-w $(INCLUDE) -FFLAGS := -fno-alias -safe-cray-ptr -fpe0 -ftz -assume byterecl -i4 -r8 -nowarn -check noarg_temp_created -assume nobuffered_io -convert big_endian -grecord-gcc-switches -align all -FFLAGS_OPT := {FFLAGS_OPT} -FFLAGS_REPORT := -qopt-report=5 -qopt-report-annotate -FFLAGS_DEBUG := -g3 -O0 -debug all -check -check noarg_temp_created -check nopointer -warn -warn noerrors -ftrapuv -traceback -FFLAGS_REPRO := -fp-model precise -fp-model source -align all -FFLAGS_VERBOSE := -v -V -what - -CFLAGS := -D__IFC $(INCLUDE) -CFLAGS_OPT := {CFLAGS_OPT} -CFLAGS_REPORT := -qopt-report=5 -qopt-report-annotate -CFLAGS_DEBUG := -O0 -g -ftrapuv -traceback -CFLAGS_REPRO := -fp-model precise -fp-model source - -LDFLAGS := -LDFLAGS_VERBOSE := -Wl,-V,--verbose,-cref,-M - -ifneq ($(REPRO),) -CFLAGS += $(CFLAGS_REPRO) -FFLAGS += $(FFLAGS_REPRO) -endif - -ifneq ($(DEBUG),) -CFLAGS += $(CFLAGS_DEBUG) -FFLAGS += $(FFLAGS_DEBUG) -else -CFLAGS += $(CFLAGS_OPT) -FFLAGS += $(FFLAGS_OPT) -endif - -ifneq ($(VERBOSE),) -CFLAGS += $(CFLAGS_VERBOSE) -FFLAGS += $(FFLAGS_VERBOSE) -LDFLAGS += $(LDFLAGS_VERBOSE) -endif - -ifneq ($(REPORT),) -CFLAGS += $(CFLAGS_REPORT) -FFLAGS += $(FFLAGS_REPORT) -endif - -LIBS := {libs} - -ifneq ($(OASIS_ROOT),) -LIBS += -L$(OASIS_ROOT)/Linux/lib -lpsmile.MPI1 -lmct -lmpeu -lscrip -endif - -ifneq ($(LIBACCESSOM2_ROOT),) -LIBS += -L$(LIBACCESSOM2_ROOT)/build/lib -laccessom2 -endif - -LDFLAGS += $(LIBS) -""" - - # Add support for the ifx compiler - # TODO: `.replace() is a temporary workaround for: - # icx: error: unsupported argument 'source' to option '-ffp-model=' - # The `.replace()` apparently doesn't modify the object. - config["oneapi"] = config["intel"].replace("CFLAGS_REPRO := -fp-model precise -fp-model source", "CFLAGS_REPRO := -fp-model precise") - - if self.__version in ["access-esm1.5", "access-esm1.6"]: - config["post"] = """ -# you should never need to change any lines below. - -# see the MIPSPro F90 manual for more details on some of the file extensions -# discussed here. -# this makefile template recognizes fortran sourcefiles with extensions -# .f, .f90, .F, .F90. Given a sourcefile ., where is one of -# the above, this provides a number of default actions: - -# make .opt create an optimization report -# make .o create an object file -# make .s create an assembly listing -# make .x create an executable file, assuming standalone -# source -# make .i create a preprocessed file (for .F) -# make .i90 create a preprocessed file (for .F90) - -# The macro TMPFILES is provided to slate files like the above for removal. - -RM = rm -f -SHELL = /bin/csh -f -TMPFILES = .*.m *.B *.L *.i *.i90 *.l *.s *.mod *.opt - -.SUFFIXES: .F .F90 .H .L .T .f .f90 .h .i .i90 .l .o .s .opt .x - -.f.L: - $(FC) $(FFLAGS) -c -listing $*.f -.f.opt: - $(FC) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.f -.f.l: - $(FC) $(FFLAGS) -c $(LIST) $*.f -.f.T: - $(FC) $(FFLAGS) -c -cif $*.f -.f.o: - $(FC) $(FFLAGS) -c $*.f -.f.s: - $(FC) $(FFLAGS) -S $*.f -.f.x: - $(FC) $(FFLAGS) -o $*.x $*.f *.o $(LDFLAGS) -.f90.L: - $(FC) $(FFLAGS) -c -listing $*.f90 -.f90.opt: - $(FC) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.f90 -.f90.l: - $(FC) $(FFLAGS) -c $(LIST) $*.f90 -.f90.T: - $(FC) $(FFLAGS) -c -cif $*.f90 -.f90.o: - $(FC) $(FFLAGS) -c $*.f90 -.f90.s: - $(FC) $(FFLAGS) -c -S $*.f90 -.f90.x: - $(FC) $(FFLAGS) -o $*.x $*.f90 *.o $(LDFLAGS) -.F.L: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -listing $*.F -.F.opt: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.F -.F.l: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $(LIST) $*.F -.F.T: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -cif $*.F -.F.f: - $(FC) $(CPPDEFS) $(FPPFLAGS) -EP $*.F > $*.f -.F.i: - $(FC) $(CPPDEFS) $(FPPFLAGS) -P $*.F -.F.o: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $*.F -.F.s: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -S $*.F -.F.x: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -o $*.x $*.F *.o $(LDFLAGS) -.F90.L: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -listing $*.F90 -.F90.opt: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.F90 -.F90.l: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $(LIST) $*.F90 -.F90.T: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -cif $*.F90 -.F90.f90: - $(FC) $(CPPDEFS) $(FPPFLAGS) -EP $*.F90 > $*.f90 -.F90.i90: - $(FC) $(CPPDEFS) $(FPPFLAGS) -P $*.F90 -.F90.o: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $*.F90 -.F90.s: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -S $*.F90 -.F90.x: - $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -o $*.x $*.F90 *.o $(LDFLAGS) -""" - else: - # Copied from bin/mkmf.template.t90 - # TODO: Why from `mkmf.template.t90`? Copy bin/mkmf.template.nci. - config["post"] = """ -# you should never need to change any lines below. - -# see the CF90 manual for more details on some of the file extensions -# discussed here. -# this makefile template recognizes fortran sourcefiles with extensions -# .f, .f90, .F, .F90. Given a sourcefile ., where is one of -# the above, this provides a number of default actions: - -# make .T create a CIF file -# make .lst create a compiler listing -# make .o create an object file -# make .s create an assembly listing -# make .x create an executable file, assuming standalone -# source - -# make .i create a preprocessed file (only for .F and .F90 -# extensions) - -# make .hpm produce hpm output from .x -# make .proc produce procstat output from .x - -# The macro TMPFILES is provided to slate files like the above for removal. - -RM = rm -f -SHELL = /bin/csh -TMPFILES = .*.m *.T *.TT *.hpm *.i *.lst *.proc *.s - -.SUFFIXES: .F .F90 .H .T .f .F90 .h .hpm .i .lst .proc .o .s .x - -.f.T: - $(FC) $(FFLAGS) -c -Ca $*.f -.f.lst: - $(FC) $(FFLAGS) $(LIST) -c $*.f -.f.o: - $(FC) $(FFLAGS) -c $*.f -.f.s: - $(FC) $(FFLAGS) -eS $*.f -.f.x: - $(FC) $(FFLAGS) $(LDFLAGS) -o $*.x $*.f -.f90.T: - $(FC) $(FFLAGS) -c -Ca $*.f90 -.f90.lst: - $(FC) $(FFLAGS) $(LIST) -c $*.f90 -.f90.o: - $(FC) $(FFLAGS) -c $*.f90 -.f90.s: - $(FC) $(FFLAGS) -c -eS $*.f90 -.f90.x: - $(FC) $(FFLAGS) $(LDFLAGS) -o $*.x $*.f90 -.F.T: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -Ca $*.F -.F.i: - $(FC) $(CPPDEFS) $(CPPFLAGS) -eP $*.F -.F.lst: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LIST) -c $*.F -.F.o: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c $*.F -.F.s: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -eS $*.F -.F.x: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LDFLAGS) -o $*.x $*.F -.F90.T: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -Ca $*.F90 -.F90.i: - $(FC) $(CPPDEFS) $(CPPFLAGS) -eP $*.F90 -.F90.lst: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LIST) -c $*.F90 -.F90.o: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c $*.F90 -.F90.s: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -eS $*.F90 -.F90.x: - $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LDFLAGS) -o $*.x $*.F90 -.x.proc: - procstat -R $*.proc $*.x -.x.hpm: - hpm -r -o $*.hpm $*.x -""" - - fullconfig = config[self.compiler.name] + config["post"] - print(fullconfig) - with open(makeinc_path, "w") as makeinc: - makeinc.write(fullconfig) - - def build(self, spec, prefix): - - # cd ${ACCESS_OM_DIR}/src/mom/exp - # export mom_type=ACCESS-OM - # ./MOM_compile.csh --type $mom_type --platform spack - with working_dir(join_path(self.stage.source_path, "exp")): - build = Executable("./MOM_compile.csh") - - if self.spec.satisfies("+restart_repro"): - build.add_default_env("REPRO", "true") - print("INFO: +restart_repro applied") - - if self.__builds[self.__version]["gtracers"]: - build.add_default_env("SPACK_GTRACERS_EXTERNAL", "true") - - if self.__version != "access-esm1.5": - # The MOM5 commit d7ba13a3f364ce130b6ad0ba813f01832cada7a2 - # requires the --no_version switch to avoid git hashes being - # embedded in the binary. - build.add_default_arg("--no_version") - if self.spec.satisfies("+optimisation_report"): - build.add_default_env("REPORT", "true") - print("INFO: +optimisation_report applied") - - build( - "--type", - self.__builds[self.__version]["type"], - "--platform", - self.__platform, - "--no_environ" - ) - - def install(self, spec, prefix): - - mkdirp(prefix.bin) - install( - join_path( - "exec", - self.__platform, - self.__builds[self.__version]["type"], - "fms_" + self.__builds[self.__version]["type"] + ".x" - ), - prefix.bin + " type=" + self.__types[self.__version] ) - install(join_path("bin", "mppnccombine." + self.__platform), prefix.bin) + + def cmake_args(self): + args = [ + self.define("MOM5_TYPE", self.__types[self.__version]), + self.define_from_variant("MOM5_DETERMINISTIC", "deterministic"), + ] + return args \ No newline at end of file From 1dcfbf454c49a0a05c62f73a2eb250049732026f Mon Sep 17 00:00:00 2001 From: dougiesquire Date: Mon, 12 May 2025 22:33:38 +1000 Subject: [PATCH 2/6] mom5: re-add support for access-esm1.5 --- packages/access-om2-bgc/package.py | 4 +- packages/mom5/package.py | 390 ++++++++++++++++++++++++++--- 2 files changed, 353 insertions(+), 41 deletions(-) diff --git a/packages/access-om2-bgc/package.py b/packages/access-om2-bgc/package.py index 6fb2a586..509689c9 100644 --- a/packages/access-om2-bgc/package.py +++ b/packages/access-om2-bgc/package.py @@ -25,12 +25,12 @@ class AccessOm2Bgc(BundlePackage): depends_on("cice5+deterministic", when="+deterministic", type="run") depends_on("cice5~deterministic", when="~deterministic", type="run") depends_on( - "mom5@legacy-access-om2-bgc+deterministic", + "mom5@access-om2-bgc-legacy+deterministic", when="+deterministic", type="run" ) depends_on( - "mom5@legacy-access-om2-bgc~deterministic", + "mom5@access-om2-bgc-legacy~deterministic", when="~deterministic", type="run" ) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index ddd280a5..c0dd54ac 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -3,9 +3,10 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) from spack.package import * +from spack.build_systems import cmake, makefile from spack.version.version_types import GitVersion, StandardVersion -class Mom5(CMakePackage): +class Mom5(CMakePackage, MakefilePackage): """MOM is a numerical ocean model based on the hydrostatic primitive equations.""" homepage = "https://www.access-nri.org.au" @@ -19,71 +20,382 @@ class Mom5(CMakePackage): version("mom_solo", branch="master", preferred=True) version("mom_sis", branch="master") version("access-om2", branch="master") - version("legacy-access-om2-bgc", branch="master") + version("access-om2-bgc-legacy", branch="master") + version("access-esm1.5", branch="access-esm1.5") version("access-esm1.6", branch="master") - variant("build_type", default="RelWithDebInfo", - description="CMake build type", - values=("Debug", "Release", "RelWithDebInfo") + build_system( + conditional("makefile", when="@access-esm1.5"), + "cmake", + default="makefile", ) - variant("deterministic", default=False, description="Deterministic build") - depends_on("cmake@3.18:", type="build") - depends_on("mpi") - depends_on("netcdf-c@4.7.4:") - depends_on("netcdf-fortran@4.5.2:") + with when("build_system=cmake"): + variant("build_type", default="RelWithDebInfo", + description="CMake build type", + values=("Debug", "Release", "RelWithDebInfo") + ) + variant("deterministic", default=False, description="Deterministic build") - with when("@access-om2,access-esm1.6,legacy-access-om2-bgc"): - depends_on("datetime-fortran") - depends_on("libaccessom2+deterministic", when="+deterministic") - depends_on("libaccessom2~deterministic", when="~deterministic") + depends_on("mpi") + depends_on("netcdf-c@4.7.4:") + depends_on("netcdf-fortran@4.5.2:") + + # NOTE: when("@access-om2") matches version "access-om2-bgc-legacy" + with when("@access-om2,access-esm1.6"): depends_on("oasis3-mct+deterministic", when="+deterministic") depends_on("oasis3-mct~deterministic", when="~deterministic") depends_on("access-fms") depends_on("access-generic-tracers") + with when("@access-om2"): + depends_on("datetime-fortran") + depends_on("libaccessom2+deterministic", when="+deterministic") + depends_on("libaccessom2~deterministic", when="~deterministic") + + with when("@access-esm1.5"): + variant("restart_repro", default=True, description="Reproducible restart build.") + + depends_on("openmpi") + depends_on("netcdf-c@4.7.1:") + depends_on("netcdf-fortran@4.5.1:") + depends_on("oasis3-mct@access-esm1.5") + + def url_for_version(self, version): + return "https://github.com/ACCESS-NRI/mom5/tarball/{0}".format(version) + + +class CMakeBuilder(cmake.CMakeBuilder): root_cmakelists_dir = "cmake/" - phases = ["setup", "cmake", "build", "install"] + phases = ("setup", "cmake", "build", "install") - # NOTE: The keys in the __types variable are required to check whether - # a valid version was passed in by the user. __types = { "mom_solo": "MOM5_SOLO", "mom_sis": "MOM5_SIS", "access-om2": "MOM5_ACCESS_OM", "access-esm1.6": "MOM5_ACCESS_ESM", - "legacy-access-om2-bgc": "MOM5_ACCESS_OM_BGC" + "access-om2-bgc-legacy": "MOM5_ACCESS_OM_BGC" } __version = "INVALID" - def url_for_version(self, version): - return "https://github.com/ACCESS-NRI/mom5/tarball/{0}".format(version) - - def setup(self, spec, prefix): - - if isinstance(self.version, GitVersion): - self.__version = self.version.ref_version.string - elif isinstance(self.version, StandardVersion): - self.__version = self.version.string + def setup(self, pkg, spec, prefix): + if isinstance(pkg.version, GitVersion): + self.__version = pkg.version.ref_version.string + elif isinstance(pkg.version, StandardVersion): + self.__version = pkg.version.string else: - print("ERROR: version=" + self.version.string) - raise ValueError + raise ValueError("version=" + pkg.version.string) - # The rest of the checks are only required if a __types member - # variable exists if self.__version not in self.__types.keys(): - print("ERROR: The version must be selected from: " + - ", ".join(self.__types.keys())) - raise ValueError - - print("INFO: version=" + self.__version + - " type=" + self.__types[self.__version] - ) + raise ValueError( + "The version must be selected from: " + + ", ".join(self.__types.keys()) + ) def cmake_args(self): args = [ self.define("MOM5_TYPE", self.__types[self.__version]), self.define_from_variant("MOM5_DETERMINISTIC", "deterministic"), ] - return args \ No newline at end of file + return args + + +class MakefileBuilder(makefile.MakefileBuilder): + phases = ("edit", "build", "install") + + __type = "ACCESS-CM" # Building mom5@access-esm1.5 + __platform = "spack" + + def edit(self, pkg, spec, prefix): + + srcdir = pkg.stage.source_path + makeinc_path = join_path(srcdir, "bin", "mkmf.template.spack") + config = {} + + # NOTE: The order of the libraries matters during the linking step! + istr = " ".join([ + join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1"), + join_path((spec["oasis3-mct"].headers).cpp_flags, "mct")]) + ideps = ["netcdf-fortran"] + ldeps = ["oasis3-mct", "netcdf-c", "netcdf-fortran"] + FFLAGS_OPT = "-O3 -debug minimal -xCORE-AVX512 -align array64byte" + CFLAGS_OPT = "-O2 -debug minimal -no-vec" + + incs = " ".join([istr] + [(spec[d].headers).cpp_flags for d in ideps]) + libs = " ".join([(spec[d].libs).ld_flags for d in ldeps]) + + # Copied from bin/mkmf.template.ubuntu + config["gcc"] = f""" +FC = mpifort +CC = gcc +LD = $(FC) +######### +# flags # +######### +DEBUG = +REPRO = +VERBOSE = +OPENMP = + +MAKEFLAGS += --jobs=$(shell grep '^processor' /proc/cpuinfo | wc -l) + +FPPFLAGS := + +FFLAGS := -fcray-pointer -fdefault-real-8 -ffree-line-length-none -fno-range-check -Waliasing -Wampersand -Warray-bounds -Wcharacter-truncation -Wconversion -Wline-truncation -Wintrinsics-std -Wsurprising -Wno-tabs -Wunderflow -Wunused-parameter -Wintrinsic-shadow -Wno-align-commons -fallow-argument-mismatch -fallow-invalid-boz +FFLAGS += {incs} +FFLAGS += -DGFORTRAN + +# +FFLAGS_OPT = -O2 +FFLAGS_REPRO = +FFLAGS_DEBUG = -O0 -g -W -fbounds-check +FFLAGS_OPENMP = -fopenmp +FFLAGS_VERBOSE = + +CFLAGS := -D__IFC {incs} +CFLAGS += $(shell nc-config --cflags) +CFLAGS_OPT = -O2 +CFLAGS_OPENMP = -fopenmp +CFLAGS_DEBUG = -O0 -g + +# Optional Testing compile flags. Mutually exclusive from DEBUG, REPRO, and OPT +# *_TEST will match the production if no new option(s) is(are) to be tested. +FFLAGS_TEST = -O2 +CFLAGS_TEST = -O2 + +LDFLAGS := +LDFLAGS_OPENMP := -fopenmp +LDFLAGS_VERBOSE := + +ifneq ($(REPRO),) +CFLAGS += $(CFLAGS_REPRO) +FFLAGS += $(FFLAGS_REPRO) +endif +ifneq ($(DEBUG),) +CFLAGS += $(CFLAGS_DEBUG) +FFLAGS += $(FFLAGS_DEBUG) +else ifneq ($(TEST),) +CFLAGS += $(CFLAGS_TEST) +FFLAGS += $(FFLAGS_TEST) +else +CFLAGS += $(CFLAGS_OPT) +FFLAGS += $(FFLAGS_OPT) +endif + +ifneq ($(OPENMP),) +CFLAGS += $(CFLAGS_OPENMP) +FFLAGS += $(FFLAGS_OPENMP) +LDFLAGS += $(LDFLAGS_OPENMP) +endif + +ifneq ($(VERBOSE),) +CFLAGS += $(CFLAGS_VERBOSE) +FFLAGS += $(FFLAGS_VERBOSE) +LDFLAGS += $(LDFLAGS_VERBOSE) +endif + +ifeq ($(NETCDF),3) + # add the use_LARGEFILE cppdef + ifneq ($(findstring -Duse_netCDF,$(CPPDEFS)),) + CPPDEFS += -Duse_LARGEFILE + endif +endif + +LIBS := {libs} +LDFLAGS += $(LIBS) +""" + + # Copied from bin/mkmf.template.nci + config["intel"] = f""" +ifeq ($(VTRACE), yes) + FC = mpifort-vt + LD = mpifort-vt +else + FC = mpifort + LD = mpifort +endif + +CC = mpicc + +REPRO = +VERBOSE = +OPT = on + +MAKEFLAGS += --jobs=4 + +INCLUDE = {incs} + +FPPFLAGS := -fpp -Wp,-w $(INCLUDE) +FFLAGS := -fno-alias -safe-cray-ptr -fpe0 -ftz -assume byterecl -i4 -r8 -traceback -nowarn -check noarg_temp_created -assume buffered_io -convert big_endian +FFLAGS_OPT = {FFLAGS_OPT} +FFLAGS_DEBUG = -g -O0 -debug all -check -check noarg_temp_created -check nopointer -warn -warn noerrors -ftrapuv +FFLAGS_REPRO = -O2 -debug minimal -no-vec -fp-model precise +FFLAGS_VERBOSE = -v -V -what + +CFLAGS := -D__IFC $(INCLUDE) +CFLAGS_OPT = {CFLAGS_OPT} +CFLAGS_DEBUG = -O0 -g -ftrapuv -traceback + +LDFLAGS := +LDFLAGS_VERBOSE := -Wl,-V,--verbose,-cref,-M + +ifneq ($(REPRO),) +CFLAGS += $(CFLAGS_REPRO) +FFLAGS += $(FFLAGS_REPRO) +endif + +ifneq ($(DEBUG),) +CFLAGS += $(CFLAGS_DEBUG) +FFLAGS += $(FFLAGS_DEBUG) +else +CFLAGS += $(CFLAGS_OPT) +FFLAGS += $(FFLAGS_OPT) +endif + +ifneq ($(VERBOSE),) +CFLAGS += $(CFLAGS_VERBOSE) +FFLAGS += $(FFLAGS_VERBOSE) +LDFLAGS += $(LDFLAGS_VERBOSE) +endif + +LIBS := {libs} + +LDFLAGS += $(LIBS) +""" + + # Add support for the ifx compiler + # TODO: `.replace() is a temporary workaround for: + # icx: error: unsupported argument 'source' to option '-ffp-model=' + # The `.replace()` apparently doesn't modify the object. + config["oneapi"] = config["intel"].replace("CFLAGS_REPRO := -fp-model precise -fp-model source", "CFLAGS_REPRO := -fp-model precise") + + config["post"] = """ +# you should never need to change any lines below. + +# see the MIPSPro F90 manual for more details on some of the file extensions +# discussed here. +# this makefile template recognizes fortran sourcefiles with extensions +# .f, .f90, .F, .F90. Given a sourcefile ., where is one of +# the above, this provides a number of default actions: + +# make .opt create an optimization report +# make .o create an object file +# make .s create an assembly listing +# make .x create an executable file, assuming standalone +# source +# make .i create a preprocessed file (for .F) +# make .i90 create a preprocessed file (for .F90) + +# The macro TMPFILES is provided to slate files like the above for removal. + +RM = rm -f +SHELL = /bin/csh -f +TMPFILES = .*.m *.B *.L *.i *.i90 *.l *.s *.mod *.opt + +.SUFFIXES: .F .F90 .H .L .T .f .f90 .h .i .i90 .l .o .s .opt .x + +.f.L: + $(FC) $(FFLAGS) -c -listing $*.f +.f.opt: + $(FC) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.f +.f.l: + $(FC) $(FFLAGS) -c $(LIST) $*.f +.f.T: + $(FC) $(FFLAGS) -c -cif $*.f +.f.o: + $(FC) $(FFLAGS) -c $*.f +.f.s: + $(FC) $(FFLAGS) -S $*.f +.f.x: + $(FC) $(FFLAGS) -o $*.x $*.f *.o $(LDFLAGS) +.f90.L: + $(FC) $(FFLAGS) -c -listing $*.f90 +.f90.opt: + $(FC) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.f90 +.f90.l: + $(FC) $(FFLAGS) -c $(LIST) $*.f90 +.f90.T: + $(FC) $(FFLAGS) -c -cif $*.f90 +.f90.o: + $(FC) $(FFLAGS) -c $*.f90 +.f90.s: + $(FC) $(FFLAGS) -c -S $*.f90 +.f90.x: + $(FC) $(FFLAGS) -o $*.x $*.f90 *.o $(LDFLAGS) +.F.L: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -listing $*.F +.F.opt: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.F +.F.l: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $(LIST) $*.F +.F.T: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -cif $*.F +.F.f: + $(FC) $(CPPDEFS) $(FPPFLAGS) -EP $*.F > $*.f +.F.i: + $(FC) $(CPPDEFS) $(FPPFLAGS) -P $*.F +.F.o: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $*.F +.F.s: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -S $*.F +.F.x: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -o $*.x $*.F *.o $(LDFLAGS) +.F90.L: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -listing $*.F90 +.F90.opt: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -opt_report_level max -opt_report_phase all -opt_report_file $*.opt $*.F90 +.F90.l: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $(LIST) $*.F90 +.F90.T: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -cif $*.F90 +.F90.f90: + $(FC) $(CPPDEFS) $(FPPFLAGS) -EP $*.F90 > $*.f90 +.F90.i90: + $(FC) $(CPPDEFS) $(FPPFLAGS) -P $*.F90 +.F90.o: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c $*.F90 +.F90.s: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -c -S $*.F90 +.F90.x: + $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -o $*.x $*.F90 *.o $(LDFLAGS) +""" + + fullconfig = config[pkg.compiler.name] + config["post"] + print(fullconfig) + with open(makeinc_path, "w") as makeinc: + makeinc.write(fullconfig) + + def build(self, pkg, spec, prefix): + + # cd ${ACCESS_OM_DIR}/src/mom/exp + # export mom_type=ACCESS-CM + # ./MOM_compile.csh --type $mom_type --platform spack + with working_dir(join_path(pkg.stage.source_path, "exp")): + build = Executable("./MOM_compile.csh") + + if pkg.spec.satisfies("+restart_repro"): + build.add_default_env("REPRO", "true") + print("INFO: +restart_repro applied") + + build( + "--type", + self.__type, + "--platform", + self.__platform, + "--no_environ" + ) + + def install(self, pkg, spec, prefix): + + mkdirp(prefix.bin) + install( + join_path( + "exec", + self.__platform, + self.__type, + "fms_" + self.__type + ".x" + ), + prefix.bin + ) + install(join_path("bin", "mppnccombine." + self.__platform), prefix.bin) \ No newline at end of file From e7c653bd3b2ef11d719f2f22dd1582ebb3a55d6a Mon Sep 17 00:00:00 2001 From: dougiesquire Date: Wed, 14 May 2025 09:27:29 +1000 Subject: [PATCH 3/6] Add legacy makefile support for access-om2 and legacy-access-om2-bgc --- packages/access-om2-bgc/package.py | 4 +- packages/mom5/package.py | 286 +++++++++++++++++++++++++---- 2 files changed, 254 insertions(+), 36 deletions(-) diff --git a/packages/access-om2-bgc/package.py b/packages/access-om2-bgc/package.py index 509689c9..6fb2a586 100644 --- a/packages/access-om2-bgc/package.py +++ b/packages/access-om2-bgc/package.py @@ -25,12 +25,12 @@ class AccessOm2Bgc(BundlePackage): depends_on("cice5+deterministic", when="+deterministic", type="run") depends_on("cice5~deterministic", when="~deterministic", type="run") depends_on( - "mom5@access-om2-bgc-legacy+deterministic", + "mom5@legacy-access-om2-bgc+deterministic", when="+deterministic", type="run" ) depends_on( - "mom5@access-om2-bgc-legacy~deterministic", + "mom5@legacy-access-om2-bgc~deterministic", when="~deterministic", type="run" ) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index c0dd54ac..7a77d6f2 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -12,22 +12,23 @@ class Mom5(CMakePackage, MakefilePackage): homepage = "https://www.access-nri.org.au" git = "https://github.com/ACCESS-NRI/mom5.git" - maintainers("dougiesquire", "harshula") + maintainers("dougiesquire", "harshula", "penguian") # https://github.com/ACCESS-NRI/MOM5#LGPL-3.0-1-ov-file license("LGPL-3.0-only", checked_by="dougiesquire") - version("mom_solo", branch="master", preferred=True) + version("mom_solo", branch="master") version("mom_sis", branch="master") - version("access-om2", branch="master") - version("access-om2-bgc-legacy", branch="master") + version("access-om2", branch="master", preferred=True) + version("legacy-access-om2-bgc", branch="master") version("access-esm1.5", branch="access-esm1.5") version("access-esm1.6", branch="master") + # NOTE: @mom matches both mom_solo and mom_sis build_system( - conditional("makefile", when="@access-esm1.5"), - "cmake", - default="makefile", + conditional("makefile", when="@access-om2,legacy-access-om2-bgc,access-esm1.5"), + conditional("cmake", when="@mom,access-om2,legacy-access-om2-bgc,access-esm1.6"), + default="cmake", ) with when("build_system=cmake"): @@ -37,28 +38,47 @@ class Mom5(CMakePackage, MakefilePackage): ) variant("deterministic", default=False, description="Deterministic build") - depends_on("mpi") + with when("build_system=makefile"): + variant("restart_repro", default=True, description="Reproducible restart build.") + variant( + "deterministic", + default=False, + description="Deterministic build", + when="@access-om2,legacy-access-om2-bgc,access-esm1.6" + ) + variant( + "optimisation_report", + default=False, + description="Generate optimisation reports", + when="@access-om2,legacy-access-om2-bgc,access-esm1.6" + ) + + with when("@mom,access-om2,legacy-access-om2-bgc,access-esm1.6"): depends_on("netcdf-c@4.7.4:") depends_on("netcdf-fortran@4.5.2:") + depends_on("mpi") - # NOTE: when("@access-om2") matches version "access-om2-bgc-legacy" - with when("@access-om2,access-esm1.6"): + with when("@access-om2,legacy-access-om2-bgc,access-esm1.6"): depends_on("oasis3-mct+deterministic", when="+deterministic") depends_on("oasis3-mct~deterministic", when="~deterministic") - depends_on("access-fms") - depends_on("access-generic-tracers") - with when("@access-om2"): + with when("@access-om2,legacy-access-om2-bgc"): depends_on("datetime-fortran") depends_on("libaccessom2+deterministic", when="+deterministic") depends_on("libaccessom2~deterministic", when="~deterministic") - with when("@access-esm1.5"): - variant("restart_repro", default=True, description="Reproducible restart build.") + with when("@access-om2,access-esm1.6"): + depends_on("access-fms") + depends_on("access-generic-tracers") - depends_on("openmpi") + with when("@legacy-access-om2-bgc"): + depends_on("access-fms", when="build_system=cmake") + depends_on("access-generic-tracers", when="build_system=cmake") + + with when("@access-esm1.5"): depends_on("netcdf-c@4.7.1:") depends_on("netcdf-fortran@4.5.1:") + depends_on("openmpi") depends_on("oasis3-mct@access-esm1.5") def url_for_version(self, version): @@ -75,7 +95,7 @@ class CMakeBuilder(cmake.CMakeBuilder): "mom_sis": "MOM5_SIS", "access-om2": "MOM5_ACCESS_OM", "access-esm1.6": "MOM5_ACCESS_ESM", - "access-om2-bgc-legacy": "MOM5_ACCESS_OM_BGC" + "legacy-access-om2-bgc": "MOM5_ACCESS_OM_BGC" } __version = "INVALID" @@ -89,8 +109,8 @@ def setup(self, pkg, spec, prefix): if self.__version not in self.__types.keys(): raise ValueError( - "The version must be selected from: " + - ", ".join(self.__types.keys()) + f"CMakeBuilder doesn't support version {self.__version}. The version must " + "be selected from: " + ", ".join(self.__types.keys()) ) def cmake_args(self): @@ -102,11 +122,30 @@ def cmake_args(self): class MakefileBuilder(makefile.MakefileBuilder): - phases = ("edit", "build", "install") + phases = ("setup", "edit", "build", "install") - __type = "ACCESS-CM" # Building mom5@access-esm1.5 + __types = { + "access-om2": "ACCESS-OM", + "legacy-access-om2-bgc": "ACCESS-OM-BGC", + "access-esm1.5": "ACCESS-CM" + } + __version = "INVALID" __platform = "spack" + def setup(self, pkg, spec, prefix): + if isinstance(pkg.version, GitVersion): + self.__version = pkg.version.ref_version.string + elif isinstance(pkg.version, StandardVersion): + self.__version = pkg.version.string + else: + raise ValueError("version=" + pkg.version.string) + + if self.__version not in self.__types.keys(): + raise ValueError( + f"MakefileBuilder doesn't support version {self.__version}. The version must " + "be selected from: " + ", ".join(self.__types.keys()) + ) + def edit(self, pkg, spec, prefix): srcdir = pkg.stage.source_path @@ -114,13 +153,27 @@ def edit(self, pkg, spec, prefix): config = {} # NOTE: The order of the libraries matters during the linking step! - istr = " ".join([ - join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1"), - join_path((spec["oasis3-mct"].headers).cpp_flags, "mct")]) - ideps = ["netcdf-fortran"] - ldeps = ["oasis3-mct", "netcdf-c", "netcdf-fortran"] - FFLAGS_OPT = "-O3 -debug minimal -xCORE-AVX512 -align array64byte" - CFLAGS_OPT = "-O2 -debug minimal -no-vec" + if self.__version == "access-esm1.5": + istr = " ".join([ + join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1"), + join_path((spec["oasis3-mct"].headers).cpp_flags, "mct")]) + ideps = ["netcdf-fortran"] + ldeps = ["oasis3-mct", "netcdf-c", "netcdf-fortran"] + FFLAGS_OPT = "-O3 -debug minimal -xCORE-AVX512 -align array64byte" + CFLAGS_OPT = "-O2 -debug minimal -no-vec" + else: + istr = join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1") + ideps = ["oasis3-mct", "libaccessom2", "netcdf-fortran"] + # NOTE: datetime-fortran is a dependency of libaccessom2. + ldeps = ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"] + + # TODO: https://github.com/ACCESS-NRI/ACCESS-OM/issues/12 + FFLAGS_OPT = "-g3 -O2 -xCORE-AVX2 -debug all -check none -traceback" + CFLAGS_OPT = "-O2 -debug minimal -xCORE-AVX2" + if self.spec.satisfies("+deterministic"): + FFLAGS_OPT = "-g0 -O0 -xCORE-AVX2 -debug none -check none" + CFLAGS_OPT = "-O0 -debug none -xCORE-AVX2" + print("INFO: +deterministic applied") incs = " ".join([istr] + [(spec[d].headers).cpp_flags for d in ideps]) libs = " ".join([(spec[d].libs).ld_flags for d in ldeps]) @@ -207,7 +260,8 @@ def edit(self, pkg, spec, prefix): """ # Copied from bin/mkmf.template.nci - config["intel"] = f""" + if self.__version == "access-esm1.5": + config["intel"] = f""" ifeq ($(VTRACE), yes) FC = mpifort-vt LD = mpifort-vt @@ -261,6 +315,78 @@ def edit(self, pkg, spec, prefix): LIBS := {libs} +LDFLAGS += $(LIBS) +""" + else: + config["intel"] = f""" +ifeq ($(VTRACE), yes) + FC := mpifort-vt + LD := mpifort-vt +else + FC := mpifort + LD := mpifort +endif + +CC := mpicc + +VERBOSE := +OPT := on + +MAKEFLAGS += -j + +INCLUDE := {incs} + +FPPFLAGS := -fpp -Wp,-w $(INCLUDE) +FFLAGS := -fno-alias -safe-cray-ptr -fpe0 -ftz -assume byterecl -i4 -r8 -nowarn -check noarg_temp_created -assume nobuffered_io -convert big_endian -grecord-gcc-switches -align all +FFLAGS_OPT := {FFLAGS_OPT} +FFLAGS_REPORT := -qopt-report=5 -qopt-report-annotate +FFLAGS_DEBUG := -g3 -O0 -debug all -check -check noarg_temp_created -check nopointer -warn -warn noerrors -ftrapuv -traceback +FFLAGS_REPRO := -fp-model precise -fp-model source -align all +FFLAGS_VERBOSE := -v -V -what + +CFLAGS := -D__IFC $(INCLUDE) +CFLAGS_OPT := {CFLAGS_OPT} +CFLAGS_REPORT := -qopt-report=5 -qopt-report-annotate +CFLAGS_DEBUG := -O0 -g -ftrapuv -traceback +CFLAGS_REPRO := -fp-model precise -fp-model source + +LDFLAGS := +LDFLAGS_VERBOSE := -Wl,-V,--verbose,-cref,-M + +ifneq ($(REPRO),) +CFLAGS += $(CFLAGS_REPRO) +FFLAGS += $(FFLAGS_REPRO) +endif + +ifneq ($(DEBUG),) +CFLAGS += $(CFLAGS_DEBUG) +FFLAGS += $(FFLAGS_DEBUG) +else +CFLAGS += $(CFLAGS_OPT) +FFLAGS += $(FFLAGS_OPT) +endif + +ifneq ($(VERBOSE),) +CFLAGS += $(CFLAGS_VERBOSE) +FFLAGS += $(FFLAGS_VERBOSE) +LDFLAGS += $(LDFLAGS_VERBOSE) +endif + +ifneq ($(REPORT),) +CFLAGS += $(CFLAGS_REPORT) +FFLAGS += $(FFLAGS_REPORT) +endif + +LIBS := {libs} + +ifneq ($(OASIS_ROOT),) +LIBS += -L$(OASIS_ROOT)/Linux/lib -lpsmile.MPI1 -lmct -lmpeu -lscrip +endif + +ifneq ($(LIBACCESSOM2_ROOT),) +LIBS += -L$(LIBACCESSOM2_ROOT)/build/lib -laccessom2 +endif + LDFLAGS += $(LIBS) """ @@ -270,7 +396,8 @@ def edit(self, pkg, spec, prefix): # The `.replace()` apparently doesn't modify the object. config["oneapi"] = config["intel"].replace("CFLAGS_REPRO := -fp-model precise -fp-model source", "CFLAGS_REPRO := -fp-model precise") - config["post"] = """ + if self.__version == "access-esm1.5": + config["post"] = """ # you should never need to change any lines below. # see the MIPSPro F90 manual for more details on some of the file extensions @@ -360,6 +487,88 @@ def edit(self, pkg, spec, prefix): .F90.x: $(FC) $(CPPDEFS) $(FPPFLAGS) $(FFLAGS) -o $*.x $*.F90 *.o $(LDFLAGS) """ + else: + # Copied from bin/mkmf.template.t90 + # TODO: Why from `mkmf.template.t90`? Copy bin/mkmf.template.nci. + config["post"] = """ +# you should never need to change any lines below. + +# see the CF90 manual for more details on some of the file extensions +# discussed here. +# this makefile template recognizes fortran sourcefiles with extensions +# .f, .f90, .F, .F90. Given a sourcefile ., where is one of +# the above, this provides a number of default actions: + +# make .T create a CIF file +# make .lst create a compiler listing +# make .o create an object file +# make .s create an assembly listing +# make .x create an executable file, assuming standalone +# source + +# make .i create a preprocessed file (only for .F and .F90 +# extensions) + +# make .hpm produce hpm output from .x +# make .proc produce procstat output from .x + +# The macro TMPFILES is provided to slate files like the above for removal. + +RM = rm -f +SHELL = /bin/csh +TMPFILES = .*.m *.T *.TT *.hpm *.i *.lst *.proc *.s + +.SUFFIXES: .F .F90 .H .T .f .F90 .h .hpm .i .lst .proc .o .s .x + +.f.T: + $(FC) $(FFLAGS) -c -Ca $*.f +.f.lst: + $(FC) $(FFLAGS) $(LIST) -c $*.f +.f.o: + $(FC) $(FFLAGS) -c $*.f +.f.s: + $(FC) $(FFLAGS) -eS $*.f +.f.x: + $(FC) $(FFLAGS) $(LDFLAGS) -o $*.x $*.f +.f90.T: + $(FC) $(FFLAGS) -c -Ca $*.f90 +.f90.lst: + $(FC) $(FFLAGS) $(LIST) -c $*.f90 +.f90.o: + $(FC) $(FFLAGS) -c $*.f90 +.f90.s: + $(FC) $(FFLAGS) -c -eS $*.f90 +.f90.x: + $(FC) $(FFLAGS) $(LDFLAGS) -o $*.x $*.f90 +.F.T: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -Ca $*.F +.F.i: + $(FC) $(CPPDEFS) $(CPPFLAGS) -eP $*.F +.F.lst: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LIST) -c $*.F +.F.o: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c $*.F +.F.s: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -eS $*.F +.F.x: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LDFLAGS) -o $*.x $*.F +.F90.T: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -Ca $*.F90 +.F90.i: + $(FC) $(CPPDEFS) $(CPPFLAGS) -eP $*.F90 +.F90.lst: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LIST) -c $*.F90 +.F90.o: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c $*.F90 +.F90.s: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) -c -eS $*.F90 +.F90.x: + $(FC) $(CPPDEFS) $(CPPFLAGS) $(FFLAGS) $(LDFLAGS) -o $*.x $*.F90 +.x.proc: + procstat -R $*.proc $*.x +.x.hpm: + hpm -r -o $*.hpm $*.x +""" fullconfig = config[pkg.compiler.name] + config["post"] print(fullconfig) @@ -369,7 +578,7 @@ def edit(self, pkg, spec, prefix): def build(self, pkg, spec, prefix): # cd ${ACCESS_OM_DIR}/src/mom/exp - # export mom_type=ACCESS-CM + # export mom_type=ACCESS-OM # ./MOM_compile.csh --type $mom_type --platform spack with working_dir(join_path(pkg.stage.source_path, "exp")): build = Executable("./MOM_compile.csh") @@ -378,9 +587,18 @@ def build(self, pkg, spec, prefix): build.add_default_env("REPRO", "true") print("INFO: +restart_repro applied") + if self.__version != "access-esm1.5": + # The MOM5 commit d7ba13a3f364ce130b6ad0ba813f01832cada7a2 + # requires the --no_version switch to avoid git hashes being + # embedded in the binary. + build.add_default_arg("--no_version") + if pkg.spec.satisfies("+optimisation_report"): + build.add_default_env("REPORT", "true") + print("INFO: +optimisation_report applied") + build( "--type", - self.__type, + self.__types[self.__version], "--platform", self.__platform, "--no_environ" @@ -393,8 +611,8 @@ def install(self, pkg, spec, prefix): join_path( "exec", self.__platform, - self.__type, - "fms_" + self.__type + ".x" + self.__types[self.__version], + "fms_" + self.__types[self.__version] + ".x" ), prefix.bin ) From 1517db9b1921aeb3f33b872f19d1de9e08c152cf Mon Sep 17 00:00:00 2001 From: dougiesquire Date: Wed, 14 May 2025 13:33:31 +1000 Subject: [PATCH 4/6] Changes requested by Harshula --- packages/mom5/package.py | 56 ++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index 7a77d6f2..5fee439a 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -1,8 +1,10 @@ # Copyright Spack Project Developers. See COPYRIGHT file for details. # +# Copyright ACCESS-NRI +# # SPDX-License-Identifier: (Apache-2.0 OR MIT) -from spack.package import * +from spack.package import install, join_path, mkdirp from spack.build_systems import cmake, makefile from spack.version.version_types import GitVersion, StandardVersion @@ -67,6 +69,8 @@ class Mom5(CMakePackage, MakefilePackage): depends_on("libaccessom2+deterministic", when="+deterministic") depends_on("libaccessom2~deterministic", when="~deterministic") + # NOTE: Spack will also match "access-om2-legacy-bgc" here, that's why + # it has been renamed to "legacy-access-om2-bgc". with when("@access-om2,access-esm1.6"): depends_on("access-fms") depends_on("access-generic-tracers") @@ -90,7 +94,9 @@ class CMakeBuilder(cmake.CMakeBuilder): phases = ("setup", "cmake", "build", "install") - __types = { + # NOTE: The keys in the __builds variable are required to check whether + # a valid version was passed in by the user. + __builds = { "mom_solo": "MOM5_SOLO", "mom_sis": "MOM5_SIS", "access-om2": "MOM5_ACCESS_OM", @@ -99,6 +105,9 @@ class CMakeBuilder(cmake.CMakeBuilder): } __version = "INVALID" + # NOTE: This functionality will hopefully be implemented in the Spack core + # in the future. Till then, this approach can be used in other SPRs + # where this functionality is required. def setup(self, pkg, spec, prefix): if isinstance(pkg.version, GitVersion): self.__version = pkg.version.ref_version.string @@ -107,15 +116,20 @@ def setup(self, pkg, spec, prefix): else: raise ValueError("version=" + pkg.version.string) - if self.__version not in self.__types.keys(): + # The rest of the checks are only required if a __builds member + # variable exists + if self.__version not in self.__builds.keys(): raise ValueError( f"CMakeBuilder doesn't support version {self.__version}. The version must " - "be selected from: " + ", ".join(self.__types.keys()) + "be selected from: " + ", ".join(self.__builds.keys()) ) + print("INFO: version=" + self.__version + + " type=" + self.__builds[self.__version]) + def cmake_args(self): args = [ - self.define("MOM5_TYPE", self.__types[self.__version]), + self.define("MOM5_TYPE", self.__builds[self.__version]), self.define_from_variant("MOM5_DETERMINISTIC", "deterministic"), ] return args @@ -124,7 +138,7 @@ def cmake_args(self): class MakefileBuilder(makefile.MakefileBuilder): phases = ("setup", "edit", "build", "install") - __types = { + __builds = { "access-om2": "ACCESS-OM", "legacy-access-om2-bgc": "ACCESS-OM-BGC", "access-esm1.5": "ACCESS-CM" @@ -132,6 +146,9 @@ class MakefileBuilder(makefile.MakefileBuilder): __version = "INVALID" __platform = "spack" + # NOTE: This functionality will hopefully be implemented in the Spack core + # in the future. Till then, this approach can be used in other SPRs + # where this functionality is required. def setup(self, pkg, spec, prefix): if isinstance(pkg.version, GitVersion): self.__version = pkg.version.ref_version.string @@ -140,12 +157,17 @@ def setup(self, pkg, spec, prefix): else: raise ValueError("version=" + pkg.version.string) - if self.__version not in self.__types.keys(): + # The rest of the checks are only required if a __builds member + # variable exists + if self.__version not in self.__builds.keys(): raise ValueError( f"MakefileBuilder doesn't support version {self.__version}. The version must " - "be selected from: " + ", ".join(self.__types.keys()) + "be selected from: " + ", ".join(self.__builds.keys()) ) + print("INFO: version=" + self.__version + + " type=" + self.__builds[self.__version]) + def edit(self, pkg, spec, prefix): srcdir = pkg.stage.source_path @@ -193,7 +215,7 @@ def edit(self, pkg, spec, prefix): MAKEFLAGS += --jobs=$(shell grep '^processor' /proc/cpuinfo | wc -l) -FPPFLAGS := +FPPFLAGS := FFLAGS := -fcray-pointer -fdefault-real-8 -ffree-line-length-none -fno-range-check -Waliasing -Wampersand -Warray-bounds -Wcharacter-truncation -Wconversion -Wline-truncation -Wintrinsics-std -Wsurprising -Wno-tabs -Wunderflow -Wunused-parameter -Wintrinsic-shadow -Wno-align-commons -fallow-argument-mismatch -fallow-invalid-boz FFLAGS += {incs} @@ -201,16 +223,16 @@ def edit(self, pkg, spec, prefix): # FFLAGS_OPT = -O2 -FFLAGS_REPRO = -FFLAGS_DEBUG = -O0 -g -W -fbounds-check +FFLAGS_REPRO = +FFLAGS_DEBUG = -O0 -g -W -fbounds-check FFLAGS_OPENMP = -fopenmp -FFLAGS_VERBOSE = +FFLAGS_VERBOSE = CFLAGS := -D__IFC {incs} CFLAGS += $(shell nc-config --cflags) CFLAGS_OPT = -O2 CFLAGS_OPENMP = -fopenmp -CFLAGS_DEBUG = -O0 -g +CFLAGS_DEBUG = -O0 -g # Optional Testing compile flags. Mutually exclusive from DEBUG, REPRO, and OPT # *_TEST will match the production if no new option(s) is(are) to be tested. @@ -219,7 +241,7 @@ def edit(self, pkg, spec, prefix): LDFLAGS := LDFLAGS_OPENMP := -fopenmp -LDFLAGS_VERBOSE := +LDFLAGS_VERBOSE := ifneq ($(REPRO),) CFLAGS += $(CFLAGS_REPRO) @@ -598,7 +620,7 @@ def build(self, pkg, spec, prefix): build( "--type", - self.__types[self.__version], + self.__builds[self.__version], "--platform", self.__platform, "--no_environ" @@ -611,8 +633,8 @@ def install(self, pkg, spec, prefix): join_path( "exec", self.__platform, - self.__types[self.__version], - "fms_" + self.__types[self.__version] + ".x" + self.__builds[self.__version], + "fms_" + self.__builds[self.__version] + ".x" ), prefix.bin ) From 45a19817aee613637f25091560d4fa76b49a8623 Mon Sep 17 00:00:00 2001 From: Dougie Squire <42455466+dougiesquire@users.noreply.github.com> Date: Thu, 22 May 2025 13:09:59 +1000 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Harshula Jayasuriya --- packages/mom5/package.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index 5fee439a..08378711 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -46,24 +46,29 @@ class Mom5(CMakePackage, MakefilePackage): "deterministic", default=False, description="Deterministic build", - when="@access-om2,legacy-access-om2-bgc,access-esm1.6" + when="@access-om2,legacy-access-om2-bgc" ) variant( "optimisation_report", default=False, description="Generate optimisation reports", - when="@access-om2,legacy-access-om2-bgc,access-esm1.6" + when="@access-om2,legacy-access-om2-bgc" ) with when("@mom,access-om2,legacy-access-om2-bgc,access-esm1.6"): depends_on("netcdf-c@4.7.4:") depends_on("netcdf-fortran@4.5.2:") + # Depend on virtual package "mpi". depends_on("mpi") - with when("@access-om2,legacy-access-om2-bgc,access-esm1.6"): + with when("@access-om2,legacy-access-om2-bgc"): depends_on("oasis3-mct+deterministic", when="+deterministic") depends_on("oasis3-mct~deterministic", when="~deterministic") + with when("@access-esm1.6"): + depends_on("oasis3-mct@access-esm1.5+deterministic", when="+deterministic") + depends_on("oasis3-mct@access-esm1.5~deterministic", when="~deterministic") + with when("@access-om2,legacy-access-om2-bgc"): depends_on("datetime-fortran") depends_on("libaccessom2+deterministic", when="+deterministic") @@ -75,6 +80,8 @@ class Mom5(CMakePackage, MakefilePackage): depends_on("access-fms") depends_on("access-generic-tracers") + # legacy-access-om2-bgc builds with access-generic-tracers but it + # is not configured for use in ACCESS-OM2-BGC configurations. with when("@legacy-access-om2-bgc"): depends_on("access-fms", when="build_system=cmake") depends_on("access-generic-tracers", when="build_system=cmake") @@ -638,4 +645,5 @@ def install(self, pkg, spec, prefix): ), prefix.bin ) - install(join_path("bin", "mppnccombine." + self.__platform), prefix.bin) \ No newline at end of file + install(join_path("bin", "mppnccombine." + self.__platform), prefix.bin) + From ed25bd497a716cc4edaa867cc14e06dbc3777149 Mon Sep 17 00:00:00 2001 From: Dougie Squire <42455466+dougiesquire@users.noreply.github.com> Date: Thu, 22 May 2025 17:32:09 +1000 Subject: [PATCH 6/6] Update packages/mom5/package.py Co-authored-by: Harshula Jayasuriya --- packages/mom5/package.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/mom5/package.py b/packages/mom5/package.py index 08378711..84af31f2 100644 --- a/packages/mom5/package.py +++ b/packages/mom5/package.py @@ -62,18 +62,16 @@ class Mom5(CMakePackage, MakefilePackage): depends_on("mpi") with when("@access-om2,legacy-access-om2-bgc"): + depends_on("datetime-fortran") depends_on("oasis3-mct+deterministic", when="+deterministic") depends_on("oasis3-mct~deterministic", when="~deterministic") + depends_on("libaccessom2+deterministic", when="+deterministic") + depends_on("libaccessom2~deterministic", when="~deterministic") with when("@access-esm1.6"): depends_on("oasis3-mct@access-esm1.5+deterministic", when="+deterministic") depends_on("oasis3-mct@access-esm1.5~deterministic", when="~deterministic") - with when("@access-om2,legacy-access-om2-bgc"): - depends_on("datetime-fortran") - depends_on("libaccessom2+deterministic", when="+deterministic") - depends_on("libaccessom2~deterministic", when="~deterministic") - # NOTE: Spack will also match "access-om2-legacy-bgc" here, that's why # it has been renamed to "legacy-access-om2-bgc". with when("@access-om2,access-esm1.6"):