diff --git a/.github/workflows/ubuntu-ci-x86_64-gnu.yaml b/.github/workflows/ubuntu-ci-x86_64-gnu.yaml index 32bbb9e39..7bb6742ca 100644 --- a/.github/workflows/ubuntu-ci-x86_64-gnu.yaml +++ b/.github/workflows/ubuntu-ci-x86_64-gnu.yaml @@ -82,9 +82,10 @@ jobs: spack stack create env --site linux.default --template ${TEMPLATE} --name ${ENVNAME} --compiler gcc spack env activate ${ENVDIR} - # Turn off variant "adp" for neptune-dev (NRL internal only) + # Turn off variants "adp" and "jedi" for neptune-dev (NRL internal only) if [[ "${TEMPLATE}" == *"neptune-dev"* ]]; then sed -i 's/+adp/~adp/g' ${ENVDIR}/spack.yaml + sed -i 's/+jedi/~jedi/g' ${ENVDIR}/spack.yaml fi unset SPACK_DISABLE_LOCAL_CONFIG diff --git a/.github/workflows/ubuntu-ci-x86_64-oneapi-ifx.yaml b/.github/workflows/ubuntu-ci-x86_64-oneapi-ifx.yaml index cf27ce5f8..d8370ed5c 100644 --- a/.github/workflows/ubuntu-ci-x86_64-oneapi-ifx.yaml +++ b/.github/workflows/ubuntu-ci-x86_64-oneapi-ifx.yaml @@ -82,9 +82,10 @@ jobs: spack stack create env --site linux.default --template ${TEMPLATE} --name ${ENVNAME} --compiler oneapi spack env activate ${ENVDIR} - # Turn off variant "adp" for neptune-dev (NRL internal only) + # Turn off variants "adp" and "jedi" for neptune-dev (NRL internal only) if [[ "${TEMPLATE}" == *"neptune-dev"* ]]; then sed -i 's/+adp/~adp/g' ${ENVDIR}/spack.yaml + sed -i 's/+jedi/~jedi/g' ${ENVDIR}/spack.yaml fi unset SPACK_DISABLE_LOCAL_CONFIG diff --git a/.github/workflows/ubuntu-ci-x86_64-oneapi.yaml b/.github/workflows/ubuntu-ci-x86_64-oneapi.yaml index 4dded738e..23fe94ef6 100644 --- a/.github/workflows/ubuntu-ci-x86_64-oneapi.yaml +++ b/.github/workflows/ubuntu-ci-x86_64-oneapi.yaml @@ -82,9 +82,10 @@ jobs: spack stack create env --site linux.default --template ${TEMPLATE} --name ${ENVNAME} --compiler oneapi spack env activate ${ENVDIR} - # Turn off variant "adp" for neptune-dev (NRL internal only) + # Turn off variants "adp" and "jedi" for neptune-dev (NRL internal only) if [[ "${TEMPLATE}" == *"neptune-dev"* ]]; then sed -i 's/+adp/~adp/g' ${ENVDIR}/spack.yaml + sed -i 's/+jedi/~jedi/g' ${ENVDIR}/spack.yaml fi unset SPACK_DISABLE_LOCAL_CONFIG diff --git a/configs/common/packages_gcc.yaml b/configs/common/packages_gcc.yaml index 1f3925485..d89f64e65 100644 --- a/configs/common/packages_gcc.yaml +++ b/configs/common/packages_gcc.yaml @@ -20,3 +20,6 @@ packages: ectrans: require: - +fftw + oops: + require: + - ~mkl \ No newline at end of file diff --git a/configs/common/packages_oneapi.yaml b/configs/common/packages_oneapi.yaml index 30db8f8c7..6ef85cb5c 100644 --- a/configs/common/packages_oneapi.yaml +++ b/configs/common/packages_oneapi.yaml @@ -19,40 +19,43 @@ packages: # Individual package settings antlr: require: - - '%gcc' + - '%c,cxx=gcc' bison: require: - - '%gcc' + - '%c,cxx=gcc' cmake: require: - - '%gcc' + - '%c,cxx=gcc' ectrans: require: - +mkl ~fftw gmake: require: - - '%gcc' + - '%c=gcc' libbsd: require: - - '%gcc' + - '%c=gcc' libmd: require: - - '%gcc' + - '%c=gcc' met: require: - +shared-intel nco: require: - - '%gcc' + - '%c,cxx=gcc' + oops: + require: + - +mkl parallel-netcdf: require: - +shared-intel py-maturin: require: - - '%gcc' + - '%c=gcc' py-scipy: require: - 'cxxflags="-O1"' qt: require: - - '%gcc' + - '%c,cxx=gcc' diff --git a/configs/sites/tier1/nautilus/packages_gcc-13.3.1.yaml b/configs/sites/tier1/nautilus/packages_gcc-13.3.1.yaml index 3a4a8adee..5d3e17386 100644 --- a/configs/sites/tier1/nautilus/packages_gcc-13.3.1.yaml +++ b/configs/sites/tier1/nautilus/packages_gcc-13.3.1.yaml @@ -18,7 +18,7 @@ packages: openmpi: buildable: False externals: - - spec: openmpi@5.0.8 + - spec: openmpi@5.0.8 ~internal-hwloc +two_level_namespace prefix: /p/app/penguin/openmpi/5.0.8/gcc-13.3.1 modules: - penguin/openmpi/5.0.8/gcc-13.3.1 diff --git a/configs/sites/tier2/blackpearl/packages.yaml b/configs/sites/tier2/blackpearl/packages.yaml index 992254c14..c8c415b14 100644 --- a/configs/sites/tier2/blackpearl/packages.yaml +++ b/configs/sites/tier2/blackpearl/packages.yaml @@ -61,7 +61,6 @@ packages: - spec: groff@1.22.4 prefix: /usr m4: - # DH* NEEDED? buildable: false externals: - spec: m4@1.4.19 prefix: /usr diff --git a/configs/sites/tier2/bounty/packages.yaml b/configs/sites/tier2/bounty/packages.yaml index 8a6f8d470..c98ee71fe 100644 --- a/configs/sites/tier2/bounty/packages.yaml +++ b/configs/sites/tier2/bounty/packages.yaml @@ -65,7 +65,6 @@ packages: - spec: groff@1.22.4 prefix: /usr m4: - # DH* NEEDED? buildable: false externals: - spec: m4@1.4.19 prefix: /usr diff --git a/configs/templates/neptune-dev/spack.yaml b/configs/templates/neptune-dev/spack.yaml index 96e1dfc9e..15bdd438c 100644 --- a/configs/templates/neptune-dev/spack.yaml +++ b/configs/templates/neptune-dev/spack.yaml @@ -12,8 +12,8 @@ spack: - neptune-env +debug +openmp +espc +ncview ^esmf@=8.9.1 - neptune-env ~debug ~openmp +espc +ncview ^esmf@=8.9.1 - neptune-env +debug ~openmp +espc +ncview ^esmf@=8.9.1 - - neptune-python-env ^neptune-env ~debug +openmp +espc +ncview ^esmf@=8.9.1 - - jedi-neptune-env +adp ^neptune-env ~debug +openmp +espc +ncview ^esmf@=8.9.1 + - neptune-python-env ^neptune-env ~debug +openmp +espc +ncview ^esmf@=8.9.1 + - jedi-neptune-env +adp +jedi ^neptune-env ~debug +openmp +espc +ncview ^esmf@=8.9.1 - crtm@3.1.2 - crtm@v2.4.1-jedi.2 diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/ioda/ioda_cmake_import.patch b/repos/spack_stack/spack_repo/spack_stack/packages/ioda/ioda_cmake_import.patch new file mode 100644 index 000000000..0fc3e334d --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/ioda/ioda_cmake_import.patch @@ -0,0 +1,18 @@ +--- a/cmake/ioda-import.cmake.in ++++ b/cmake/ioda-import.cmake.in +@@ -65,6 +65,15 @@ + if(@odc_FOUND@) + find_dependency( odc 1.0.2 REQUIRED ) + endif() ++ ++if(@bufr_FOUND@) ++ find_dependency( bufr 12.0.1 REQUIRED ) ++endif() ++ ++if(@bufr_query_FOUND@) ++ find_dependency( bufr_query 0.0.4 REQUIRED ) ++endif() ++ + # Header-only. Not exposed. + #find_dependency( Boost 1.64.0 ) + diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/ioda/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/ioda/package.py new file mode 100644 index 000000000..241b895d1 --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/ioda/package.py @@ -0,0 +1,79 @@ +# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.cmake import CMakePackage, generator + +from spack.package import * + + +class Ioda(CMakePackage): + """Interface for Observation Data Access""" + + homepage = "https://github.com/JCSDA/ioda" + git = "https://github.com/JCSDA/ioda.git" + + maintainers("climbfuji") + + version("develop", branch="develop", no_cache=True) + version("2.9.0.20250826", commit="6e76616001067384f7d0ca4341ad78e81527af8b") + + patch("ioda_cmake_import.patch", when="@2.9.0.20250826") + + variant("doc", default=False, description="Build IODA documentation") + # Let's always assume IODA_BUILD_LANGUAGE_FORTRAN=on. + # variant('fortran', default=True, description='Build the ioda Fortran interface') + variant("odc", default=True, description="Build ODC bindings") + # ioda has no explicit OpenMP calls, but header files from Eigen and oops do use openmp. + variant("openmp", default=True, description="Build with OpenMP support") + # Let's always BUILD_PYTHON_BINDINGS. + # variant('python', default=True, description='Build the ioda Python interface') + + generator("make") + + # Project doesn't list "c" as a dependency in CMakeLists.txt, but cmake step fails w/o it + depends_on("c", type=("build")) + depends_on("cxx", type=("build")) + depends_on("fortran", type=("build")) + + depends_on("boost@1.64.0:") + depends_on("bufr") + depends_on("bufr@12.0.1:", when="@2.9:") + # This is optional, and it needs netcdf-cxx - which versions? + depends_on("bufr-query@0.0.4:", when="@2.9:") + depends_on("cmake", type=("build")) + depends_on("cmake@3.14:", type=("build"), when="@2.9:") + depends_on("ecbuild", type=("build")) + depends_on("ecbuild@3.3.2:", type=("build"), when="@2.9:") + depends_on("eckit") + depends_on("eckit@1.23.0:", when="@2.9:") + depends_on("eigen") + depends_on("fckit") + depends_on("fckit@0.10.1:", when="@2.9:") + depends_on("gsl-lite") + depends_on("hdf5@1.12.0: +mpi") + depends_on("hdf5@1.14.0: +mpi", when="@2.9:") + depends_on("jedi-cmake", type=("build")) + depends_on("llvm-openmp", when="+openmp %apple-clang", type=("build", "link", "run")) + depends_on("mpi") + depends_on("nccmp") + # netcdf-cxx needed for bufr-query, but which version? + depends_on("netcdf-cxx", when="@2.9:") + depends_on("odc", when="+odc") + depends_on("odc@1.4.6:", when="@2.9: +odc") + depends_on("oops+openmp", when="+openmp") + depends_on("oops~openmp", when="~openmp") + depends_on("oops@1.10", when="@2.9:") + depends_on("python") + depends_on("python@3.9:3.11", when="@2.9:") + depends_on("py-pybind11") + depends_on("py-pycodestyle") + depends_on("udunits") + depends_on("udunits@2.2.0:", when="@2.9:") + + def cmake_args(self): + res = [ + self.define_from_variant("ENABLE_IODA_DOC", "doc"), + ] + return res diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/ioda_converters/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/ioda_converters/package.py new file mode 100644 index 000000000..5880bce31 --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/ioda_converters/package.py @@ -0,0 +1,46 @@ +# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.cmake import CMakePackage, generator + +from spack.package import * + + +class IodaConverters(CMakePackage): + """Interface for Observation Data Access""" + + homepage = "https://github.com/JCSDA/ioda-converters" + git = "https://github.com/JCSDA/ioda-converters.git" + + maintainers("climbfuji") + + version("develop", branch="develop", no_cache=True) + version("0.0.1.20250830", commit="a91f432d9d50940910605e689cd1cf93a1ce3798") + + generator("make") + + # Project doesn't list "c" as a dependency in CMakeLists.txt, but cmake step fails w/o it + depends_on("c", type=("build")) + depends_on("cxx", type=("build")) + depends_on("fortran", type=("build")) + + extends("python") + + depends_on("ecbuild", type=("build")) + depends_on("ecbuild@3.3.2:", type=("build"), when="@0.0.1:") + + depends_on("bufr@12:") + depends_on("eccodes") + depends_on("eckit") + depends_on("eigen@3") + depends_on("gsl-lite") + depends_on("ioda") + depends_on("jedi-cmake", type=("build")) + depends_on("mpi") + depends_on("netcdf-cxx") + depends_on("netcdf-fortran") + depends_on("oops") + depends_on("py-cartopy") + depends_on("py-pybind11") diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/jedi_neptune_env/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/jedi_neptune_env/package.py index 51689bf64..19bff422e 100644 --- a/repos/spack_stack/spack_repo/spack_stack/packages/jedi_neptune_env/package.py +++ b/repos/spack_stack/spack_repo/spack_stack/packages/jedi_neptune_env/package.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) from spack_repo.builtin.build_systems.bundle import BundlePackage + from spack.package import * @@ -20,6 +21,8 @@ class JediNeptuneEnv(BundlePackage): variant("adp", default=False, description="Build ADP preprocessors") + variant("jedi", default=False, description="Build JEDI components required for JEDI-NEPTUNE") + depends_on("jedi-base-env", type="run") depends_on("neptune-env", type="run") depends_on("neptune-python-env", type="run") @@ -27,4 +30,10 @@ class JediNeptuneEnv(BundlePackage): with when("+adp"): depends_on("adp-preprocessors", type="run") - # There is no need for install() since there is no code. + with when("+jedi"): + depends_on("oops@1.10.0.20250827", type="run") + depends_on("crtm@v2.4.1-jedi.2", type="run") + depends_on("ioda@2.9.0.20250826", type="run") + depends_on("ioda-converters@0.0.1.20250830", type="run") + depends_on("ropp-ufo@11.0.20251022", type="run") + depends_on("ufo@1.10.0.20250821 +crtm-v2 +ropp", type="run") diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/oops/include_algorithm.patch b/repos/spack_stack/spack_repo/spack_stack/packages/oops/include_algorithm.patch new file mode 100644 index 000000000..771ae3990 --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/oops/include_algorithm.patch @@ -0,0 +1,10 @@ +--- a/src/oops/base/ParameterTraitsObsVariables.h ++++ b/src/oops/base/ParameterTraitsObsVariables.h +@@ -7,6 +7,7 @@ + + #pragma once + ++#include + #include + #include + diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/oops/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/oops/package.py new file mode 100644 index 000000000..b94385a1c --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/oops/package.py @@ -0,0 +1,70 @@ +# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.cmake import CMakePackage, generator + +from spack.package import * + + +class Oops(CMakePackage): + """Object Oriented Prediction System""" + + homepage = "https://github.com/JCSDA/oops" + git = "https://github.com/JCSDA/oops.git" + + maintainers("climbfuji") + + version("develop", branch="develop", no_cache=True) + # This commit plus the patch below accounts for commit + # 2340e9b664f82de9fa01c136c3a31d87e4a0bec9 in NRL GitHub + version("1.10.0.20250827", commit="91889ad09d3789f14a1184701dd80a4913d3ce3e") + + patch("include_algorithm.patch", when="@1.10.0.20250827") + + variant("l95", default=True, description="Build LORENZ95 toy model") + variant("mkl", default=False, description="Use MKL for LAPACK implementation (if available)") + variant("openmp", default=True, description="Build oops with OpenMP support") + variant("qg", default=True, description="Build QG toy model") + variant('gptl', default=False, description='Use GPTL profiling library (if available)') + + generator("make") + + # Project doesn't list "c" as a dependency in CMakeLists.txt, but cmake step fails w/o it + depends_on("c", type="build") + depends_on("cxx", type="build") + depends_on("fortran", type="build") + + depends_on("boost@1.64:") + depends_on("cmake", type=("build")) + depends_on("cmake@3.12:", type=("build"), when="@1.10:") + depends_on("ecbuild", type=("build")) + depends_on("ecbuild@3.3.2:", type=("build"), when="@1.10:") + depends_on("eckit") + depends_on("eckit@1.24.4:", when="@1.10:") + depends_on("ecmwf-atlas") + depends_on("ecmwf-atlas@0.35.0:", when="@1.10:") + depends_on("eigen") + depends_on("fckit") + depends_on("fckit@0.11.0:", when="@1.10:") + depends_on('gptl', when='+gptl') + depends_on("jedi-cmake", type=("build")) + depends_on("lapack", when="~mkl") + depends_on("mkl", when="+mkl") + depends_on("llvm-openmp", when="+openmp %apple-clang", type=("build", "link", "run")) + depends_on("mpi") + depends_on("netcdf-c+mpi") + depends_on("netcdf-fortran") + depends_on("nlohmann-json") + depends_on("nlohmann-json-schema-validator") + + def cmake_args(self): + res = [ + self.define_from_variant("ENABLE_LORENZ95_MODEL", "l95"), + self.define_from_variant("ENABLE_QG_MODEL", "qg"), + self.define_from_variant("ENABLE_MKL", "mkl"), + self.define_from_variant("OPENMP", "openmp"), + self.define_from_variant("ENABLE_GPTL", "gptl"), + ] + return res diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/ropp_ufo/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/ropp_ufo/package.py new file mode 100644 index 000000000..1a99bc190 --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/ropp_ufo/package.py @@ -0,0 +1,34 @@ +# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.cmake import CMakePackage, generator + +from spack.package import * + + +class RoppUfo(CMakePackage): + """Unified Forward Operator Interface for Radio Occultation Pre-processing package (ROPP)""" + + # DH* TODO CHANGE BACK TO JCSDA-INTERNAL ONCE THE NRL VERSION WAS MERGED BACK + homepage = "https://github.nrlmry.navy.mil/jcsda/ropp-ufo" + git = "https://github.nrlmry.navy.mil/jcsda/ropp-ufo.git" + + maintainers("climbfuji") + + version("develop", branch="develop", no_cache=True) + # this is the ropp-submodule branch ... update once merged + version("11.0.20251022", commit="02e21ef0696e67675184797427af265c06973ecf", submodules=True) + + # Project doesn't list "c" as a dependency in CMakeLists.txt, but cmake step fails w/o it + depends_on("c", type=("build")) + depends_on("fortran", type=("build")) + + depends_on("cmake", type=("build")) + depends_on("cmake@3.12:", type=("build"), when="@11:") + depends_on("ecbuild", type=("build")) + depends_on("ecbuild@3.3.2:", type=("build"), when="@11:") + depends_on("jedi-cmake", type=("build")) + depends_on("netcdf-c") + depends_on("netcdf-fortran") diff --git a/repos/spack_stack/spack_repo/spack_stack/packages/ufo/package.py b/repos/spack_stack/spack_repo/spack_stack/packages/ufo/package.py new file mode 100644 index 000000000..9cb0e8752 --- /dev/null +++ b/repos/spack_stack/spack_repo/spack_stack/packages/ufo/package.py @@ -0,0 +1,98 @@ +# Copyright 2013-2022 Lawrence Livermore National Security, LLC and other +# Spack Project Developers. See the top-level COPYRIGHT file for details. +# +# SPDX-License-Identifier: (Apache-2.0 OR MIT) + +from spack_repo.builtin.build_systems.cmake import CMakePackage, generator + +from spack.package import * + + +class Ufo(CMakePackage): + """Unified Forward Operator""" + + homepage = "https://github.com/JCSDA/ufo" + git = "https://github.com/JCSDA/ufo.git" + + maintainers("climbfuji") + + version("develop", branch="develop", no_cache=True) + version("1.10.0.20250821", commit="1ca49e253caa6d6a507f41ffa6875e0db7cc0751") + + patch( + "https://github.com/jcsda/ufo/commit/ce4cbf8d9dbfd11ac5f2d4add61aa1c1bbc075fc.patch?full_index=1", + sha256="b12aa105e8058409e5897fcc1d18054f4aef7fdb444bbb0341c1af6e0025197b", + when="@1.10.0.20250821", + ) + + variant("crtm-v2", default=True, description="Build CRTM v2 operator") + variant("crtm-v3", default=False, description="Build CRTM v3 operator") + # JCSDA-internal repository needed. + variant("geos-aero", default=False, description="Build GEOS-AERO AOD operator") + # Package gsw not yet available in spack + variant("gsw", default=False, description="Build marine observation operators") + # JCSDA-internal repository is public, but there is no "release" of the code yet. + variant( + "oasim", default=False, description="Build with Ocean Atmosphere Spectral Irradiance Model" + ) + # NRL-internal repository needed. + variant("ropp", default=False, description="Build ROPP operator") + # JCSDA-internal repository needed. + variant("rttov", default=False, description="Build RTTOV operator") + + conflicts("+crtm-v2 +crtm-v3", msg="UFO: choose either CRTM v2 or v3, not both.") + + conflicts("+geos-aero", msg="UFO: GEOS-AERO to be implemented.") + conflicts("+gsw", msg="UFO: GSW to be implemented.") + conflicts("+oasim", msg="UFO: OASIM to be implemented.") + conflicts("+rttov", msg="UFO: RTTOV to be implemented.") + + # Project doesn't list "c" as a dependency in CMakeLists.txt, but cmake step fails w/o it + depends_on("c", type=("build")) + depends_on("cxx", type=("build")) + depends_on("fortran", type=("build")) + + # DH* TODO FIX THIS (ALSO IN IODA ETC - CHECK ALL) + # -- Download test data from https://bin.ssec.wisc.edu/pub/s4/CRTM/file/crtm_coefficients_2.4.1_skylab_4.0.tar.gz + + depends_on("boost") + depends_on("cmake", type=("build")) + depends_on("cmake@3.12:", type=("build"), when="@1.10:") + depends_on("ecbuild", type=("build")) + depends_on("ecbuild@3.3.2:", type=("build"), when="@1.10:") + depends_on("eckit") + depends_on("eckit@1.24.4:", when="@1.10:") + depends_on("eigen") + depends_on("fckit") + depends_on("fckit@0.11.0:", when="@1.10:") + depends_on("gsl-lite") + depends_on("ioda") + depends_on("ioda@2.9", when="@1.10:") + depends_on("jedi-cmake", type=("build")) + depends_on("mpi") + depends_on("netcdf-c+mpi") + depends_on("netcdf-fortran") + depends_on("oops") + depends_on("oops@1.10", when="@1.10") + + depends_on("crtm@v2", when="+crtm-v2") + # DOES THIS INCLUDE THE ONEAPI IFX OPENMP BUG FIX? DH* TODO + depends_on("crtm@=v2.4.1-jedi.2", when="@1.10 +crtm-v2") + + depends_on("crtm@3", when="+crtm-v3") + depends_on("crtm@=3.1.2", when="@1.10 +crtm-v3") + + # depends_on('geos-aero', when='+geos-aero') + # depends_on('geos-aero@0.0.0', when='@1.7.0 +geos-aero') + + # depends_on('oasim', when='+oasim') + # depends_on('oasim@0.0.0', when='@1.7.0 +oasim') + + # depends_on("gsw", when="+gsw") + # depends_on("gsw@3.0.7", when="@1.7: +gsw") + + depends_on('ropp-ufo', when='+ropp') + depends_on('ropp-ufo@11.0', when='@1.10 +ropp') + + # depends_on('rttov', when='+rttov') + # depends_on('rttov@12.1.0', when='@1.7.0 +rttov') diff --git a/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack.py b/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack.py index c26630594..7abd05640 100644 --- a/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack.py +++ b/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack.py @@ -8,6 +8,10 @@ setup_meta_modules_parser, stack_setup_meta_modules, ) +from spack.extensions.stack.cmd.stack_cmds.check_preferred_compiler import ( + setup_preferred_compiler_parser, + stack_check_preferred_compiler, +) from spack.extensions.stack.stack_paths import stack_path description = "Create spack-stack environment" @@ -26,10 +30,17 @@ def setup_parser(subparser): sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='stack_command') create_parser = sp.add_parser('create', help='Create spack-stack environment or container.') - meta_modules_parser = sp.add_parser('setup-meta-modules', - help='Create lmod/lua or tcl/tk meta-modules') + meta_modules_parser = sp.add_parser( + 'setup-meta-modules', + help='Create lmod/lua or tcl/tk meta-modules', + ) + preferred_compiler_parser = sp.add_parser( + 'check-preferred-compiler', + help='Check that the preferred compiler is being used', + ) setup_create_parser(create_parser) setup_meta_modules_parser(meta_modules_parser) + setup_preferred_compiler_parser(preferred_compiler_parser) # Main command that calls subcommands @@ -38,3 +49,5 @@ def stack(parser, args): stack_create(parser, args) if args.stack_command == 'setup-meta-modules': stack_setup_meta_modules(parser, args) + if args.stack_command == 'check-preferred-compiler': + stack_check_preferred_compiler(parser, args) diff --git a/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack_cmds/check_preferred_compiler.py b/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack_cmds/check_preferred_compiler.py new file mode 100644 index 000000000..13f4d1120 --- /dev/null +++ b/spack-ext/lib/jcsda-emc/spack-stack/stack/cmd/stack_cmds/check_preferred_compiler.py @@ -0,0 +1,14 @@ +from spack.extensions.stack.compiler_utils import check_preferred_compiler + +description = "Check preferred compiler" +section = "spack-stack" +level = "long" + + +# Add potential arguments to check-preferred-compiler +def setup_preferred_compiler_parser(subparser): + pass + + +def stack_check_preferred_compiler(parser, args): + check_preferred_compiler() diff --git a/spack-ext/lib/jcsda-emc/spack-stack/stack/common.py b/spack-ext/lib/jcsda-emc/spack-stack/stack/common.py new file mode 100755 index 000000000..5379715e1 --- /dev/null +++ b/spack-ext/lib/jcsda-emc/spack-stack/stack/common.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +# Terminal colors +RED = "\033[91m" +GREEN = "\033[92m" +YELLOW = "\033[93m" +BLUE = "\033[94m" +RESET = "\033[0m" + +# Colored log levels +INFO_LABEL = "INFO: " +ERROR_LABEL = "\033[91mERROR:\033[0m " + +# Aliases to shorten module paths for tcl modules. These aliases must match +# the compiler and MPI name translations in configs/common/modules_tcl.yaml +ALIASES = { + "none" : "none", + # Compilers + "gcc" : "gcc", + "intel-oneapi-compilers-classic" : "intel", + "intel-oneapi-compilers" : "oneapi", + "llvm" : "llvm", + # MPI + "cray-mpich" : "cray-mpich", + "intel-oneapi-mpi" : "impi", + "mpich" : "mpich", + "mpt" : "mpt", + "openmpi" : "openmpi", +} + + +def get_preferred_compiler(config): + """Determine the preferred compiler by looking at + packages: + fortran: + prefer: + - COMPILER_NAME (gcc, intel-oneapi-compilers, llvm, ..) + """ + try: + preferred_compilers = config.get("packages")["fortran"]["prefer"] + except: + raise Exception( + """Unable to detect preferred compiler from environment. + Does the environment have the config entry 'packages:fortran:prefer?'""" + ) + if len(preferred_compilers)>1: + raise Exception(f"Invalid value for packages:fortran:prefer is {preferred_compilers}") + preferred_compiler = preferred_compilers[0] + return preferred_compiler diff --git a/spack-ext/lib/jcsda-emc/spack-stack/stack/compiler_utils.py b/spack-ext/lib/jcsda-emc/spack-stack/stack/compiler_utils.py new file mode 100755 index 000000000..3bc165cc6 --- /dev/null +++ b/spack-ext/lib/jcsda-emc/spack-stack/stack/compiler_utils.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 + +import copy +import logging +import os +import re +import sys + +import spack +import spack.environment as ev +from spack.provider_index import ProviderIndex + +from spack.extensions.stack.common import ALIASES +from spack.extensions.stack.common import RED, RESET +from spack.extensions.stack.common import get_preferred_compiler + + +def get_compiler_name_and_version(string): + compiler_name = string.replace("@=", "@").split("@")[0] + try: + compiler_version = string.replace("@=", "@").split("@")[1] + except: + compiler_version = None + return (compiler_name, compiler_version) + + +def get_compiler_choice(string): + """Parse string for a Spack version 1 compiler dependency + declaration. By intentionally not matching old (spack v0) + compiler dependency declarations ("%gcc", "%oneapi", ...), + we force updating the Spack configuration files to v1.""" + COMPILER_CHOICE_REGEX_STRING = "^(%+)(" + \ + "c=|" + \ + "cxx=|" + \ + "fortran=|" + \ + "c,cxx=|" + \ + "cxx,c=|" + \ + "c,fortran=|" + \ + "fortran,c=|" + \ + "cxx,fortran=|" + \ + "fortran,cxx=|" + \ + "c,cxx,fortran=|" + \ + "fortran,c,cxx=|" + \ + "cxx,fortran,c=|" + \ + "c,fortran,cxx=|" + \ + "cxx,c,fortran=|" + \ + "fortran,cxx,c=)(\S+)\s*$" + COMPILER_CHOICE_REGEX = re.compile(COMPILER_CHOICE_REGEX_STRING) + match = COMPILER_CHOICE_REGEX.match(string) + if match: + return match.group(3) + return None + + +def check_preferred_compiler(): + """For an active environment, check that the preferred compiler + is being used for all packages except those that explicitly + request a different compiler. For the latter packages, check + that the explicitly requested compiler is being used.""" + + logging.info("Configuring active spack environment ...") + env_dir = ev.active_environment().path + if not env_dir: + raise Exception("No active spack environment") + env = spack.environment.Environment(env_dir) + spack.environment.environment.activate(env) + logging.info(" ... environment directory: {}".format(env_dir)) + + # Get all specs and determine compilers + specs = env.all_specs() + if not specs: + raise Exception(f"{RED}No specs found - did you run 'spack concretize'?{RESET}") + q = ProviderIndex(specs=specs, repository=spack.repo.PATH) + + c_providers = q.providers_for("c") + cxx_providers = q.providers_for("cxx") + fortran_providers = q.providers_for("fortran") + compilers = list(set(c_providers + cxx_providers + fortran_providers)) + if not compilers: + raise Exception(f"{RED}No compilers found{RESET}!") + logging.info(f" ... compilers: {compilers}") + + # Determine the preferred compiler + preferred_compiler = get_preferred_compiler(spack.config) + (preferred_compiler_name, preferred_compiler_version) = get_compiler_name_and_version(preferred_compiler) + logging.info(" ... preferred compiler: {}".format(preferred_compiler)) + + # Get package config to compare actual specs against the intended config + package_config = spack.config.get("packages") + + logging.info("Checking all specs ...") + errors = 0 + for spec in specs: + # If the spec has no compiler dependency, an exception will be thrown - ignore package + try: + compiler_name = spec.compiler.name + compiler_version = spec.compiler.version if preferred_compiler_version else None + except: + logging.info(f" ... {spec.name}@{spec.version}/{spec.dag_hash(length=7)} has no compiler dependency") + continue + # If the spec compiler matches the preferred compiler for the environment, move on. + # Note that this permits situations where a packages has an explicit preferred (but + # not explicitly required) compiler, but Spack decides to use the preferred (and + # different) compiler for the environment instead. + if preferred_compiler_name == compiler_name and preferred_compiler_version == compiler_version: + logging.info(f" ... {spec.name}@{spec.version}/{spec.dag_hash(length=7)} uses preferred compiler") + else: + spec_required_compiler_name = None + spec_required_compiler_version = None + spec_preferred_compiler_name = None + spec_preferred_compiler_version = None + for key, value in package_config[spec.name].items(): + # To simplify parsing, turn scalar values into CommentedSeq of length 1 + if isinstance(value, (str, bytes)): + values = CommentedSeq([value]) + else: + values = value + # Loop through all values to check for required or preferred compilers + for entry in values: + if key.lower() == "require": + choice = get_compiler_choice(entry.lower()) + # Not a compiler preference, carry on + if not choice: + continue + # Check that the explicitly required compiler is a valid (existing) + # compiler for this environment. This requirement may be relaxed in + # the future if we start building compilers in spack environments. + if any(choice in c for c in compilers): + (spec_required_compiler_name, spec_required_compiler_version) = get_compiler_name_and_version(choice) + elif key.lower() == "prefer": + choice = get_compiler_choice(entry.lower()) + # Not a compiler preference, carry on + if not choice: + continue + # Check that the explicitly preferred compiler is a valid (existing) + # compiler for this environment. This requirement may be relaxed in + # the future if we start building compilers in spack environments. + if any(choice in c for c in compilers): + (spec_preferred_compiler_name, spec_preferred_compiler_version) = get_compiler_name_and_version(choice) + # If we have a hard requirement for a compiler, we can stop scanning the spec package config + if spec_required_compiler_name: + break + if spec_required_compiler_name == compiler_name and \ + ( (not spec_required_compiler_version or not compiler_version) or \ + (spec_required_compiler_version==compiler_version) ): + logging.info(f" ... {spec.name}@{spec.version}/{spec.dag_hash(length=7)} uses explicitly required compiler") + elif spec_preferred_compiler_name == compiler_name and \ + ( (not spec_preferred_compiler_version or not compiler_version) or \ + (spec_preferred_compiler_version==compiler_version) ): + logging.info(f" ... {spec.name}@{spec.version}/{spec.dag_hash(length=7)} uses explicitly preferred compiler") + else: + errors += 1 + logging.error(f" ... {RED}error: {spec.name}@{spec.version}/{spec.dag_hash(length=7)} does not use intended compiler\n" + \ + f" check also that any explicit preferred/required compiler dependencies are using Spack v1 syntax{RESET}") + if errors: + raise Exception(f"{RED}Detected {errors} compiler mismatches!{RESET}") diff --git a/spack-ext/lib/jcsda-emc/spack-stack/stack/meta_modules.py b/spack-ext/lib/jcsda-emc/spack-stack/stack/meta_modules.py index 0411dec69..a31fb1d92 100755 --- a/spack-ext/lib/jcsda-emc/spack-stack/stack/meta_modules.py +++ b/spack-ext/lib/jcsda-emc/spack-stack/stack/meta_modules.py @@ -8,9 +8,11 @@ import spack import spack.environment as ev -# import spack.repo from spack.provider_index import ProviderIndex +from spack.extensions.stack.common import ALIASES +from spack.extensions.stack.common import get_preferred_compiler + # logging.basicConfig(level=logging.INFO) logging.basicConfig(format="%(message)s", level=logging.DEBUG) @@ -52,24 +54,6 @@ "MPIROOT": "", } -# Aliases to shorten module paths for tcl modules. These aliases must match -# the compiler and MPI name translations in configs/common/modules_tcl.yaml -ALIASES = { - "none" : "none", - # Compilers - "gcc" : "gcc", - "intel-oneapi-compilers-classic" : "intel", - "intel-oneapi-compilers" : "oneapi", - "llvm" : "llvm", - # MPI - "cray-mpich" : "cray-mpich", - # Do we still need intel-mpi, and if yes, use the same impi? - "intel-oneapi-mpi" : "impi", - "mpich" : "mpich", - "mpt" : "mpt", - "openmpi" : "openmpi", -} - def setenv_command(module_choice, key, value): if module_choice == "lmod": @@ -172,26 +156,6 @@ def substitute_config_vars(config_str): return config_str -def get_preferred_compiler(): - """Determine the preferred compiler by looking at - packages: - fortran: - prefer: - - COMPILER_NAME (gcc, intel-oneapi-compilers, llvm, ..) - """ - try: - preferred_compilers = spack.config.get("packages")["fortran"]["prefer"] - except: - raise Exception( - """Unable to detect preferred compiler from environment. - Does the environment have the config entry 'packages:fortran:prefer?'""" - ) - if len(preferred_compilers)>1: - raise Exception(f"Invalid value for packages:fortran:prefer is {preferred_compilers}") - preferred_compiler = preferred_compilers[0] - return preferred_compiler - - def remove_compiler_prefices_from_tcl_modulefiles(modulepath, compiler_list, mpi_provider, module_choice): """Remove compiler and mpi prefices from tcl modulefiles in modulepath""" logging.info(f" ... ... removing compiler/mpi prefices from tcl modulefiles in {modulepath}") @@ -243,7 +207,12 @@ def remove_compiler_prefices_from_tcl_modulefiles(modulepath, compiler_list, mpi def setup_meta_modules(): - # Find currently active spack environment, activate here + """For an active environment, create meta-modules for the preferred + compiler and the MPI provider (compiled with the preferred compiler). + For tcl/tk environment modules, remove modulepath prefices from the + spack-generated modules and implement a module hiearchy modeled after + the lua/lmod modules.""" + logging.info("Configuring active spack environment ...") env_dir = ev.active_environment().path if not env_dir: @@ -282,8 +251,7 @@ def setup_meta_modules(): logging.info(f" ... module directory: {module_dir}") # Get all specs and determine compilers - hashes = env.all_hashes() - specs = spack.store.STORE.db.query(hashes=hashes) + specs = env.all_specs() q = ProviderIndex(specs=specs, repository=spack.repo.PATH) c_providers = q.providers_for("c") @@ -320,7 +288,7 @@ def setup_meta_modules(): # takes it and adds it to the stack-COMPILER metamodule. Likewise, we need # to save the list of compiler substitutions from the preferred compiler # so that we have access to it when we build the MPI meta module. - preferred_compiler = get_preferred_compiler() + preferred_compiler = get_preferred_compiler(spack.config) logging.info(" ... preferred compiler: {}".format(preferred_compiler)) # Sort the list using a custom key @@ -330,7 +298,7 @@ def custom_sort_key(entry): return (1 if preferred_compiler in entry else 0, entry) compilers = sorted(compilers, key=custom_sort_key) - # Get mpi providers (currently only one mpi provider is supported) + # Get mpi providers (currently only one mpi provider is supported) mpi_providers = q.providers_for("mpi") if len(mpi_providers)>1: raise Exception(f"Expected no or one MPI provider, but got {mpi_providers}")