diff --git a/.gitmodules b/.gitmodules index e2e2e815e..dc0798c32 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,7 @@ path = ccpp/physics url = https://github.com/NCAR/ccpp-physics branch = main +[submodule "CMakeModules"] + path = CMakeModules + url = https://github.com/noaa-emc/CMakeModules + branch = develop diff --git a/CMakeModules b/CMakeModules new file mode 160000 index 000000000..0065b18c1 --- /dev/null +++ b/CMakeModules @@ -0,0 +1 @@ +Subproject commit 0065b18c1ad586c17dc2a3c88bb2f52476c79eaf diff --git a/contrib/build_nceplibs.sh b/contrib/build_nceplibs.sh deleted file mode 100755 index d471c252b..000000000 --- a/contrib/build_nceplibs.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -set -e - -if [ "$#" -ne 1 ]; then - echo "Illegal number of parameters, need to specify install directory for NCEPLIBS." - exit 1 -fi - -NCEPLIBS_DIR=$1 -if [ -d $NCEPLIBS_DIR ]; then - while true; do - read -p "Warning, destination $NCEPLIBS_DIR already exists. Proceed [y/n]? " yn - case $yn in - [Yy]* ) break;; - [Nn]* ) exit;; - * ) echo "Please answer yes or no.";; - esac - done -fi -NCEPLIBS_SRC=$NCEPLIBS_DIR/src -mkdir -p $NCEPLIBS_SRC - -cd $NCEPLIBS_SRC -git clone -b v2.4.1 --recursive https://github.com/NOAA-EMC/NCEPLIBS-bacio -cd NCEPLIBS-bacio -mkdir build -cd build -cmake -DCMAKE_INSTALL_PREFIX=$NCEPLIBS_DIR .. -make VERBOSE=1 -make install - -cd ../.. -git clone -b v2.3.3 --recursive https://github.com/NOAA-EMC/NCEPLIBS-sp -cd NCEPLIBS-sp -mkdir build -cd build -cmake -DCMAKE_INSTALL_PREFIX=$NCEPLIBS_DIR .. -make VERBOSE=1 -make install - -cd ../.. -git clone -b v2.4.1 --recursive https://github.com/NOAA-EMC/NCEPLIBS-w3nco -cd NCEPLIBS-w3nco -mkdir build -cd build -cmake -DCMAKE_INSTALL_PREFIX=$NCEPLIBS_DIR .. -make VERBOSE=1 -make install - -echo " " -echo " " -echo "Set the following environment variables for building the single column model:" -echo "=============================================================================" -echo "# for bash" -echo "export bacio_ROOT=$NCEPLIBS_DIR" -echo "export sp_ROOT=$NCEPLIBS_DIR" -echo "export w3nco_ROOT=$NCEPLIBS_DIR" -echo "# for csh" -echo "setenv bacio_ROOT $NCEPLIBS_DIR" -echo "setenv sp_ROOT $NCEPLIBS_DIR" -echo "setenv w3nco_ROOT $NCEPLIBS_DIR" -echo "=============================================================================" -echo " " -echo " " diff --git a/contrib/get_mg_inccn_data.sh b/contrib/get_mg_inccn_data.sh index c4fa5cd4e..aa90774f5 100755 --- a/contrib/get_mg_inccn_data.sh +++ b/contrib/get_mg_inccn_data.sh @@ -12,6 +12,8 @@ BASEDIR=$MYDIR/.. # Change to directory containing the physics input data, download and extract archive cd $BASEDIR/scm/data/physics_input_data/ -wget https://github.com/NCAR/gmtb-scm/releases/download/v5.0.0-alpha/MG_INCCN_data.tar +wget https://github.com/NCAR/ccpp-scm/releases/download/v5.0.0-alpha/MG_INCCN_data.tar tar -xvf MG_INCCN_data.tar rm -f MG_INCCN_data.tar +cd $BASEDIR/ + diff --git a/contrib/get_thompson_tables.sh b/contrib/get_thompson_tables.sh index 51b9b7cd7..9ea44c6c3 100755 --- a/contrib/get_thompson_tables.sh +++ b/contrib/get_thompson_tables.sh @@ -15,3 +15,5 @@ cd $BASEDIR/scm/data/physics_input_data/ wget https://github.com/NCAR/ccpp-scm/releases/download/v5.0.0/thompson_tables2.tar tar -xvf thompson_tables2.tar rm -f thompson_tables2.tar +cd $BASEDIR/ + diff --git a/scm/etc/CENTOS_docker_setup.sh b/scm/etc/CENTOS_docker_setup.sh index 1a519050b..c7164cfd0 100755 --- a/scm/etc/CENTOS_docker_setup.sh +++ b/scm/etc/CENTOS_docker_setup.sh @@ -2,7 +2,9 @@ echo "Setting environment variables for SCM-CCPP on CENTOS with gcc/gfortran" -export SCM_ROOT=$PWD +MYDIR=$(cd "$(dirname "$(readlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) + +export SCM_ROOT=$MYDIR/../.. export CC=/opt/rh/devtoolset-9/root/usr/bin/gcc export CXX=/opt/rh/devtoolset-9/root/usr/bin/g++ diff --git a/scm/etc/Cheyenne_setup_gnu.csh b/scm/etc/Cheyenne_setup_gnu.csh index 013709868..50bc8df0b 100755 --- a/scm/etc/Cheyenne_setup_gnu.csh +++ b/scm/etc/Cheyenne_setup_gnu.csh @@ -2,7 +2,17 @@ echo "Setting environment variables for CCPP-SCM on Cheyenne with gcc/gfortran" -setenv SCM_ROOT $PWD +set called=($_) + +if ( "$called" != "") then ### called by source + set MYSCRIPT=`readlink -f -n $called[2]` +else ### called by direct execution of the script + set MYSCRIPT=`readlink -f -n '$0'` +endif +set MYDIR=`dirname $MYSCRIPT` +set MYDIR=`cd $MYDIR && pwd -P` + +setenv SCM_ROOT $MYDIR/../.. #start with a "clean" environment; activate and deactivate ncar_pylib in order to successfully deactivate previously activated environment without errors module load ncarenv/1.3 diff --git a/scm/etc/Cheyenne_setup_gnu.sh b/scm/etc/Cheyenne_setup_gnu.sh index 09fc32c44..2ffe04b9c 100755 --- a/scm/etc/Cheyenne_setup_gnu.sh +++ b/scm/etc/Cheyenne_setup_gnu.sh @@ -2,7 +2,9 @@ echo "Setting environment variables for CCPP-SCM on Cheyenne with gcc/gfortran" -export SCM_ROOT=$PWD +MYDIR=$(cd "$(dirname "$(readlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) + +export SCM_ROOT=$MYDIR/../.. #start with a "clean" environment; activate and deactivate ncar_pylib in order to successfully deactivate previously activated environment without errors module load ncarenv/1.3 diff --git a/scm/etc/Cheyenne_setup_intel.csh b/scm/etc/Cheyenne_setup_intel.csh index 9254e0c4e..33538215e 100755 --- a/scm/etc/Cheyenne_setup_intel.csh +++ b/scm/etc/Cheyenne_setup_intel.csh @@ -2,7 +2,17 @@ echo "Setting environment variables for CCPP-SCM on Cheyenne with icc/ifort" -setenv SCM_ROOT $PWD +set called=($_) + +if ( "$called" != "") then ### called by source + set MYSCRIPT=`readlink -f -n $called[2]` +else ### called by direct execution of the script + set MYSCRIPT=`readlink -f -n '$0'` +endif +set MYDIR=`dirname $MYSCRIPT` +set MYDIR=`cd $MYDIR && pwd -P` + +setenv SCM_ROOT $MYDIR/../.. #start with a "clean" environment; activate and deactivate ncar_pylib in order to successfully deactivate previously activated environment without errors module load ncarenv/1.3 diff --git a/scm/etc/Cheyenne_setup_intel.sh b/scm/etc/Cheyenne_setup_intel.sh index c29d3e4fc..7ec357324 100755 --- a/scm/etc/Cheyenne_setup_intel.sh +++ b/scm/etc/Cheyenne_setup_intel.sh @@ -2,7 +2,9 @@ echo "Setting environment variables for CCPP-SCM on Cheyenne with icc/ifort" -export SCM_ROOT=$PWD +MYDIR=$(cd "$(dirname "$(readlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) + +export SCM_ROOT=$MYDIR/../.. #start with a "clean" environment; activate and deactivate ncar_pylib in order to successfully deactivate previously activated environment without errors module load ncarenv/1.3 diff --git a/scm/etc/Desktop_setup_gfortran.csh b/scm/etc/Desktop_setup_gfortran.csh index 9d39d0d28..d038405f4 100755 --- a/scm/etc/Desktop_setup_gfortran.csh +++ b/scm/etc/Desktop_setup_gfortran.csh @@ -2,7 +2,17 @@ echo "Setting environment variables for CCPP-SCM on Desktop (MacOS) with gcc/gfortran" -setenv SCM_ROOT $PWD +set called=($_) + +if ( "$called" != "") then ### called by source + set MYSCRIPT=`readlink -f -n $called[2]` +else ### called by direct execution of the script + set MYSCRIPT=`readlink -f -n '$0'` +endif +set MYDIR=`dirname $MYSCRIPT` +set MYDIR=`cd $MYDIR && pwd -P` + +setenv SCM_ROOT $MYDIR/../.. echo "Setting CC/CXX/FC environment variables" setenv CC /opt/local/bin/gcc-mp-10 diff --git a/scm/etc/Desktop_setup_gfortran.sh b/scm/etc/Desktop_setup_gfortran.sh index b178772fd..37485a163 100755 --- a/scm/etc/Desktop_setup_gfortran.sh +++ b/scm/etc/Desktop_setup_gfortran.sh @@ -2,7 +2,13 @@ echo "Setting environment variables for CCPP-SCM on Desktop (MacOS) with gcc/gfortran" -export SCM_ROOT=$PWD +if [[ $(uname -s) == Darwin ]]; then + MYDIR=$(cd "$(dirname "$(greadlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) +else + MYDIR=$(cd "$(dirname "$(readlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) +fi + +export SCM_ROOT=$MYDIR/../.. echo "Setting CC/CXX/FC environment variables" export CC=/opt/local/bin/gcc-mp-10 diff --git a/scm/etc/Hera_setup_intel.csh b/scm/etc/Hera_setup_intel.csh index fa2276619..6a7858433 100755 --- a/scm/etc/Hera_setup_intel.csh +++ b/scm/etc/Hera_setup_intel.csh @@ -2,7 +2,17 @@ echo "Setting environment variables for CCPP-SCM on Hera with icc/ifort" -setenv SCM_ROOT $PWD +set called=($_) + +if ( "$called" != "") then ### called by source + set MYSCRIPT=`readlink -f -n $called[2]` +else ### called by direct execution of the script + set MYSCRIPT=`readlink -f -n '$0'` +endif +set MYDIR=`dirname $MYSCRIPT` +set MYDIR=`cd $MYDIR && pwd -P` + +setenv SCM_ROOT $MYDIR/../.. #load the modules in order to compile the CCPP SCM echo "Loading intel and netcdf modules..." diff --git a/scm/etc/Hera_setup_intel.sh b/scm/etc/Hera_setup_intel.sh index 49ac72346..cf0d092a5 100755 --- a/scm/etc/Hera_setup_intel.sh +++ b/scm/etc/Hera_setup_intel.sh @@ -2,7 +2,9 @@ echo "Setting environment variables for CCPP-SCM on Hera with icc/ifort" -export SCM_ROOT=$PWD +MYDIR=$(cd "$(dirname "$(readlink -f -n "${BASH_SOURCE[0]}" )" )" && pwd -P) + +export SCM_ROOT=$MYDIR/../.. #load the modules in order to compile the CCPP SCM echo "Loading intel and netcdf modules..." diff --git a/scm/src/CMakeLists.txt b/scm/src/CMakeLists.txt index a1221089e..e9476f360 100644 --- a/scm/src/CMakeLists.txt +++ b/scm/src/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.14) project(scm VERSION 5.0.0 - LANGUAGES C CXX Fortran) + LANGUAGES C Fortran) set(PROJECT "CCPP-SCM") #################################################################### @@ -64,60 +64,12 @@ ENABLE_LANGUAGE(Fortran) include(CMakeForceCompiler) -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/../../CMakeModules/Modules) -# First, need to figure out if using the new NCEPLIBS-external/NCEPLIBS packages -# or the legacy/contrib netCDF and NCEPLIBS installations -IF(DEFINED ENV{bacio_ROOT}) - message(STATUS "Configuring SCM build for new NCEPLIBS-external/NCEPLIBS software packages") - set(NEW_NCEPLIBS ON) -ELSE() - message(STATUS "Configuring SCM build for legacy/contrib netCDF/NCEPLIBS software packages") - set(NEW_NCEPLIBS OFF) -ENDIF() - -IF(NEW_NCEPLIBS) - MESSAGE(STATUS "Using FindNetCDF.cmake module for $ENV{CMAKE_Platform}") - FIND_PACKAGE(NetCDF REQUIRED COMPONENTS C Fortran) -else() - IF(DEFINED ENV{NETCDF}) - MESSAGE(STATUS "The NETCDF environment variable is $ENV{NETCDF}") - SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{NETCDF}) - ELSE(DEFINED ENV{NETCDF}) - MESSAGE(FATAL_ERROR "The NETCDF environement variable must be set to point to your NetCDF installation before building. Stopping...") - ENDIF(DEFINED ENV{NETCDF}) -ENDIF() - -IF(NEW_NCEPLIBS) - find_package(bacio REQUIRED) - find_package(sp REQUIRED) - find_package(w3nco REQUIRED) -ELSE() - # Find bacio - IF(DEFINED ENV{BACIO_LIB4}) - MESSAGE(STATUS "The BACIO_LIB4 environment variable is $ENV{BACIO_LIB4}") - SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{BACIO_LIB4}) - SET(BACIO_LIB4 $ENV{BACIO_LIB4}) - ELSE() - MESSAGE(FATAL_ERROR "The BACIO_LIB4 environment variable must be set to point to your BACIO installation (part of NCEPLIBS) before building. Stopping...") - ENDIF() - # Find sp - IF(DEFINED ENV{SP_LIBd}) - MESSAGE(STATUS "The SP_LIBd environment variable is $ENV{SP_LIBd}") - SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{SP_LIBd}) - SET(SP_LIBd $ENV{SP_LIBd}) - ELSE(DEFINED ENV{SP_LIBd}) - MESSAGE(FATAL_ERROR "The SP_LIBd environment variable must be set to point to your SP installation (part of NCEPLIBS) before building. Stopping...") - ENDIF(DEFINED ENV{SP_LIBd}) - # Find w3nco - IF(DEFINED ENV{W3NCO_LIBd}) - MESSAGE(STATUS "The W3NCO_LIBd environment variable is $ENV{W3NCO_LIBd}") - SET(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} $ENV{W3NCO_LIBd}) - SET(W3NCO_LIBd $ENV{W3NCO_LIBd}) - ELSE(DEFINED ENV{W3NCO_LIBd}) - MESSAGE(FATAL_ERROR "The W3NCO_LIBd environment variable must be set to point to your W3NCO installation (part of NCEPLIBS) before building. Stopping...") - ENDIF(DEFINED ENV{W3NCO_LIBd}) -ENDIF() +find_package(NetCDF REQUIRED COMPONENTS C Fortran) +find_package(bacio REQUIRED) +find_package(sp REQUIRED) +find_package(w3nco REQUIRED) SET(CCPP_FRAMEWORK_SRC ${CMAKE_SOURCE_DIR}/../../ccpp/framework) SET(CCPP_PHYSICS_SRC ${CMAKE_SOURCE_DIR}/../../ccpp/physics) @@ -132,8 +84,7 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release") endif() INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/ccpp/framework/src) @@ -143,7 +94,7 @@ INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}/ccpp/physics) # Add required preprocessor flags for build type if (${CMAKE_BUILD_TYPE} MATCHES "Debug") ADD_DEFINITIONS(-DDEBUG) -endif (${CMAKE_BUILD_TYPE} MATCHES "Debug") +endif () #------------------------------------------------------------------------------ # Add -DCCPP preprocessor flag (needed to preprocess GFS_typedefs.F90 from FV3) @@ -164,7 +115,7 @@ if(APPLE) ADD_DEFINITIONS(-DMACOSX) elseif(UNIX) ADD_DEFINITIONS(-DLINUX) -else (APPLE) +else () message (FATAL_ERROR "Unsupported platform, only Linux and MacOSX are supported at this time.") endif() @@ -196,10 +147,8 @@ if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") endif() set(CMAKE_C_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_Fortran_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_C_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) - set(CMAKE_CXX_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_Fortran_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -g -traceback -fpp -fno-alias -auto -safe-cray-ptr -ftz -assume byterecl -nowarn -sox -align array64byte -qno-opt-dynamic-align") @@ -233,10 +182,8 @@ elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel") endif() set(CMAKE_C_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) - set(CMAKE_CXX_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_Fortran_FLAGS_RELEASE "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_C_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) - set(CMAKE_CXX_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) set(CMAKE_Fortran_FLAGS_BITFORBIT "-O2 -fPIC" CACHE STRING "" FORCE) else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") message (FATAL_ERROR "This program has only been compiled with gfortran and ifort. If another compiler is needed, the appropriate flags must be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt") @@ -263,79 +210,19 @@ else() endif() #------------------------------------------------------------------------------ -# Set netCDF flags for preprocessor, compiler and linker (if defined) -if(NEW_NCEPLIBS) - #If using hera.intel, we use the target_link_libraries(NetCDF::NetCDF_[Fortran,C,CXX]), but ccpp-phyics inherits ${CMAKE_Fortran_FLAGS} for compiling physics, so we still need to set these - ADD_DEFINITIONS(-DNETCDF) - message (STATUS "Enable netCDF support") - message (STATUS "NetCDF_Fortran_INCLUDE_DIRS is ${NetCDF_Fortran_INCLUDE_DIRS}") - message (STATUS "NetCDF_Fortran_LIBRARIES is ${NetCDF_Fortran_LIBRARIES}") - set (NETCDF_INC "-I${NetCDF_Fortran_INCLUDE_DIRS}") - set (NETCDF_LIB ${NetCDF_Fortran_LIBRARIES}) - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${NETCDF_INC} ${NETCDF_LIB}") -else() - set(NETCDF_DIR $ENV{NETCDF}) - if(NETCDF_DIR) - set (NETCDF_INC "-I${NETCDF_DIR}/include") - set (NETCDF_LIB "-L${NETCDF_DIR}/lib -lnetcdff -lnetcdf") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${NETCDF_INC} ${NETCDF_LIB}") - ADD_DEFINITIONS(-DNETCDF) - message (STATUS "Enable netCDF support") - else(NETCDF_DIR) - message (STATUS "Disable netCDF support") - endif(NETCDF_DIR) -endif() - -#------------------------------------------------------------------------------ -# Set SIONlib flags for preprocessor, compiler and linker (if defined) -set(SIONLIB $ENV{SIONLIB}) -if (SIONLIB) - execute_process(COMMAND ${SIONLIB}/bin/sionconfig --mpi --cflags --f90 - RESULT_VARIABLE exit_code - OUTPUT_VARIABLE SIONlib_Fortran_FLAGS - ERROR_VARIABLE stderr - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE) - if (NOT exit_code EQUAL "0") - message (FATAL_ERROR "Execution of '${SIONLIB}/bin/sionconfig --mpi --cflags --f90' failed") - endif (NOT exit_code EQUAL "0") - execute_process(COMMAND ${SIONLIB}/bin/sionconfig --mpi --libs --f90 - RESULT_VARIABLE exit_code - OUTPUT_VARIABLE SIONlib_Fortran_LIBS - ERROR_VARIABLE stderr - OUTPUT_STRIP_TRAILING_WHITESPACE - ERROR_STRIP_TRAILING_WHITESPACE) - if (NOT exit_code EQUAL "0") - message (FATAL_ERROR "Execution of '${SIONLIB}/bin/sionconfig --mpi --libs --f90' failed") - endif (NOT exit_code EQUAL "0") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${SIONlib_Fortran_FLAGS} ${SIONlib_Fortran_LIBS}") - ADD_DEFINITIONS(-DSION) - message (STATUS "Enable SIONlib support") -else (SIONLIB) - message (STATUS "Disable SIONlib support") -endif (SIONLIB) +# Set netCDF flags for preprocessor +ADD_DEFINITIONS(-DNETCDF) -#------------------------------------------------------------------------------ -# CMake Modules -# Set the CMake module path -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../../ccpp/framework/cmake") -#------------------------------------------------------------------------------ # Set OpenMP flags for C/C++/Fortran if (OPENMP) - # OpenMP broken for clang compiler + # Clang compiler does not support OpenMP out of the box if(CMAKE_CXX_COMPILER_ID MATCHES "Clang*") - message(FATAL_ERROR "OpenMP not supported for the LLVM Clang compiler") + message(FATAL_ERROR "OpenMP not supported for Apple/LLVM Clang compilers") endif() - include(detect_openmp) - detect_openmp() - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}") - message(STATUS "Enable OpenMP support for C/C++/Fortran compiler") -else(OPENMP) - message (STATUS "Disable OpenMP support for C/C++/Fortran compiler") + find_package(OpenMP REQUIRED) endif() +# How about using proper compile targets etc? #------------------------------------------------------------------------------ # Configure sources ADD_SUBDIRECTORY(${CCPP_FRAMEWORK_SRC} ${CMAKE_BINARY_DIR}/ccpp/framework) @@ -352,27 +239,25 @@ SET(scm_source_files scm.F90 scm_output.F90 ) - ADD_EXECUTABLE(scm ${scm_source_files} ccpp_static_api.F90) -if(NEW_NCEPLIBS) - #the FindNetCDF.cmake module suggests to use NetCDF::NetCDF_[Fortran,C,CXX] rather than set compiler flags; you actually need both Fortan and C components to compile successfully - TARGET_LINK_LIBRARIES(scm ccpp_physics ccpp_framework NetCDF::NetCDF_Fortran NetCDF::NetCDF_C bacio::bacio_4 sp::sp_d w3nco::w3nco_d) -else() - TARGET_LINK_LIBRARIES(scm ccpp_physics ccpp_framework ${BACIO_LIB4} ${SP_LIBd} ${W3NCO_LIBd}) -endif() -set_target_properties(scm PROPERTIES +if(OPENMP) + TARGET_LINK_LIBRARIES(scm PUBLIC OpenMP::OpenMP_Fortran) +endif() +TARGET_LINK_LIBRARIES(scm NetCDF::NetCDF_Fortran) +TARGET_LINK_LIBRARIES(scm bacio::bacio_4) +TARGET_LINK_LIBRARIES(scm sp::sp_d) +TARGET_LINK_LIBRARIES(scm w3nco::w3nco_d) +TARGET_LINK_LIBRARIES(scm ccpp_framework) +TARGET_LINK_LIBRARIES(scm ccpp_physics) + +SET_TARGET_PROPERTIES(scm PROPERTIES COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}" LINK_FLAGS "${CMAKE_Fortran_FLAGS}") -add_custom_command( +ADD_CUSTOM_COMMAND( TARGET scm POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/run_scm.py ${CMAKE_CURRENT_BINARY_DIR}/run_scm.py) -add_custom_command( - TARGET scm POST_BUILD - COMMAND ${CMAKE_COMMAND} -E create_symlink - ${CMAKE_SOURCE_DIR}/multi_run_scm.py - ${CMAKE_CURRENT_BINARY_DIR}/multi_run_scm.py) diff --git a/scm/src/cmake/modules/FindNetCDF.cmake b/scm/src/cmake/modules/FindNetCDF.cmake deleted file mode 100644 index 64d039554..000000000 --- a/scm/src/cmake/modules/FindNetCDF.cmake +++ /dev/null @@ -1,340 +0,0 @@ -# (C) Copyright 2011- ECMWF. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation nor -# does it submit to any jurisdiction. - -# Try to find NetCDF includes and library. -# Supports static and shared libaries and allows each component to be found in sepearte prefixes. -# -# This module defines -# -# - NetCDF_FOUND - System has NetCDF -# - NetCDF_INCLUDE_DIRS - the NetCDF include directories -# - NetCDF_VERSION - the version of NetCDF -# - NetCDF_CONFIG_EXECUTABLE - the netcdf-config executable if found -# - NetCDF_PARALLEL - Boolean True if NetCDF4 has parallel IO support via hdf5 and/or pnetcdf -# - NetCDF_HAS_PNETCDF - Boolean True if NetCDF4 has pnetcdf support -# -# Deprecated Defines -# - NetCDF_LIBRARIES - [Deprecated] Use NetCDF::NetCDF_ targets instead. -# -# -# Following components are available: -# -# - C - C interface to NetCDF (netcdf) -# - CXX - CXX4 interface to NetCDF (netcdf_c++4) -# - Fortran - Fortran interface to NetCDF (netcdff) -# -# For each component the following are defined: -# -# - NetCDF__FOUND - whether the component is found -# - NetCDF__LIBRARIES - the libraries for the component -# - NetCDF__LIBRARY_SHARED - Boolean is true if libraries for component are shared -# - NetCDF__INCLUDE_DIRS - the include directories for specified component -# - NetCDF::NetCDF_ - target of component to be used with target_link_libraries() -# -# The following paths will be searched in order if set in CMake (first priority) or environment (second priority) -# -# - NetCDF_ROOT - root of NetCDF installation -# - NetCDF_PATH - root of NetCDF installation -# -# The search process begins with locating NetCDF Include headers. If these are in a non-standard location, -# set one of the following CMake or environment variables to point to the location: -# -# - NetCDF_INCLUDE_DIR or NetCDF_${comp}_INCLUDE_DIR -# - NetCDF_INCLUDE_DIRS or NetCDF_${comp}_INCLUDE_DIR -# -# Notes: -# -# - Use "NetCDF::NetCDF_" targets only. NetCDF_LIBRARIES exists for backwards compatibility and should not be used. -# - These targets have all the knowledge of include directories and library search directories, and a single -# call to target_link_libraries will provide all these transitive properties to your target. Normally all that is -# needed to build and link against NetCDF is, e.g.: -# target_link_libraries(my_c_tgt PUBLIC NetCDF::NetCDF_C) -# - "NetCDF" is always the preferred naming for this package, its targets, variables, and environment variables -# - For compatibility, some variables are also set/checked using alternate names NetCDF4, NETCDF, or NETCDF4 -# - Environments relying on these older environment variable names should move to using a "NetCDF_ROOT" environment variable -# - Preferred component capitalization follows the CMake LANGUAGES variables: i.e., C, Fortran, CXX -# - For compatibility, alternate capitalizations are supported but should not be used. -# - If no components are defined, all components will be searched -# - -list( APPEND _possible_components C CXX Fortran ) - -## Include names for each component -set( NetCDF_C_INCLUDE_NAME netcdf.h ) -set( NetCDF_CXX_INCLUDE_NAME netcdf ) -set( NetCDF_Fortran_INCLUDE_NAME netcdf.mod ) - -## Library names for each component -set( NetCDF_C_LIBRARY_NAME netcdf ) -set( NetCDF_CXX_LIBRARY_NAME netcdf_c++4 ) -set( NetCDF_Fortran_LIBRARY_NAME netcdff ) - -## Enumerate search components -foreach( _comp ${_possible_components} ) - string( TOUPPER "${_comp}" _COMP ) - set( _arg_${_COMP} ${_comp} ) - set( _name_${_COMP} ${_comp} ) -endforeach() - -set( _search_components C) -foreach( _comp ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS} ) - string( TOUPPER "${_comp}" _COMP ) - set( _arg_${_COMP} ${_comp} ) - list( APPEND _search_components ${_name_${_COMP}} ) - if( NOT _name_${_COMP} ) - message(SEND_ERROR "Find${CMAKE_FIND_PACKAGE_NAME}: COMPONENT ${_comp} is not a valid component. Valid components: ${_possible_components}" ) - endif() -endforeach() -list( REMOVE_DUPLICATES _search_components ) - -## Search hints for finding include directories and libraries -foreach( _comp IN ITEMS "_" "_C_" "_Fortran_" "_CXX_" ) - foreach( _name IN ITEMS NetCDF4 NetCDF NETCDF4 NETCDF ) - foreach( _var IN ITEMS ROOT PATH ) - list(APPEND _search_hints ${${_name}${_comp}${_var}} $ENV{${_name}${_comp}${_var}} ) - list(APPEND _include_search_hints - ${${_name}${_comp}INCLUDE_DIR} $ENV{${_name}${_comp}INCLUDE_DIR} - ${${_name}${_comp}INCLUDE_DIRS} $ENV{${_name}${_comp}INCLUDE_DIRS} ) - endforeach() - endforeach() -endforeach() -#Old-school HPC module env variable names -foreach( _name IN ITEMS NetCDF4 NetCDF NETCDF4 NETCDF ) - foreach( _comp IN ITEMS "_C" "_Fortran" "_CXX" ) - list(APPEND _search_hints ${${_name}} $ENV{${_name}}) - list(APPEND _search_hints ${${_name}${_comp}} $ENV{${_name}${_comp}}) - endforeach() -endforeach() - -## Find headers for each component -set(NetCDF_INCLUDE_DIRS) -set(_new_search_components) -foreach( _comp IN LISTS _search_components ) - if(NOT ${PROJECT_NAME}_NetCDF_${_comp}_FOUND) - list(APPEND _new_search_components ${_comp}) - endif() - find_file(NetCDF_${_comp}_INCLUDE_FILE - NAMES ${NetCDF_${_comp}_INCLUDE_NAME} - DOC "NetCDF ${_comp} include directory" - HINTS ${_include_search_hints} ${_search_hints} - PATH_SUFFIXES include include/netcdf - ) - mark_as_advanced(NetCDF_${_comp}_INCLUDE_FILE) - message(DEBUG "NetCDF_${_comp}_INCLUDE_FILE: ${NetCDF_${_comp}_INCLUDE_FILE}") - if( NetCDF_${_comp}_INCLUDE_FILE ) - get_filename_component(NetCDF_${_comp}_INCLUDE_FILE ${NetCDF_${_comp}_INCLUDE_FILE} ABSOLUTE) - get_filename_component(NetCDF_${_comp}_INCLUDE_DIR ${NetCDF_${_comp}_INCLUDE_FILE} DIRECTORY) - list(APPEND NetCDF_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIR}) - endif() -endforeach() -if(NetCDF_INCLUDE_DIRS) - list(REMOVE_DUPLICATES NetCDF_INCLUDE_DIRS) -endif() -set(NetCDF_INCLUDE_DIRS "${NetCDF_INCLUDE_DIRS}" CACHE STRING "NetCDF Include directory paths" FORCE) - -## Find n*-config executables for search components -foreach( _comp IN LISTS _search_components ) - if( _comp MATCHES "^(C)$" ) - set(_conf "c") - elseif( _comp MATCHES "^(Fortran)$" ) - set(_conf "f") - elseif( _comp MATCHES "^(CXX)$" ) - set(_conf "cxx4") - endif() - find_program( NetCDF_${_comp}_CONFIG_EXECUTABLE - NAMES n${_conf}-config - HINTS ${NetCDF_INCLUDE_DIRS} ${_include_search_hints} ${_search_hints} - PATH_SUFFIXES bin Bin ../bin ../../bin - DOC "NetCDF n${_conf}-config helper" ) - message(DEBUG "NetCDF_${_comp}_CONFIG_EXECUTABLE: ${NetCDF_${_comp}_CONFIG_EXECUTABLE}") -endforeach() - -set(_C_libs_flag --libs) -set(_Fortran_libs_flag --flibs) -set(_CXX_libs_flag --libs) -set(_C_includes_flag --includedir) -set(_Fortran_includes_flag --includedir) -set(_CXX_includes_flag --includedir) -function(netcdf_config exec flag output_var) - set(${output_var} False PARENT_SCOPE) - if( exec ) - execute_process( COMMAND ${exec} ${flag} RESULT_VARIABLE _ret OUTPUT_VARIABLE _val) - if( _ret EQUAL 0 ) - string( STRIP ${_val} _val ) - set( ${output_var} ${_val} PARENT_SCOPE ) - endif() - endif() -endfunction() - -## Find libraries for each component -set( NetCDF_LIBRARIES ) -foreach( _comp IN LISTS _search_components ) - string( TOUPPER "${_comp}" _COMP ) - - find_library( NetCDF_${_comp}_LIBRARY - NAMES ${NetCDF_${_comp}_LIBRARY_NAME} - DOC "NetCDF ${_comp} library" - HINTS ${NetCDF_${_comp}_INCLUDE_DIRS} ${_search_hints} - PATH_SUFFIXES lib64 lib ../lib64 ../lib ../../lib64 ../../lib ) - mark_as_advanced( NetCDF_${_comp}_LIBRARY ) - get_filename_component(NetCDF_${_comp}_LIBRARY ${NetCDF_${_comp}_LIBRARY} ABSOLUTE) - set(NetCDF_${_comp}_LIBRARY ${NetCDF_${_comp}_LIBRARY} CACHE STRING "NetCDF ${_comp} library" FORCE) - message(DEBUG "NetCDF_${_comp}_LIBRARY: ${NetCDF_${_comp}_LIBRARY}") - - if( NetCDF_${_comp}_LIBRARY ) - if( NetCDF_${_comp}_LIBRARY MATCHES ".a$" ) - set( NetCDF_${_comp}_LIBRARY_SHARED FALSE ) - set( _library_type STATIC) - else() - list( APPEND NetCDF_LIBRARIES ${NetCDF_${_comp}_LIBRARY} ) - set( NetCDF_${_comp}_LIBRARY_SHARED TRUE ) - set( _library_type SHARED) - endif() - endif() - - #Use nc-config to set per-component LIBRARIES variable if possible - netcdf_config( ${NetCDF_${_comp}_CONFIG_EXECUTABLE} ${_${_comp}_libs_flag} _val ) - if( _val ) - set( NetCDF_${_comp}_LIBRARIES ${_val} ) - if(NOT NetCDF_${_comp}_LIBRARY_SHARED AND NOT NetCDF_${_comp}_FOUND) #Static targets should use nc_config to get a proper link line with all necessary static targets. - list( APPEND NetCDF_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) - endif() - else() - set( NetCDF_${_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARY} ) - if(NOT NetCDF_${_comp}_LIBRARY_SHARED) - message(SEND_ERROR "Unable to properly find NetCDF. Found static libraries at: ${NetCDF_${_comp}_LIBRARY} but could not run nc-config: ${NetCDF_CONFIG_EXECUTABLE}") - endif() - endif() - - #Use nc-config to set per-component INCLUDE_DIRS variable if possible - netcdf_config( ${NetCDF_${_comp}_CONFIG_EXECUTABLE} ${_${_comp}_includes_flag} _val ) - if( _val ) - string( REPLACE " " ";" _val ${_val} ) - set( NetCDF_${_comp}_INCLUDE_DIRS ${_val} ) - else() - set( NetCDF_${_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIR} ) - endif() - - if( NetCDF_${_comp}_LIBRARIES AND NetCDF_${_comp}_INCLUDE_DIRS ) - set( ${CMAKE_FIND_PACKAGE_NAME}_${_arg_${_COMP}}_FOUND TRUE ) - if (NOT TARGET NetCDF::NetCDF_${_comp}) - add_library(NetCDF::NetCDF_${_comp} ${_library_type} IMPORTED) - set_target_properties(NetCDF::NetCDF_${_comp} PROPERTIES - IMPORTED_LOCATION ${NetCDF_${_comp}_LIBRARY} - INTERFACE_INCLUDE_DIRECTORIES "${NetCDF_${_comp}_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) - if( NOT _comp MATCHES "^(C)$" ) - target_link_libraries(NetCDF::NetCDF_${_comp} INTERFACE NetCDF::NetCDF_C) - endif() - endif() - endif() -endforeach() -if(NetCDF_LIBRARIES AND NetCDF_${_comp}_LIBRARY_SHARED) - list(REMOVE_DUPLICATES NetCDF_LIBRARIES) -endif() -set(NetCDF_LIBRARIES "${NetCDF_LIBRARIES}" CACHE STRING "NetCDF library targets" FORCE) - -## Find version via netcdf-config if possible -if (NetCDF_INCLUDE_DIRS) - if( NetCDF_C_CONFIG_EXECUTABLE ) - netcdf_config( ${NetCDF_C_CONFIG_EXECUTABLE} --version _vers ) - if( _vers ) - string(REGEX REPLACE ".* ((([0-9]+)\\.)+([0-9]+)).*" "\\1" NetCDF_VERSION "${_vers}" ) - endif() - else() - foreach( _dir IN LISTS NetCDF_INCLUDE_DIRS) - if( EXISTS "${_dir}/netcdf_meta.h" ) - file(STRINGS "${_dir}/netcdf_meta.h" _netcdf_version_lines - REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)") - string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" _netcdf_version_major "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" _netcdf_version_minor "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" _netcdf_version_patch "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" _netcdf_version_note "${_netcdf_version_lines}") - set(NetCDF_VERSION "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}") - unset(_netcdf_version_major) - unset(_netcdf_version_minor) - unset(_netcdf_version_patch) - unset(_netcdf_version_note) - unset(_netcdf_version_lines) - endif() - endforeach() - endif() -endif () - -## Detect additional package properties -netcdf_config(${NetCDF_C_CONFIG_EXECUTABLE} --has-parallel4 _val) -if( NOT _val MATCHES "^(yes|no)$" ) - netcdf_config(${NetCDF_C_CONFIG_EXECUTABLE} --has-parallel _val) -endif() -if( _val MATCHES "^(yes)$" ) - set(NetCDF_PARALLEL TRUE CACHE STRING "NetCDF has parallel IO capability via pnetcdf or hdf5." FORCE) -else() - set(NetCDF_PARALLEL FALSE CACHE STRING "NetCDF has no parallel IO capability." FORCE) -endif() - -## Finalize find_package -include(FindPackageHandleStandardArgs) - -if(NOT NetCDF_FOUND OR _new_search_components) - find_package_handle_standard_args( ${CMAKE_FIND_PACKAGE_NAME} - REQUIRED_VARS NetCDF_INCLUDE_DIRS NetCDF_LIBRARIES - VERSION_VAR NetCDF_VERSION - HANDLE_COMPONENTS ) -endif() - -foreach( _comp IN LISTS _search_components ) - if( NetCDF_${_comp}_FOUND ) - #Record found components to avoid duplication in NetCDF_LIBRARIES for static libraries - set(NetCDF_${_comp}_FOUND ${NetCDF_${_comp}_FOUND} CACHE BOOL "NetCDF ${_comp} Found" FORCE) - #Set a per-package, per-component found variable to communicate between multiple calls to find_package() - set(${PROJECT_NAME}_NetCDF_${_comp}_FOUND True) - endif() -endforeach() - -if( ${CMAKE_FIND_PACKAGE_NAME}_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY AND _new_search_components) - message( STATUS "Find${CMAKE_FIND_PACKAGE_NAME} defines targets:" ) - message( STATUS " - NetCDF_VERSION [${NetCDF_VERSION}]") - message( STATUS " - NetCDF_PARALLEL [${NetCDF_PARALLEL}]") - foreach( _comp IN LISTS _new_search_components ) - string( TOUPPER "${_comp}" _COMP ) - message( STATUS " - NetCDF_${_comp}_CONFIG_EXECUTABLE [${NetCDF_${_comp}_CONFIG_EXECUTABLE}]") - if( ${CMAKE_FIND_PACKAGE_NAME}_${_arg_${_COMP}}_FOUND ) - get_filename_component(_root ${NetCDF_${_comp}_INCLUDE_DIR}/.. ABSOLUTE) - if( NetCDF_${_comp}_LIBRARY_SHARED ) - message( STATUS " - NetCDF::NetCDF_${_comp} [SHARED] [Root: ${_root}] Lib: ${NetCDF_${_comp}_LIBRARY} ") - else() - message( STATUS " - NetCDF::NetCDF_${_comp} [STATIC] [Root: ${_root}] Lib: ${NetCDF_${_comp}_LIBRARY} ") - endif() - endif() - endforeach() -endif() - -foreach( _prefix NetCDF NetCDF4 NETCDF NETCDF4 ${CMAKE_FIND_PACKAGE_NAME} ) - set( ${_prefix}_INCLUDE_DIRS ${NetCDF_INCLUDE_DIRS} ) - set( ${_prefix}_LIBRARIES ${NetCDF_LIBRARIES}) - set( ${_prefix}_VERSION ${NetCDF_VERSION} ) - set( ${_prefix}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_FOUND} ) - set( ${_prefix}_CONFIG_EXECUTABLE ${NetCDF_CONFIG_EXECUTABLE} ) - set( ${_prefix}_PARALLEL ${NetCDF_PARALLEL} ) - - foreach( _comp ${_search_components} ) - string( TOUPPER "${_comp}" _COMP ) - set( _arg_comp ${_arg_${_COMP}} ) - set( ${_prefix}_${_comp}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) - set( ${_prefix}_${_COMP}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) - set( ${_prefix}_${_arg_comp}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) - - set( ${_prefix}_${_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) - set( ${_prefix}_${_COMP}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) - set( ${_prefix}_${_arg_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) - - set( ${_prefix}_${_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) - set( ${_prefix}_${_COMP}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) - set( ${_prefix}_${_arg_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) - endforeach() -endforeach() diff --git a/scm/src/multi_run_scm.py b/scm/src/multi_run_scm.py deleted file mode 100755 index 59df6921d..000000000 --- a/scm/src/multi_run_scm.py +++ /dev/null @@ -1,320 +0,0 @@ -#!/usr/bin/env python - -import sys -import argparse -import importlib -import os -import logging -import subprocess -from suite_info import suite, suite_list -from supported_cases import cases -import timeit, functools - -# Name of the python runscript executable to run, including path (relative to run dir) -RUN_SCRIPT = './run_scm.py' - -# which suites in suite_info to use when running through suites -SUITE_CHOICE = 'supported' -#SUITE_CHOIE = 'all' - -# number of realizations to time if timer is used -timer_iterations = 1 - -# error strings to check in the output -ERROR_STRINGS = ['systemexit','sigsegv','backtrace'] - -parser = argparse.ArgumentParser() -group = parser.add_mutually_exclusive_group()#required=True) -group.add_argument('-c', '--case', help='name of case to run',) -group.add_argument('-s', '--suite', help='name of suite to use',) -group.add_argument('-f', '--file', help='name of file where SCM runs are defined',) -parser.add_argument('-v', '--verbose', help='once: set logging level to debug; twice: set logging level to debug '\ - 'and write log to file', action='count', default=0) -parser.add_argument('-t', '--timer', help='set to time each subprocess', action='store_true', default=False) -parser.add_argument('-d', '--docker', help='include if scm is being run in a docker container to mount volumes', action='store_true', default=False) -parser.add_argument('--runtime', help='set the runtime in the namelists', action='store', required=False) -parser.add_argument('--runtime_mult', help='multiply the existing runtime in the namelist by some factor', action='store', required=False) -parser.add_argument('-l', '--levels', help='number of vertical levels', required=False) -parser.add_argument('--npz_type', help='type of FV3 vertical grid to produce (see scm_vgrid.F90 for valid values)', required=False) -parser.add_argument('--run_dir', help='path for the run directory', required=False) -parser.add_argument('--vert_coord_file', help='filename with coefficients to produce a vertical grid', required=False) -parser.add_argument('--case_data_dir', help='directory containing the case input data netCDF file', required=False) -parser.add_argument('--n_itt_out', help='period of instantaneous output (number of timesteps)', required=False, type=int) -parser.add_argument('--n_itt_diag', help='period of diagnostic output (number of timesteps)', required=False, type=int) -parser.add_argument('-dt', '--timestep', help='timestep (s)', required=False, type=float) - -# Results are recorded in this global list (to avoid complications with getting return values from the partial functions used below) -RESULTS = [] - -def setup_logging(verbose): - """Sets up the logging module.""" - # print out debug messages (logs and output from subprocesses) if verbose argument is set - if verbose: - LOG_LEVEL = logging.DEBUG - else: - LOG_LEVEL = logging.INFO - LOG_FILE = 'multi_run_scm.log' - LOG_FORMAT = '%(levelname)s: %(message)s' - - logging.basicConfig(format=LOG_FORMAT, level=LOG_LEVEL) - - # write out a log file if verbosity is set twice (-vv) - if verbose > 1: - fh = logging.FileHandler(LOG_FILE, mode='w') - logger = logging.getLogger() - logger.addHandler(fh) - -# function to spawn subprocesses directly (default) or through a timer -def spawn_subprocess(command, timer): - if timer: - t = timeit.Timer(functools.partial(subprocess_work, command)) - #timer_iterations determines how many realizations are executed in order to calculate the elapsed time - elapsed_time = t.timeit(timer_iterations) - return elapsed_time - else: - subprocess_work(command) - -# function to actually do the work of executing a subprocess and adding the stderr and stdout to the log -# at the DEBUG level -def subprocess_work(command): - p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True) - (output, err) = p.communicate() - p_status = p.wait() - logging.debug(output) - exit_code = p.returncode - if not exit_code == 0: - message = '####### The subprocess started using the command ({0}) exited with code {1}. #######\n'\ - 'Run the command ({0}) by itself again or use the -v or -vv options for more details.'.format(command, exit_code) - logging.critical(message) - else: - found_error_strings = {x for x in ERROR_STRINGS if x in str(output).lower()} - if found_error_strings: - message = '####### The subprocess started using the command ({0}) exited with a normal exit code, but\n'\ - 'the terminal output indicated the following strings occured in the output: ({1}). #######\n'\ - 'Run the command ({0}) by itself again or use the -v or -vv options for more details.'.format(command, found_error_strings) - logging.critical(message) - exit_code = -999 #unknown - RESULTS.append([command, exit_code]) - -def main(): - args = parser.parse_args() - - setup_logging(args.verbose) - - passthrough_args = '' - if args.levels: - passthrough_args += ' -l ' + args.levels - if args.npz_type: - if args.npz_type == 'input': - if args.vert_coord_file: - passthrough_args += ' --npz_type ' + args.npz_type + ' --vert_coord_file ' + args.vert_coord_file - else: - message = 'The npz_type was set to \'input\' but no file was specified via --vert_coord_file. Please specify the name of the file via --vert_coord_file name_of_file' - logging.critical(message) - raise Exception(message) - else: - passthrough_args += ' --npz_type ' + args.npz_type - if args.case_data_dir: - passthrough_args += ' --case_data_dir ' + args.case_data_dir - if args.docker: - passthrough_args += ' -d' - if args.runtime: - passthrough_args += ' --runtime ' + args.runtime - if args.runtime_mult: - passthrough_args += ' --runtime_mult ' + args.runtime_mult - if args.run_dir: - passthrough_args += ' --run_dir ' + args.run_dir - if args.n_itt_out: - passthrough_args += ' --n_itt_out ' + args.n_itt_out - if args.n_itt_diag: - passthrough_args += ' --n_itt_diag ' + args.n_itt_diag - if args.timestep: - passthrough_args += ' -dt ' + args.timestep - - # if the case argument is specified, run through all supported suites with the specified case - if args.case: - n_suites = 0 - if SUITE_CHOICE == 'supported': - logging.info('Running all supported suites in suite_info list with case {0}'.format(args.case)) - for s in suite_list: - if s._supported: - n_suites += 1 - else: - logging.info('Running all suites in suite_info list with case {0}'.format(args.case)) - n_suites = len(suite_list) - - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, s in enumerate(suite_list,1): - if SUITE_CHOICE == 'all' or (SUITE_CHOICE == 'supported' and s._supported): - command = RUN_SCRIPT + ' -c ' + args.case + ' -s ' + s._name + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format(i, n_suites, command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, args.case, s._name)) - - # if the suite argument is specified, run through all supported cases with the specified suite - if args.suite: - logging.info('Running all supported cases with suite {0}'.format(args.suite)) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(cases,1): - command = RUN_SCRIPT + ' -c ' + case + ' -s ' + args.suite + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format(i, len(cases), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, args.suite)) - - # For maximum flexibility, run the SCM as specified from an external file where cases, suites, and physics namelists - # are all specified. This file must contain python lists called 'cases','suites', and 'namelists'. The suites and - # namelists lists can be empty ([]) if necessary. - #The following rules apply: - # 1. The case list in the file must not be empty. - # 2. If only a case list is specified, the cases are run with the default suite specified in run_scm.py with - # the default namelists specified in default_namelists.py. - # 3. If a case list and suite list is provided without a namelist list, all permutations of cases and suites will - # be run using default namelists specified in default_namelists.py. - # 4. If a case list and suite list is provided with a namelist: - # 4a. If only one suite is specified, it can be run with any number of namelists. - # 4b. If more than one suite is specified, the number of namelists must match, and each case is run with each - # (suite,namelist) pair, by order specified in the lists. - # 5. If a case list and namelist list are specified without a suite list, each case is run with the default suite - # specified in run_scm.py using the supplied namelists. - if args.file: - logging.info('Importing {0} to run requested combinations'.format(args.file)) - try: - dirname, basename = os.path.split(args.file) - sys.path.append(dirname) - module_name = os.path.splitext(basename)[0] - scm_runs = importlib.import_module(module_name) - sys.path.pop() - except ImportError: - message = 'There was a problem loading {0}. Please check that the path exists.'.format(args.file) - logging.critical(message) - raise Exception(message) - - if not scm_runs.cases: - message = 'The cases list in {0} must not be empty'.format(args.file) - logging.critical(message) - raise Exception(message) - - if scm_runs.cases and not scm_runs.suites and not scm_runs.namelists: - logging.info( - 'Only cases were specified in {0}, so running all cases with the default suite'.format(args.file)) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(scm_runs.cases,1): - command = RUN_SCRIPT + ' -c ' + case + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format(i, len(scm_runs.cases), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, args.file)) - - if scm_runs.cases and scm_runs.suites: - if scm_runs.namelists: - if len(scm_runs.suites) == 1: - logging.info('Cases and namelists were specified with 1 suite in {0}, so running all cases with '\ - 'the suite {1} for all specified namelists'.format(args.file, scm_runs.suites[0])) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(scm_runs.cases): - for j, namelist in enumerate(scm_runs.namelists,1): - command = RUN_SCRIPT + ' -c ' + case + ' -s ' + scm_runs.suites[0] + ' -n ' + namelist + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format( - len(scm_runs.namelists)*i+j, len(scm_runs.cases)*len(scm_runs.namelists), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, scm_runs.suites[0])) - elif len(scm_runs.suites) == len(scm_runs.namelists): - logging.info('Cases, suites, and namelists were specified in {0}, so running all cases with all '\ - 'suites, matched with namelists by order'.format(args.file)) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(scm_runs.cases): - for j, suite in enumerate(scm_runs.suites,1): - command = RUN_SCRIPT + ' -c ' + case + ' -s ' + suite + ' -n ' + scm_runs.namelists[j-1] + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format( - len(scm_runs.suites)*i+j, len(scm_runs.cases)*len(scm_runs.suites), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, suite)) - else: - message = 'The number of suites and namelists specified in {0} is incompatible. Either use one '\ - 'suite with many namelists or the number of suites must match the number of namelists '\ - 'provided.'.format(args.file) - logging.critical(message) - raise Exception(message) - else: - logging.info('Cases and suites specified in {0}, so running all cases with all suites using default '\ - 'namelists for each suite'.format(args.file)) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(scm_runs.cases): - for j, suite in enumerate(scm_runs.suites,1): - command = RUN_SCRIPT + ' -c ' + case + ' -s ' + suite + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format( - len(scm_runs.suites)*i+j, len(scm_runs.cases)*len(scm_runs.suites), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, suite)) - - if scm_runs.cases and not scm_runs.suites and scm_runs.namelists: - logging.info('Cases and namelists were specified in {0}, so running all cases with the default suite '\ - 'using the list of namelists'.format(args.file)) - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for i, case in enumerate(scm_runs.cases): - for j, namelist in enumerate(scm_runs.namelists,1): - command = RUN_SCRIPT + ' -c ' + case + ' -n ' + namelist + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format( - len(scm_runs.namelists)*i+j, len(scm_runs.cases)*len(scm_runs.namelists), command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} namelist: {2}'\ - .format(elapsed_time/timer_iterations, case, namelist)) - - # If running the script with no arguments, run all supported (case,suite) permutations. - if not args.case and not args.suite and not args.file: - n_suites = 0 - if SUITE_CHOICE == 'supported': - logging.info('Since no arguments were specified, running through all permuatations of supported cases and '\ - 'supported suites in suite_info.') - if passthrough_args != '': - logging.info('Using passthrough args: {}'.format(passthrough_args)) - for s in suite_list: - if s._supported: - n_suites += 1 - else: - logging.info('Since no arguments were specified, running through all permuatations of supported cases and '\ - 'all suites in suite_info') - n_suites = len(suite_list) - - for i, case in enumerate(cases): - for j, s in enumerate(suite_list,1): - if SUITE_CHOICE == 'all' or (SUITE_CHOICE == 'supported' and s._supported): - command = RUN_SCRIPT + ' -c ' + case + ' -s ' + s._name + passthrough_args - logging.info('Executing process {0} of {1} ({2})'.format( - n_suites*i+j, len(cases)*n_suites, command)) - elapsed_time = spawn_subprocess(command, args.timer) - if args.timer: - logging.info('elapsed time: {0} s for case: {1} suite: {2}'\ - .format(elapsed_time/timer_iterations, case, s._name)) - - # Generate report at the end of the log file when verbose flag is set - if args.verbose > 0: - for (command, exit_code) in RESULTS: - if exit_code == 0: - logging.info('Process "{}" completed successfully'.format(command)) - else: - logging.error('Process "{}" exited with code {}'.format(command, exit_code)) - - logging.info('Done'.format(command)) - -if __name__ == '__main__': - main() diff --git a/scm/src/run_scm.py b/scm/src/run_scm.py index 6e67ca056..dfe3c909c 100755 --- a/scm/src/run_scm.py +++ b/scm/src/run_scm.py @@ -5,12 +5,16 @@ import f90nml import logging import os +import re import shutil import subprocess import sys import time from suite_info import suite, suite_list from netCDF4 import Dataset +# multi-run +from supported_cases import cases +import importlib ############################################################################### # Global settings # @@ -33,10 +37,17 @@ # Copy executable to run directory if true (otherwise it will be linked) COPY_EXECUTABLE = False +# Ignore errors in the execute subroutine (so that all multirun processes are attempted) +MULTIRUN_IGNORE_ERROR = True + # Default output periods -DEFAULT_OUTPUT_PERIOD = 1 +DEFAULT_OUTPUT_PERIOD = 6 DEFAULT_DIAG_PERIOD = 6 +# which suites in suite_info to use when running through suites +SUITE_CHOICE = 'supported' +#SUITE_CHOIE = 'all' + # Path to the directory containing experiment namelists (relative to scm_root) CASE_NAMELIST_DIR = 'scm/etc/case_config' @@ -90,20 +101,21 @@ TAU_TARGET = 'ugwp_c384_tau.nc' TAU_LINK = 'ugwp_limb_tau.nc' -# For developers: set logging level to DEBUG for additional output -#LOGLEVEL = logging.DEBUG -LOGLEVEL = logging.INFO - ############################################################################### # Command line arguments # ############################################################################### parser = argparse.ArgumentParser() -parser.add_argument('-c', '--case', help='name of case to run', required=True) +mgroup = parser.add_argument_group('Multiple experiments') +mgroup.add_argument('-m', '--multirun', help='run multiple experiments (loop through supported cases and suites , '\ + 'if no file provided OR loop through run as specified by the file) mutually exclusive with --case --suite --namelist --tracers', action='store_true', default=False) +mgroup.add_argument('-f', '--file', help='name of file where multiple SCM runs are defined') +sgroup = parser.add_argument_group('Single experiment') +sgroup.add_argument('-c', '--case', help='name of case to run') +sgroup.add_argument('-s', '--suite', help='name of suite to use') +sgroup.add_argument('-n', '--namelist', help='physics namelist to use') +sgroup.add_argument('-t', '--tracers', help='tracer configuration to use') parser.add_argument('-g', '--gdb', help='invoke scm through gdb', action='store_true', default=False) -parser.add_argument('-s', '--suite', help='name of suite to use', default=DEFAULT_SUITE) -parser.add_argument('-n', '--namelist', help='physics namelist to use') -parser.add_argument('-t', '--tracers', help='tracer configuration to use') parser.add_argument('--runtime', help='set the runtime in the namelists', action='store', type=int, required=False) parser.add_argument('--runtime_mult', help='multiply the existing runtime in the namelist by some factor', action='store', type=float, required=False) parser.add_argument('-d', '--docker', help='include if scm is being run in a docker container to mount volumes', action='store_true', default=False) @@ -111,20 +123,39 @@ parser.add_argument('--npz_type', help='type of FV3 vertical grid to produce (see scm_vgrid.F90 for valid values)', required=False) parser.add_argument('--vert_coord_file', help='filename with coefficients to produce a vertical grid', required=False) parser.add_argument('--run_dir', help='path for the run directory', required=False) +parser.add_argument('--bin_dir', help='path for the bin directory', required=False) parser.add_argument('--case_data_dir', help='directory containing the case input data netCDF file', required=False) parser.add_argument('--n_itt_out', help='period of instantaneous output (number of timesteps)', required=False, type=int) parser.add_argument('--n_itt_diag', help='period of diagnostic output (number of timesteps)', required=False, type=int) parser.add_argument('-dt', '--timestep', help='timestep (s)', required=False, type=float) +parser.add_argument('-v', '--verbose', help='once: set logging level to debug; twice: set logging level to debug '\ + 'and write log to file', action='count', default=0) ############################################################################### # Functions and subroutines # ############################################################################### -def setup_logging(): +def setup_logging(verbose): """Sets up the logging module.""" - logging.basicConfig(format='%(levelname)s: %(message)s', level=LOGLEVEL) + # print out debug messages (logs and output from subprocesses) if verbose argument is set + if verbose==2: + LOG_LEVEL = logging.DEBUG + elif verbose==1: + LOG_LEVEL = logging.INFO + else: + LOG_LEVEL = logging.WARNING + LOG_FILE = 'multi_run_scm.log' + LOG_FORMAT = '%(levelname)s: %(message)s' -def execute(cmd): + logging.basicConfig(format=LOG_FORMAT, level=LOG_LEVEL) + + # write out a log file if verbosity is set twice (-vv) + if verbose > 1: + fh = logging.FileHandler(LOG_FILE, mode='w') + logger = logging.getLogger() + logger.addHandler(fh) + +def execute(cmd, ignore_error = False): """Runs a local command in a shell. Waits for completion and returns status, stdout and stderr.""" logging.debug('Executing "{0}"'.format(cmd)) @@ -137,24 +168,36 @@ def execute(cmd): message += ' stdout: "{0}"\n'.format(stdout.decode(encoding='ascii', errors='ignore').rstrip('\n')) message += ' stderr: "{0}"'.format(stderr.decode(encoding='ascii', errors='ignore').rstrip('\n')) logging.debug(message) - else: + elif not ignore_error: message = 'Execution of command "{0}" failed, exit code {1}\n'.format(cmd, status) message += ' stdout: "{0}"\n'.format(stdout.decode(encoding='ascii', errors='ignore').rstrip('\n')) message += ' stderr: "{0}"'.format(stderr.decode(encoding='ascii', errors='ignore').rstrip('\n')) - logging.debug(message) + logging.critical(message) + raise Exception('Execution of command "{0}" failed, exit code {1}\n'.format(cmd, status)) return (status, stdout.decode(encoding='ascii', errors='ignore').rstrip('\n'), stderr.decode(encoding='ascii', errors='ignore').rstrip('\n')) def parse_arguments(): """Parse command line arguments""" args = parser.parse_args() + multirun = args.multirun case = args.case - gdb = args.gdb suite = args.suite namelist = args.namelist - docker = args.docker tracers = args.tracers + # Consistency checks + if (multirun and (case or suite or namelist or tracers)) \ + or (not multirun and not case): + raise Exception("Specify either --multirun or --case [--suite --namelist --tracers]") + if not suite: + suite = DEFAULT_SUITE + file = args.file + if (file and not multirun): + logging.info('The --file argument is only applicable when --multirun is specified. Ignoring --file argument.') + gdb = args.gdb runtime = args.runtime runtime_mult = args.runtime_mult + docker = args.docker + verbose = args.verbose levels = args.levels npz_type = args.npz_type vert_coord_file = args.vert_coord_file @@ -162,15 +205,16 @@ def parse_arguments(): n_itt_out = args.n_itt_out n_itt_diag = args.n_itt_diag run_dir = args.run_dir + bin_dir = args.bin_dir timestep = args.timestep - return (case, gdb, suite, namelist, docker, tracers, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag, run_dir, timestep) + return (multirun, file, case, suite, namelist, tracers, gdb, runtime, runtime_mult, docker, verbose, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag, run_dir, bin_dir, timestep) def find_gdb(): """Detect gdb, abort if not found""" logging.info('Searching for gdb ...') cmd = 'which gdb' - (status, stdout, stderr) = execute(cmd) + (status, stdout, stderr) = execute(cmd, ignore_error = True) if status==1: message = 'gdb not found' logging.critical(message) @@ -189,7 +233,7 @@ def __init__(self, case, suite, runtime, runtime_mult, levels, npz_type, vert_co self._case = case self._suite_obj = suite self._suite = suite._name - self._name = case + '_' + suite._name + self._name = case + '_' + self._suite self._physics_namelist = suite.namelist @@ -675,23 +719,40 @@ def setup_rundir(self): execute(cmd) else: logging.info('Linking executable to run directory') - cmd = 'ln -s {0} {1}'.format(os.path.join(SCM_ROOT, SCM_BIN, EXECUTABLE_NAME), os.path.join(SCM_RUN, EXECUTABLE_NAME)) + cmd = 'ln -sf {0} {1}'.format(os.path.join(SCM_ROOT, SCM_BIN, EXECUTABLE_NAME), os.path.join(SCM_RUN, EXECUTABLE_NAME)) execute(cmd) return output_dir -def launch_executable(use_gdb, gdb): +def launch_executable(use_gdb, gdb, ignore_error = False): """Configure model run command and pass control to shell/gdb""" if use_gdb: - cmd = '(cd {scm_run} && exec {gdb} {executable})'.format(scm_run=SCM_RUN, gdb=gdb, executable=EXECUTABLE) + cmd = '(cd {scm_run} && {gdb} {executable})'.format(scm_run=SCM_RUN, gdb=gdb, executable=EXECUTABLE) else: - cmd = '(cd {scm_run} && exec {executable})'.format(scm_run=SCM_RUN, executable=EXECUTABLE) + cmd = '(cd {scm_run} && time {executable})'.format(scm_run=SCM_RUN, executable=EXECUTABLE) logging.info('Passing control to "{0}"'.format(cmd)) - time.sleep(2) - failure = os.system(cmd) - if failure: - logging.info('Execution of {0} failed!\n'.format(cmd)) - sys.exit(1) + time.sleep(1) + # This will abort in 'execute' in the event of an error if ignore_error = False + (status, stdout, stderr) = execute(cmd, ignore_error = ignore_error) + logging.info('Process "{0}" returned with status {1}'.format(cmd, status)) + # Get timing info if not using gdb + time_elapsed = None + if not use_gdb: + minutes = None + seconds = None + for line in stderr.split('\n'): + line = line.strip() + if line.startswith('real'): + matches = re.findall(r'real\s+(\d+)m(\d+\.\d+)s', stderr) + if len(matches)==1: + (minutes, seconds) = matches[0] + break + raise Exception('matches "{}"'.format(matches)) + if not minutes and not seconds: + logging.warning('Unable to get timing information from {0} stderr'.format(cmd)) + else: + time_elapsed = int(minutes)*60 + float(seconds) + return (status, time_elapsed) def copy_outdir(exp_dir): """Copy output directory to /home for this experiment.""" @@ -701,7 +762,7 @@ def copy_outdir(exp_dir): shutil.copytree(exp_dir, home_output_dir) def main(): - (case, use_gdb, suite_name, namelist, docker, tracers, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag, run_dir, timestep) = parse_arguments() + (multirun, file, case, suite_name, namelist, tracers, use_gdb, runtime, runtime_mult, docker, verbose, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag, run_dir, bin_dir, timestep) = parse_arguments() global SCM_ROOT SCM_ROOT = os.getenv('SCM_ROOT') @@ -711,7 +772,10 @@ def main(): raise Exception(message) global SCM_BIN - SCM_BIN = os.path.join(SCM_ROOT, DEFAULT_BIN_DIR) + if bin_dir: + SCM_BIN = bin_dir + else: + SCM_BIN = os.path.join(SCM_ROOT, DEFAULT_BIN_DIR) global SCM_RUN if run_dir: @@ -724,57 +788,259 @@ def main(): global EXECUTABLE EXECUTABLE = os.path.join(SCM_RUN, EXECUTABLE_NAME) - setup_logging() - - #Experiment - if namelist: - if tracers: - logging.info('Setting up experiment {0} with suite {1} using namelist {2} and tracers {3}'.format(case,suite_name,namelist,tracers)) - else: - logging.info('Setting up experiment {0} with suite {1} using namelist {2} using default tracers for the suite'.format(case,suite_name,namelist)) - else: - if tracers: - logging.info('Setting up experiment {0} with suite {1} using the default namelist for the suite and tracers {2}'.format(case,suite_name,tracers)) - else: - logging.info('Setting up experiment {0} with suite {1} using the default namelist and tracers for the suite'.format(case,suite_name)) - - active_suite = None - for s in suite_list: - if suite_name == s._name: - active_suite = s - break - - if (active_suite is None): - if (namelist and tracers): - if timestep: - active_suite = suite(suite_name, tracers, namelist, timestep, -1, False) - else: - active_sutie = suite(suite_name, tracers, namelist, -1, -1, False) - else: - message = 'The given suite ({0}), does not have defaults set in suite_info.py and either the tracers file or physics namelist file (or both) were not provided.'.format(suite_name) - logging.critical(message) - raise Exception(message) - else: - if namelist: - active_suite.namelist = namelist - if tracers: - active_suite.tracers = tracers - if timestep: - active_suite.timestep = timestep + setup_logging(verbose) - exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) - exp_dir = exp.setup_rundir() # Debugger if use_gdb: gdb = find_gdb() else: gdb = None - # Launch model on exit - if docker: - #registering this function first should mean that it executes last, which is what we want - atexit.register(copy_outdir, exp_dir) - atexit.register(launch_executable, use_gdb, gdb) + if multirun: + # For maximum flexibility, run the SCM as specified from an external file where cases, suites, and physics namelists + # are all specified. This file must contain python lists called 'cases','suites', and 'namelists'. The suites and + # namelists lists can be empty ([]) if necessary. + #The following rules apply: + # 1. The case list in the file must not be empty. + # 2. If only a case list is specified, the cases are run with the default suite specified in run_scm.py with + # the default namelists specified in default_namelists.py. + # 3. If a case list and suite list is provided without a namelist list, all permutations of cases and suites will + # be run using default namelists specified in default_namelists.py. + # 4. If a case list and suite list is provided with a namelist: + # 4a. If only one suite is specified, it can be run with any number of namelists. + # 4b. If more than one suite is specified, the number of namelists must match, and each case is run with each + # (suite,namelist) pair, by order specified in the lists. + # 5. If a case list and namelist list are specified without a suite list, each case is run with the default suite + # specified in run_scm.py using the supplied namelists. + if file: + logging.warning('Multi-run: Using {} to loop through defined runs'.format(file)) + try: + dirname, basename = os.path.split(file) + sys.path.append(dirname) + module_name = os.path.splitext(basename)[0] + scm_runs = importlib.import_module(module_name) + sys.path.pop() + except ImportError: + message = 'There was a problem loading {0}. Please check that the path exists.'.format(file) + logging.critical(message) + raise Exception(message) + + if not scm_runs.cases: + message = 'The cases list in {0} must not be empty'.format(file) + logging.critical(message) + raise Exception(message) + + if scm_runs.cases and not scm_runs.suites and not scm_runs.namelists: + logging.warning( + 'Only cases were specified in {0}, so running all cases with the default suite'.format(file)) + + active_suite = None + for s in suite_list: + if suite_name == s._name: + active_suite = s + break + + for i, case in enumerate(scm_runs.cases,1): + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + i, len(cases), case, active_suite._name, active_suite.namelist)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + + if scm_runs.cases and scm_runs.suites: + if scm_runs.namelists: + if len(scm_runs.suites) == 1: + logging.warning('Cases and namelists were specified with 1 suite in {0}, so running all cases with '\ + 'the suite {1} for all specified namelists'.format(file, scm_runs.suites[0])) + + active_suite = None + for s in suite_list: + if scm_runs.suites[0] == s._name: + active_suite = s + break + + for i, case in enumerate(scm_runs.cases): + for j, namelist in enumerate(scm_runs.namelists,1): + active_suite.namelist = namelist + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + len(scm_runs.cases)*i+j, len(scm_runs.cases)*len(scm_runs.namelists), case, active_suite._name, active_suite.namelist)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + + elif len(scm_runs.suites) == len(scm_runs.namelists): + logging.warning('Cases, suites, and namelists were specified in {0}, so running all cases with all '\ + 'suites, matched with namelists by order'.format(file)) + for i, case in enumerate(scm_runs.cases): + for j, suite in enumerate(scm_runs.suites,1): + + active_suite = None + for s in suite_list: + if suite == s._name: + active_suite = s + break + + active_suite.namelist = scm_runs.namelists[j-1] + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + len(scm_runs.cases)*i+j, len(scm_runs.cases)*len(scm_runs.suites), case, active_suite._name, active_suite.namelist)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + + else: + message = 'The number of suites and namelists specified in {0} is incompatible. Either use one '\ + 'suite with many namelists or the number of suites must match the number of namelists '\ + 'provided.'.format(file) + logging.critical(message) + raise Exception(message) + else: + logging.warning('Cases and suites specified in {0}, so running all cases with all suites using default '\ + 'namelists for each suite'.format(file)) + for i, case in enumerate(scm_runs.cases): + for j, suite in enumerate(scm_runs.suites,1): + + active_suite = None + for s in suite_list: + if suite == s._name: + active_suite = s + break + + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + len(scm_runs.cases)*i+j, len(scm_runs.cases)*len(scm_runs.suites), case, active_suite._name, active_suite.namelist)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + + if scm_runs.cases and not scm_runs.suites and scm_runs.namelists: + logging.warning('Cases and namelists were specified in {0}, so running all cases with the default suite '\ + 'using the list of namelists'.format(file)) + + active_suite = None + for s in suite_list: + if suite_name == s._name: + active_suite = s + break + + for i, case in enumerate(scm_runs.cases): + for j, namelist in enumerate(scm_runs.namelists,1): + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + len(scm_runs.cases)*i+j, len(scm_runs.cases)*len(scm_runs.namelists), case, active_suite._name, active_suite.namelist)) + active_suite.namelist = namelist + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + + else: + # Loop through all experiments + logging.warning('Multi-run: loop through all cases and suite definition files with standard namelists and tracer configs') + + # determine the number of suites to run through + active_suite_list = [] + if SUITE_CHOICE == 'supported': + for s in suite_list: + if s._supported: + active_suite_list.append(s) + else: + active_suite_list = suite_list + + for i, case in enumerate(cases): + for j, active_suite in enumerate(active_suite_list,1): + logging.warning('Executing process {0} of {1}: case={2}, suite={3}, namelist={4}'.format( + len(active_suite_list)*i+j, len(cases)*len(active_suite_list), case, active_suite._name, active_suite.namelist)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + (status, time_elapsed) = launch_executable(use_gdb, gdb, ignore_error = MULTIRUN_IGNORE_ERROR) + if status == 0: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" completed successfully'.format(case, active_suite._name, active_suite.namelist)) + else: + logging.warning('Process "(case={0}, suite={1}, namelist={2}" exited with code {3}'.format(case, active_suite._name, active_suite.namelist, status)) + if time_elapsed: + logging.warning(' Elapsed time: {0}s'.format(time_elapsed)) + if docker: + copy_outdir(exp_dir) + logging.warning('Done.') + + else: + # Single experiment + active_suite = None + for s in suite_list: + if suite_name == s._name: + active_suite = s + break + + if (active_suite is None): + if (namelist and tracers): + if timestep: + active_suite = suite(suite_name, tracers, namelist, timestep, -1, False) + else: + active_sutie = suite(suite_name, tracers, namelist, -1, -1, False) + else: + message = 'The given suite ({0}), does not have defaults set in suite_info.py and either the tracers file or physics namelist file (or both) were not provided.'.format(suite_name) + logging.critical(message) + raise Exception(message) + else: + if namelist: + active_suite.namelist = namelist + if tracers: + active_suite.tracers = tracers + if timestep: + active_suite.timestep = timestep + + suite_string = 'suite {0}'.format(active_suite._name) + namelist_string = 'namelist {0}'.format(active_suite.namelist) + tracers_string = 'tracers {0}'.format(active_suite.tracers) + logging.warning('Setting up experiment {case} with {suite} using {namelist} and {tracers}'.format( + case=case, suite=suite_string, namelist=namelist_string, tracers=tracers_string)) + exp = Experiment(case, active_suite, runtime, runtime_mult, levels, npz_type, vert_coord_file, case_data_dir, n_itt_out, n_itt_diag) + exp_dir = exp.setup_rundir() + logging.warning('Launching experiment {case} with {suite} using {namelist} and {tracers}'.format( + case=case, suite=suite_string, namelist=namelist_string, tracers=tracers_string)) + # Launch model on exit + if docker: + #registering this function first should mean that it executes last, which is what we want + atexit.register(copy_outdir, exp_dir) + # Ignore time_elapsed return value for single experiment + atexit.register(launch_executable, use_gdb, gdb) if __name__ == '__main__': main() diff --git a/scm/src/scm_utils.F90 b/scm/src/scm_utils.F90 index c7f912d28..f5dd6c157 100644 --- a/scm/src/scm_utils.F90 +++ b/scm/src/scm_utils.F90 @@ -150,18 +150,21 @@ subroutine w_to_omega(n_col, n_lev, w, p, T, omega) end subroutine w_to_omega integer function lcm(a,b) - integer:: a,b + integer, intent(in) :: a,b lcm = a*b / gcd(a,b) end function lcm integer function gcd(a,b) - integer :: a,b,t - do while (b/=0) - t = b - b = mod(a,b) - a = t + integer, intent(in) :: a,b + integer :: c,d,t + c = a + d = b + do while (d/=0) + t = d + d = mod(c,d) + c = t end do - gcd = abs(a) + gcd = abs(c) end function gcd !> @} diff --git a/test/rt.sh b/test/rt.sh index a8189c590..305ac5500 100755 --- a/test/rt.sh +++ b/test/rt.sh @@ -228,10 +228,10 @@ for compiler in "${compilers[@]}"; do RUN_DIR=$TOP_DIR/scm/run_${compiler}_${build_type_lc} # for each build/run in test BUILD_OUTPUT=${BIN_DIR}/build.out if [ "${build_type}" == "Debug" ] ; then - # Add --runtime ${runtime} to multi_run_scm.py to reduce runtime for tests - test_run_cmd="${BIN_DIR}/multi_run_scm.py -f ${TEST_DIR}/rt_test_cases.py -v --runtime_mult 0.1 --run_dir ${RUN_DIR}" + # Add --runtime ${runtime} to run_scm.py to reduce runtime for tests + test_run_cmd="${BIN_DIR}/run_scm.py -m -f ${TEST_DIR}/rt_test_cases.py -vv --runtime_mult 0.1 --run_dir ${RUN_DIR} --bin_dir ${BIN_DIR}" else - test_run_cmd="${BIN_DIR}/multi_run_scm.py -f ${TEST_DIR}/rt_test_cases.py -v --timer --runtime_mult 0.1 --run_dir ${RUN_DIR}" + test_run_cmd="${BIN_DIR}/run_scm.py -m -f ${TEST_DIR}/rt_test_cases.py -v --runtime_mult 0.1 --run_dir ${RUN_DIR} --bin_dir ${BIN_DIR}" fi . ${ETC_DIR}/${machine}_setup_${compiler}.sh diff --git a/test/summarize.sh b/test/summarize.sh index 1957996b4..fe838c0fc 100755 --- a/test/summarize.sh +++ b/test/summarize.sh @@ -68,10 +68,10 @@ fi num_output_nc_files=$(ls -l ${run_dir}/output_*/output.nc | wc -l) echo "Number of processes with output.nc files = ${num_output_nc_files}" >> ${TEST_OUTPUT} -if [ ${num_output_nc_files} -eq ${num_tests} ] ; then - echo "PASS: Number output.nc files ${num_output_nc_files} = ${num_tests}" >> ${TEST_OUTPUT} -else +if [ ${num_output_nc_files} -lt ${num_tests} ] ; then echo "FAIL: Number output.nc files ${num_output_nc_files} /= ${num_tests}" >> ${TEST_OUTPUT} +else + echo "PASS: Number output.nc files ${num_output_nc_files} = ${num_tests}" >> ${TEST_OUTPUT} fi #-----------------------------------------------------------------------