From b452fce4d2bdf926f60afb1432cd2a107f6a1a99 Mon Sep 17 00:00:00 2001 From: Harshula Jayasuriya Date: Mon, 16 Jan 2023 15:14:11 +1100 Subject: [PATCH 1/4] cice5: draft Spack package file * NOTE: The order of the libraries matter during the linking step! * NOTE: datetime-fortran is a dependency of libaccessom2. --- packages/cice5/package.py | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/cice5/package.py diff --git a/packages/cice5/package.py b/packages/cice5/package.py new file mode 100644 index 0000000..c75058a --- /dev/null +++ b/packages/cice5/package.py @@ -0,0 +1,88 @@ +# Copyright 2013-2023 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# Copyright 2023 ACCESS-NRI +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack.package import install, join_path, mkdirp + +# https://spack.readthedocs.io/en/latest/build_systems/makefilepackage.html +class Cice5(MakefilePackage): + """The Los Alamos sea ice model (CICE) is the result of an effort to develop a computationally efficient sea ice component for a fully coupled atmosphere-land global climate model.""" + + homepage = "https://www.access-nri.org.au" + git = "https://github.com/ACCESS-NRI/cice5.git" + + maintainers = ["harshula"] + + version("spack-build", branch="spack-build") + + # TODO: Should we depend on virtual package "mpi" instead? + depends_on("openmpi@4.0.2:") + depends_on("oasis3-mct") + depends_on("datetime-fortran") + depends_on("netcdf-fortran@4.5.2:") + depends_on("netcdf-c@4.7.4:") + # cice5 builds parallelio with -DWITH_PNETCDF=OFF -DPIO_ENABLE_TIMING=OFF + depends_on("parallelio") + depends_on("libaccessom2") + + phases = ["build", "install"] + + # The integr represents environment variable NTASK + __targets = {24: {}, 480: {}, 722: {}, 1682: {}} + __targets[24]["RES"] = "360x300" + __targets[24]["BLCKX"] = 360 // 24 + __targets[24]["BLCKY"] = 300 // 1 + + __targets[480]["RES"] = "1440x1080" + __targets[480]["BLCKX"] = 1440 // 48 + __targets[480]["BLCKY"] = 1080 // 40 + + # Comment from bld/config.nci.auscom.3600x2700: + # Recommendations: + # use processor_shape = slenderX1 or slenderX2 in ice_in + # one per processor with distribution_type='cartesian' or + # squarish blocks with distribution_type='rake' + # If BLCKX (BLCKY) does not divide NXGLOB (NYGLOB) evenly, padding + # will be used on the right (top) of the grid. + __targets[722]["RES"] = "3600x2700" + __targets[722]["BLCKX"] = 3600 // 90 + __targets[722]["BLCKY"] = 2700 // 90 + + __targets[1682]["RES"] = "18x15.3600x2700" + __targets[1682]["BLCKX"] = 3600 // 200 + __targets[1682]["BLCKY"] = 2700 // 180 + + + def build(self, spec, prefix): + + incs = (spec["oasis3-mct"].headers).cpp_flags + "/psmile.MPI1" + " " + for l in ["parallelio", "oasis3-mct", "libaccessom2", "netcdf-fortran"]: + incs += (spec[l].headers).cpp_flags + " " + + # NOTE: The order of the libraries matter during the linking step! + # NOTE: datetime-fortran is a dependency of libaccessom2. + libs = "-L" + (spec["parallelio"].prefix) + "/lib -lpiof -lpioc " + for l in ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"]: + libs += (spec[l].libs).ld_flags + " " + + build = Executable("bld/build.sh") + build.add_default_env("CPL_INCS", incs) + build.add_default_env("CPLLIBS", libs) + + for k in self.__targets: + build.add_default_env("NTASK", str(k)) + build.add_default_env("RES", self.__targets[k]["RES"]) + build.add_default_env("BLCKX", str(self.__targets[k]["BLCKX"])) + build.add_default_env("BLCKY", str(self.__targets[k]["BLCKY"])) + build("nci", "auscom", self.__targets[k]["RES"]) + + + def install(self, spec, prefix): + + mkdirp(prefix.bin) + install(join_path("build_auscom_360x300_24p", "cice_auscom_360x300_24p.exe"), prefix.bin) + install(join_path("build_auscom_1440x1080_480p", "cice_auscom_1440x1080_480p.exe"), prefix.bin) + install(join_path("build_auscom_3600x2700_722p", "cice_auscom_3600x2700_722p.exe"), prefix.bin) From ab2d5e9d6cfaf79b21f537bc9c805d7a349e66c2 Mon Sep 17 00:00:00 2001 From: Harshula Jayasuriya Date: Thu, 26 Jan 2023 18:01:01 +1100 Subject: [PATCH 2/4] cice5: use a version of parallelio with variant "shared" * Requires: https://github.com/spack/spack/pull/34837 --- packages/cice5/package.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/cice5/package.py b/packages/cice5/package.py index c75058a..fab7672 100644 --- a/packages/cice5/package.py +++ b/packages/cice5/package.py @@ -24,8 +24,10 @@ class Cice5(MakefilePackage): depends_on("datetime-fortran") depends_on("netcdf-fortran@4.5.2:") depends_on("netcdf-c@4.7.4:") - # cice5 builds parallelio with -DWITH_PNETCDF=OFF -DPIO_ENABLE_TIMING=OFF - depends_on("parallelio") + # TODO: For initial verification we are going to use static pio. + # Eventually we plan to move to shared pio + # ~shared requires: https://github.com/spack/spack/pull/34837 + depends_on("parallelio~pnetcdf~timing~shared") depends_on("libaccessom2") phases = ["build", "install"] From 5aaf19ec35f87779b86297f473c4305fb677305d Mon Sep 17 00:00:00 2001 From: Harshula Jayasuriya Date: Tue, 14 Feb 2023 16:18:09 +1100 Subject: [PATCH 3/4] cice5: support building with the Intel compiler * copy cice5's bld/build.sh as a template to build from spack --- packages/cice5/package.py | 174 ++++++++++++++++++++++++++++------ packages/cice5/spack-build.sh | 153 ++++++++++++++++++++++++++++++ 2 files changed, 299 insertions(+), 28 deletions(-) create mode 100755 packages/cice5/spack-build.sh diff --git a/packages/cice5/package.py b/packages/cice5/package.py index fab7672..484848c 100644 --- a/packages/cice5/package.py +++ b/packages/cice5/package.py @@ -18,8 +18,8 @@ class Cice5(MakefilePackage): version("spack-build", branch="spack-build") - # TODO: Should we depend on virtual package "mpi" instead? - depends_on("openmpi@4.0.2:") + # Depend on virtual package "mpi". + depends_on("mpi") depends_on("oasis3-mct") depends_on("datetime-fortran") depends_on("netcdf-fortran@4.5.2:") @@ -30,17 +30,20 @@ class Cice5(MakefilePackage): depends_on("parallelio~pnetcdf~timing~shared") depends_on("libaccessom2") - phases = ["build", "install"] + phases = ["edit", "build", "install"] + + _buildscript = "spack-build.sh" + _buildscript_path = join_path("bld", _buildscript) # The integr represents environment variable NTASK __targets = {24: {}, 480: {}, 722: {}, 1682: {}} - __targets[24]["RES"] = "360x300" - __targets[24]["BLCKX"] = 360 // 24 - __targets[24]["BLCKY"] = 300 // 1 + __targets[24]["driver"] = "auscom" + __targets[24]["grid"] = "360x300" + __targets[24]["blocks"] = "24x1" - __targets[480]["RES"] = "1440x1080" - __targets[480]["BLCKX"] = 1440 // 48 - __targets[480]["BLCKY"] = 1080 // 40 + __targets[480]["driver"] = "auscom" + __targets[480]["grid"] = "1440x1080" + __targets[480]["blocks"] = "48x40" # Comment from bld/config.nci.auscom.3600x2700: # Recommendations: @@ -49,16 +52,23 @@ class Cice5(MakefilePackage): # squarish blocks with distribution_type='rake' # If BLCKX (BLCKY) does not divide NXGLOB (NYGLOB) evenly, padding # will be used on the right (top) of the grid. - __targets[722]["RES"] = "3600x2700" - __targets[722]["BLCKX"] = 3600 // 90 - __targets[722]["BLCKY"] = 2700 // 90 + __targets[722]["driver"] = "auscom" + __targets[722]["grid"] = "3600x2700" + __targets[722]["blocks"] = "90x90" - __targets[1682]["RES"] = "18x15.3600x2700" - __targets[1682]["BLCKX"] = 3600 // 200 - __targets[1682]["BLCKY"] = 2700 // 180 + __targets[1682]["driver"] = "auscom" + __targets[1682]["grid"] = "3600x2700" + __targets[1682]["blocks"] = "200x180" + def edit(self, spec, prefix): - def build(self, spec, prefix): + srcdir = self.stage.source_path + buildscript_dest = join_path(srcdir, self._buildscript_path) + makeinc_path = join_path(srcdir, "bld", "Macros.spack") + + copy(join_path(self.package_dir, self._buildscript), buildscript_dest) + + config = {} incs = (spec["oasis3-mct"].headers).cpp_flags + "/psmile.MPI1" + " " for l in ["parallelio", "oasis3-mct", "libaccessom2", "netcdf-fortran"]: @@ -70,21 +80,129 @@ def build(self, spec, prefix): for l in ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"]: libs += (spec[l].libs).ld_flags + " " - build = Executable("bld/build.sh") - build.add_default_env("CPL_INCS", incs) - build.add_default_env("CPLLIBS", libs) + # Copied from bld/Macros.nci + config["pre"] = f""" +INCLDIR := -I. {incs} +SLIBS := {libs} +ULIBS := +CPP := cpp +FC := mpifort + +CPPFLAGS := -P -traditional +CPPDEFS := -DLINUX -DPAROPT +CFLAGS := -c -O2 +FIXEDFLAGS := -132 +FREEFLAGS := +""" + + config["gcc"] = f""" +# TODO: removed -std=f2008 due to compiler errors +FFLAGS = -Wall -fdefault-real-8 -fdefault-double-8 -ffpe-trap=invalid,zero,overflow -fallow-argument-mismatch +""" + + # module load intel-compiler/2019.5.281 + config["intel"] = f""" +NCI_INTEL_FLAGS := -r8 -i4 -traceback -w -fpe0 -ftz -convert big_endian -assume byterecl -check noarg_temp_created +NCI_REPRO_FLAGS := -fp-model precise -fp-model source -align all +ifeq ($(DEBUG), 1) + NCI_DEBUG_FLAGS := -g3 -O0 -debug all -check all -no-vec -assume nobuffered_io + FFLAGS := $(NCI_INTEL_FLAGS) $(NCI_REPRO_FLAGS) $(NCI_DEBUG_FLAGS) + CPPDEFS := $(CPPDEFS) -DDEBUG=$(DEBUG) +else + NCI_OPTIM_FLAGS := -g3 -O2 -axCORE-AVX2 -debug all -check none -qopt-report=5 -qopt-report-annotate -assume buffered_io + FFLAGS := $(NCI_INTEL_FLAGS) $(NCI_REPRO_FLAGS) $(NCI_OPTIM_FLAGS) +endif +""" + + # Copied from bld/Macros.nci + config["post"] = """ +MOD_SUFFIX := mod +LD := $(FC) +LDFLAGS := $(FFLAGS) -v + +CPPDEFS := $(CPPDEFS) -DNXGLOB=$(NXGLOB) -DNYGLOB=$(NYGLOB) \ + -DNUMIN=$(NUMIN) -DNUMAX=$(NUMAX) \ + -DTRAGE=$(TRAGE) -DTRFY=$(TRFY) -DTRLVL=$(TRLVL) \ + -DTRPND=$(TRPND) -DNTRAERO=$(NTRAERO) -DTRBRI=$(TRBRI) \ + -DNBGCLYR=$(NBGCLYR) -DTRBGCS=$(TRBGCS) \ + -DNICECAT=$(NICECAT) -DNICELYR=$(NICELYR) \ + -DNSNWLYR=$(NSNWLYR) \ + -DBLCKX=$(BLCKX) -DBLCKY=$(BLCKY) -DMXBLCKS=$(MXBLCKS) + +ifeq ($(COMMDIR), mpi) + SLIBS := $(SLIBS) -lmpi +endif + +ifeq ($(DITTO), yes) + CPPDEFS := $(CPPDEFS) -DREPRODUCIBLE +endif +ifeq ($(BARRIERS), yes) + CPPDEFS := $(CPPDEFS) -Dgather_scatter_barrier +endif + +ifeq ($(IO_TYPE), netcdf) + CPPDEFS := $(CPPDEFS) -Dncdf +endif + +ifeq ($(IO_TYPE), pio) + CPPDEFS := $(CPPDEFS) -Dncdf -DPIO +endif + +# TODO: Do we want to delete this conditional? +#ifeq ($(USE_ESMF), yes) +# CPPDEFS := $(CPPDEFS) -Duse_esmf +# INCLDIR := $(INCLDIR) -I ??? +# SLIBS := $(SLIBS) -L ??? -lesmf -lcprts -lrt -ldl +#endif + +ifeq ($(AusCOM), yes) + CPPDEFS := $(CPPDEFS) -DAusCOM -Dcoupled +endif + +ifeq ($(UNIT_TESTING), yes) + CPPDEFS := $(CPPDEFS) -DUNIT_TESTING +endif +ifeq ($(ACCESS), yes) + CPPDEFS := $(CPPDEFS) -DACCESS +endif +# standalone CICE with AusCOM mods +ifeq ($(ACCICE), yes) + CPPDEFS := $(CPPDEFS) -DACCICE +endif +# no MOM just CICE+UM +ifeq ($(NOMOM), yes) + CPPDEFS := $(CPPDEFS) -DNOMOM +endif +ifeq ($(OASIS3_MCT), yes) + CPPDEFS := $(CPPDEFS) -DOASIS3_MCT +endif +""" + with open(makeinc_path, "w") as makeinc: + makeinc.write( + config["pre"] + + config[self.compiler.name] + + config["post"] + ) - for k in self.__targets: - build.add_default_env("NTASK", str(k)) - build.add_default_env("RES", self.__targets[k]["RES"]) - build.add_default_env("BLCKX", str(self.__targets[k]["BLCKX"])) - build.add_default_env("BLCKY", str(self.__targets[k]["BLCKY"])) - build("nci", "auscom", self.__targets[k]["RES"]) + def build(self, spec, prefix): + + build = Executable( + join_path(self.stage.source_path, self._buildscript_path) + ) + for k in self.__targets: + build(self.__targets[k]["driver"], + self.__targets[k]["grid"], + self.__targets[k]["blocks"], + str(k)) def install(self, spec, prefix): mkdirp(prefix.bin) - install(join_path("build_auscom_360x300_24p", "cice_auscom_360x300_24p.exe"), prefix.bin) - install(join_path("build_auscom_1440x1080_480p", "cice_auscom_1440x1080_480p.exe"), prefix.bin) - install(join_path("build_auscom_3600x2700_722p", "cice_auscom_3600x2700_722p.exe"), prefix.bin) + for k in self.__targets: + name = "_".join([self.__targets[k]["driver"], + self.__targets[k]["grid"], + self.__targets[k]["blocks"], + str(k) + "p"]) + install(join_path("build_" + name, "cice_" + name + ".exe"), + prefix.bin) diff --git a/packages/cice5/spack-build.sh b/packages/cice5/spack-build.sh new file mode 100755 index 0000000..20b66a0 --- /dev/null +++ b/packages/cice5/spack-build.sh @@ -0,0 +1,153 @@ +#! /bin/csh -f + +set echo on + +if ( $#argv < 4 ) then + echo '*** Please issue the command like ***' + echo '> ./bld/build.sh []' + echo 'e.g. bld/build.sh auscom 1440x1080 48x40 480' + echo 'driver: which driver to use.' + echo 'grid: grid resolution longitude by latitude.' + echo 'blocks: grid split into the number of blocks longitude by latitude.' + echo 'ntask: number of tasks.' + echo 'debug: if this is a unit testing or debug build. Valid options are \'debug\' or \'unit_testing\'' + exit +else + set driver = $1 + set grid = $2 + set blocks = $3 + set ntask = $4 + set debug = $5 +endif + +# Location of this model +setenv SRCDIR $cwd +setenv CBLD $SRCDIR/bld + +if ($debug == 'debug') then + setenv DEBUG 1 +else + setenv DEBUG 0 +endif +if ($debug == 'unit_testing') then + setenv DEBUG 1 + setenv UNIT_TESTING yes +endif + +### Specialty code +setenv CAM_ICE no # set to yes for CAM runs (single column) +setenv SHRDIR csm_share # location of CCSM shared code +setenv IO_TYPE pio # set to none if netcdf library is unavailable +setenv DITTO no # reproducible diagnostics +setenv THRD no # set to yes for OpenMP threading +if ( $THRD == 'yes') setenv OMP_NUM_THREADS 2 # positive integer +setenv BARRIERS yes # set -Dgather_scatter_barrier, prevents hangs on raijin +setenv AusCOM yes +if ($driver == 'access') then + setenv ACCESS yes +else + setenv ACCESS no +endif +setenv OASIS3_MCT yes # oasis3-mct version +setenv NICELYR 4 # number of vertical layers in the ice +setenv NSNWLYR 1 # number of vertical layers in the snow +setenv NICECAT 5 # number of ice thickness categories + +### The version of an executable can be found with the following +### command: strings | grep 'CICE_VERSION=' +set version='202301' +sed -e "s/{CICE_VERSION}/$version/g" $SRCDIR/drivers/$driver/version.F90.template > $SRCDIR/drivers/$driver/version_mod.F90 + +### Where this model is compiled +setenv OBJDIR $SRCDIR/build_${driver}_${grid}_${blocks}_${ntask}p +if !(-d $OBJDIR) mkdir -p $OBJDIR + +setenv NTASK $ntask +setenv RES $grid +set NXGLOB = `echo $RES | sed s/x.\*//` +set NYGLOB = `echo $RES | sed s/.\*x//` +set NXBLOCK = `echo $blocks | sed s/x.\*//` +set NYBLOCK = `echo $blocks | sed s/.\*x//` +setenv BLCKX `expr $NXGLOB / $NXBLOCK` # x-dimension of blocks ( not including ) +setenv BLCKY `expr $NYGLOB / $NYBLOCK` # y-dimension of blocks ( ghost cells ) + +@ a = $NXGLOB * $NYGLOB ; @ b = $BLCKX * $BLCKY * $NTASK +@ m = $a / $b ; setenv MXBLCKS $m ; if ($MXBLCKS == 0) setenv MXBLCKS 1 +echo Autimatically generated: MXBLCKS = $MXBLCKS + +########################################### +# ars599: 24032014 +# copy from /short/p66/ars599/CICE.v5.0/accice.v504_csiro +# solo_ice_comp +########################################### +### Tracers # match ice_in tracer_nml to conserve memory +setenv TRAGE 1 # set to 1 for ice age tracer +setenv TRFY 1 # set to 1 for first-year ice area tracer +setenv TRLVL 1 # set to 1 for level and deformed ice tracers +setenv TRPND 1 # set to 1 for melt pond tracers +setenv NTRAERO 0 # number of aerosol tracers + # (up to max_aero in ice_domain_size.F90) + # CESM uses 3 aerosol tracers +setenv TRBRI 1 # set to 1 for brine height tracer +setenv NBGCLYR 0 # number of zbgc layers +setenv TRBGCS 2 # number of skeletal layer bgc tracers + # TRBGCS=0 or 2<=TRBGCS<=9) + +### File unit numbers +setenv NUMIN 11 # minimum file unit number +setenv NUMAX 99 # maximum file unit number + +if ($IO_TYPE == 'netcdf') then + setenv IODIR io_netcdf +else if ($IO_TYPE == 'pio') then + setenv IODIR io_pio +else + setenv IODIR io_binary +endif + + + +cp -f $CBLD/Makefile.std $CBLD/Makefile + +if ($NTASK == 1) then + setenv COMMDIR serial +else + setenv COMMDIR mpi +endif +echo COMMDIR: $COMMDIR + +setenv DRVDIR $driver + +cd $OBJDIR + +### List of source code directories (in order of importance). +cat >! Filepath << EOF +$SRCDIR/drivers/$DRVDIR +$SRCDIR/source +$SRCDIR/$COMMDIR +$SRCDIR/$IODIR +$SRCDIR/$SHRDIR +EOF + +cc -o makdep $CBLD/makdep.c || exit 2 + +make VPFILE=Filepath EXEC=cice_${driver}_${grid}_${blocks}_${ntask}p.exe \ + NXGLOB=$NXGLOB NYGLOB=$NYGLOB \ + BLCKX=$BLCKX BLCKY=$BLCKY MXBLCKS=$MXBLCKS \ + -f $CBLD/Makefile MACFILE=$CBLD/Macros.spack || exit 2 + +cd .. +pwd +echo NTASK = $NTASK +echo "global N, block_size" +echo "x $NXGLOB, $BLCKX" +echo "y $NYGLOB, $BLCKY" +echo max_blocks = $MXBLCKS +echo $TRAGE = TRAGE, iage tracer +echo $TRFY = TRFY, first-year ice tracer +echo $TRLVL = TRLVL, level-ice tracers +echo $TRPND = TRPND, melt pond tracers +echo $NTRAERO = NTRAERO, number of aerosol tracers +echo $TRBRI = TRBRI, brine height tracer +echo $NBGCLYR = NBGCLYR, number of bio grid layers +echo $TRBGCS = TRBGCS, number of BGC tracers From 02be46363da4ea65c98f48d7e5aa9096b97aeeb3 Mon Sep 17 00:00:00 2001 From: Harshula Jayasuriya Date: Fri, 24 Mar 2023 11:01:07 +1100 Subject: [PATCH 4/4] cice5: change branch dependency from "spack-build" to "master" for merge * https://github.com/ACCESS-NRI/spack_packages/pull/8 --- packages/cice5/package.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/packages/cice5/package.py b/packages/cice5/package.py index 484848c..2860851 100644 --- a/packages/cice5/package.py +++ b/packages/cice5/package.py @@ -13,10 +13,13 @@ class Cice5(MakefilePackage): homepage = "https://www.access-nri.org.au" git = "https://github.com/ACCESS-NRI/cice5.git" + # NOTE: URL definition required for CI + # Spack needs tarball URL to be defined to access github branches + url = "https://github.com/ACCESS-NRI/cice5/tarball/master" maintainers = ["harshula"] - version("spack-build", branch="spack-build") + version("master", branch="master") # Depend on virtual package "mpi". depends_on("mpi") @@ -35,7 +38,7 @@ class Cice5(MakefilePackage): _buildscript = "spack-build.sh" _buildscript_path = join_path("bld", _buildscript) - # The integr represents environment variable NTASK + # The integer represents environment variable NTASK __targets = {24: {}, 480: {}, 722: {}, 1682: {}} __targets[24]["driver"] = "auscom" __targets[24]["grid"] = "360x300" @@ -70,15 +73,19 @@ def edit(self, spec, prefix): config = {} - incs = (spec["oasis3-mct"].headers).cpp_flags + "/psmile.MPI1" + " " - for l in ["parallelio", "oasis3-mct", "libaccessom2", "netcdf-fortran"]: - incs += (spec[l].headers).cpp_flags + " " + istr = join_path((spec["oasis3-mct"].headers).cpp_flags, "psmile.MPI1") + ideps = ["parallelio", "oasis3-mct", "libaccessom2", "netcdf-fortran"] + incs = " ".join([istr] + [(spec[d].headers).cpp_flags for d in ideps]) + lstr = " ".join( + ["-L" + join_path(spec["parallelio"].prefix, "lib"), + "-lpiof", + "-lpioc"] + ) # NOTE: The order of the libraries matter during the linking step! # NOTE: datetime-fortran is a dependency of libaccessom2. - libs = "-L" + (spec["parallelio"].prefix) + "/lib -lpiof -lpioc " - for l in ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"]: - libs += (spec[l].libs).ld_flags + " " + ldeps = ["oasis3-mct", "libaccessom2", "netcdf-c", "netcdf-fortran", "datetime-fortran"] + libs = " ".join([lstr] + [(spec[d].libs).ld_flags for d in ldeps]) # Copied from bld/Macros.nci config["pre"] = f"""