diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..61a70ed75f --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +outputs/* +src/externals/mct/*/*.o +src/externals/mct/*/*.mod +src/externals/mct/*/*.a +src/externals/mct/Makefile.conf +src/externals/mct/config.h +src/externals/mct/config.log +src/externals/mct/config.status diff --git a/bootstrap b/bootstrap new file mode 100755 index 0000000000..4daaf6ff59 --- /dev/null +++ b/bootstrap @@ -0,0 +1,91 @@ +#!/usr/bin/env bash + +set -Eeo pipefail + +# Clone CLM5 +git clone -b release-clm5.0 https://github.com/ESCOMP/CTSM.git clm5.0 +( + cd clm5.0 + git checkout dc5ea974c3 # most recent commit as of 2020-03-04 + ./manage_externals/checkout_externals +) + +# Create src dirs +CLM5_ROOT=`realpath clm5.0` +SRC_ROOT=src +mkdir $SRC_ROOT +cd $SRC_ROOT +mkdir -p externals/{gptl,mct,pio1} csm_share \ + clm5 stub_comps datm mosart cesm + +# GPTL +cp $CLM5_ROOT/cime/src/share/timing/* externals/gptl +echo "Imported GPTL source files" + +# MCT +cp -rf $CLM5_ROOT/cime/src/externals/mct/* externals/mct +echo "Imported MCT source files" + +# PIO +cp -rf $CLM5_ROOT/cime/src/externals/pio1/pio/* externals/pio1 +echo "Imported PIO1 source files" + +# CSM share +( + cd csm_share + mkdir esmf_wrf_timemgr mct streams util + cp -rf $CLM5_ROOT/cime/src/share/esmf_wrf_timemgr/* esmf_wrf_timemgr + cp -rf $CLM5_ROOT/cime/src/drivers/mct/shr/* mct + cp -rf $CLM5_ROOT/cime/src/share/streams/* streams + cp -rf $CLM5_ROOT/cime/src/share/util/* util +) +echo "Imported CSM share source files" + +# CLM5 +clm_dirs=( + main + biogeophys + biogeochem + soilbiogeochem + dyn_subgrid + init_interp + fates/main + fates/biogeophys + fates/biogeochem + fates/fire + fates/parteh + utils + cpl +) +for src_dir in ${clm_dirs[*]}; do + dest_dir=clm5/$src_dir + mkdir -p $dest_dir + cp -rf $CLM5_ROOT/src/$src_dir/* $dest_dir +done +echo "Imported CLM source files" + +# Stub components +cp $CLM5_ROOT/cime/src/components/stub_comps/sglc/cpl/glc_comp_mct.F90 stub_comps +cp $CLM5_ROOT/cime/src/components/stub_comps/socn/cpl/ocn_comp_mct.F90 stub_comps +cp $CLM5_ROOT/cime/src/components/stub_comps/sesp/cpl/esp_comp_mct.F90 stub_comps +cp $CLM5_ROOT/cime/src/components/stub_comps/sice/cpl/ice_comp_mct.F90 stub_comps +cp $CLM5_ROOT/cime/src/components/stub_comps/swav/cpl/wav_comp_mct.F90 stub_comps +echo "Imported stub components source files" + +# DATM +cp $CLM5_ROOT/cime/src/components/data_comps/datm/datm_shr_mod.F90 datm +cp $CLM5_ROOT/cime/src/components/data_comps/datm/datm_comp_mod.F90 datm +cp $CLM5_ROOT/cime/src/components/data_comps/datm/mct/atm_comp_mct.F90 datm +echo "Imported DATM source files" + +# MOSART +cp -rf $CLM5_ROOT/components/mosart/* mosart +rm -rf mosart/cime_config +echo "Imported MOSART source files" + +# CESM +cp $CLM5_ROOT/cime/src/drivers/mct/main/* cesm +echo "Imported CESM source files" + +# Copy LICENSE file +cp $CLM5_ROOT/LICENSE . diff --git a/build_tools/build.juwels-centos8 b/build_tools/build.juwels-centos8 new file mode 100755 index 0000000000..52d9eec985 --- /dev/null +++ b/build_tools/build.juwels-centos8 @@ -0,0 +1,40 @@ +#!/usr/bin/env bash + +# ------- User-specified variables ------------ +ARCH="juwels-centos8" +CLM5_TSMP_ROOT=$(git rev-parse --show-toplevel) +BUILD_FOLDER="$CLM5_TSMP_ROOT/outputs/$ARCH/build" +INSTALL_PATH="$CLM5_TSMP_ROOT/outputs/$ARCH/run" +# --------------------------------------------- + +module purge +module use $OTHERSTAGES +module load Stages/2020 +module load Intel +module load ParaStationMPI +module load ESMF +module load NCO +module load Perl +module load CMake +module load parallel-netcdf +module load imkl +module load NCL +module load Python +module li + +echo "NetCDF_C_PATH=$EBROOTNETCDF" +echo "NetCDF_Fortran_PATH=$EBROOTNETCDFMINFORTRAN" +echo "PnetCDF_C_PATH=$EBROOTPARALLELMINNETCDF" +GENF90_PATH=$(pwd) +rm -rf $BUILD_FOLDER +cmake -S "$CLM5_TSMP_ROOT/src" \ + -B "$BUILD_FOLDER" \ + -DCMAKE_MODULE_PATH="$(pwd)/cmake" \ + -DCMAKE_C_COMPILER=mpicc \ + -DCMAKE_Fortran_COMPILER=mpifort \ + -DGENF90_PATH=$GENF90_PATH \ + -DNetCDF_C_PATH=$EBROOTNETCDF \ + -DNetCDF_Fortran_PATH=$EBROOTNETCDFMINFORTRAN \ + -DPnetCDF_PATH=$EBROOTPARALLELMINNETCDF +cmake --build "$BUILD_FOLDER" --clean-first +cmake --install "$BUILD_FOLDER" \ No newline at end of file diff --git a/build_tools/build.ubuntu-20.04LTS b/build_tools/build.ubuntu-20.04LTS new file mode 100755 index 0000000000..27d6cb1cb7 --- /dev/null +++ b/build_tools/build.ubuntu-20.04LTS @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +# ------- User-specified variables ------------ +ARCH="ubuntu-20.04LTS" +CLM5_TSMP_ROOT=$(git rev-parse --show-toplevel) +BUILD_FOLDER="$CLM5_TSMP_ROOT/outputs/$ARCH/build" +INSTALL_PATH="$CLM5_TSMP_ROOT/outputs/$ARCH/run" +# --------------------------------------------- + +NetCDF_ROOT=/opt/custom +GENF90_PATH=$(pwd) +rm -rf $BUILD_FOLDER +cmake -S "$CLM5_TSMP_ROOT/src" \ + -B "$BUILD_FOLDER" \ + -DCMAKE_INSTALL_PREFIX="$INSTALL_PATH" \ + -DCMAKE_MODULE_PATH="$(pwd)/cmake" \ + -DCMAKE_C_COMPILER=mpicc \ + -DCMAKE_Fortran_COMPILER=mpifort \ + -DGENF90_PATH=$GENF90_PATH \ + -DNetCDF_PATH=$NetCDF_ROOT \ + -DPnetCDF_PATH=$NetCDF_ROOT +cmake --build "$BUILD_FOLDER" --clean-first +cmake --install "$BUILD_FOLDER" diff --git a/build_tools/cmake/FindLAPACK.cmake b/build_tools/cmake/FindLAPACK.cmake new file mode 100644 index 0000000000..4eaee1f83e --- /dev/null +++ b/build_tools/cmake/FindLAPACK.cmake @@ -0,0 +1,464 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#[=======================================================================[.rst: + +FindLapack +---------- + +* Michael Hirsch, Ph.D. www.scivision.dev +* David Eklund + +Let Michael know if there are more MKL / Lapack / compiler combination you want. +Refer to https://software.intel.com/en-us/articles/intel-mkl-link-line-advisor + +Finds LAPACK libraries for C / C++ / Fortran. +Works with Netlib Lapack / LapackE, Atlas and Intel MKL. +Intel MKL relies on having environment variable MKLROOT set, typically by sourcing +mklvars.sh beforehand. + +Why not the FindLapack.cmake built into CMake? It has a lot of old code for +infrequently used Lapack libraries and is unreliable for me. + +Tested on Linux, MacOS and Windows with: +* GCC / Gfortran +* Clang / Flang +* PGI (pgcc, pgfortran) +* Intel (icc, ifort) + + +Parameters +^^^^^^^^^^ + +COMPONENTS default to Netlib LAPACK / LapackE, otherwise: + +``MKL`` + Intel MKL for MSVC, ICL, ICC, GCC and PGCC -- sequential by default, or add TBB or MPI as well +``OpenMP`` + Intel MPI with OpenMP threading addition to MKL +``TBB`` + Intel MPI + TBB for MKL +``MKL64`` + MKL only: 64-bit integers (default is 32-bit integers) + +``LAPACKE`` + Netlib LapackE for C / C++ +``Netlib`` + Netlib Lapack for Fortran +``OpenBLAS`` + OpenBLAS Lapack for Fortran + +``LAPACK95`` + get Lapack95 interfaces for MKL or Netlib (must also specify one of MKL, Netlib) + + +Result Variables +^^^^^^^^^^^^^^^^ + +``LAPACK_FOUND`` + Lapack libraries were found +``LAPACK__FOUND`` + LAPACK specified was found +``LAPACK_LIBRARIES`` + Lapack library files (including BLAS +``LAPACK_INCLUDE_DIRS`` + Lapack include directories (for C/C++) + + +References +^^^^^^^^^^ + +* Pkg-Config and MKL: https://software.intel.com/en-us/articles/intel-math-kernel-library-intel-mkl-and-pkg-config-tool +* MKL for Windows: https://software.intel.com/en-us/mkl-windows-developer-guide-static-libraries-in-the-lib-intel64-win-directory +* MKL Windows directories: https://software.intel.com/en-us/mkl-windows-developer-guide-high-level-directory-structure +* Atlas http://math-atlas.sourceforge.net/errata.html#LINK +* MKL LAPACKE (C, C++): https://software.intel.com/en-us/mkl-linux-developer-guide-calling-lapack-blas-and-cblas-routines-from-c-c-language-environments +#]=======================================================================] + +# clear to avoid endless appending on subsequent calls +set(LAPACK_LIBRARY) +set(LAPACK_INCLUDE_DIR) + +# ===== functions ========== + +function(atlas_libs) + +find_library(ATLAS_LIB + NAMES atlas + NAMES_PER_DIR + PATH_SUFFIXES atlas) + +pkg_check_modules(pc_atlas_lapack lapack-atlas QUIET) + +find_library(LAPACK_ATLAS + NAMES ptlapack lapack_atlas lapack + NAMES_PER_DIR + PATH_SUFFIXES atlas + HINTS ${pc_atlas_lapack_LIBRARY_DIRS} ${pc_atlas_lapack_LIBDIR}) + +pkg_check_modules(pc_atlas_blas blas-atlas QUIET) + +find_library(BLAS_LIBRARY + NAMES ptf77blas f77blas blas + NAMES_PER_DIR + PATH_SUFFIXES atlas + HINTS ${pc_atlas_blas_LIBRARY_DIRS} ${pc_atlas_blas_LIBDIR}) +# === C === +find_library(BLAS_C_ATLAS + NAMES ptcblas cblas + NAMES_PER_DIR + PATH_SUFFIXES atlas + HINTS ${pc_atlas_blas_LIBRARY_DIRS} ${pc_atlas_blas_LIBDIR}) + +find_path(LAPACK_INCLUDE_DIR + NAMES cblas-atlas.h cblas.h clapack.h + HINTS ${pc_atlas_blas_INCLUDE_DIRS} ${pc_atlas_blas_LIBDIR}) + +#=========== +if(LAPACK_ATLAS AND BLAS_C_ATLAS AND BLAS_LIBRARY AND ATLAS_LIB) + set(LAPACK_Atlas_FOUND true PARENT_SCOPE) + set(LAPACK_LIBRARY ${LAPACK_ATLAS} ${BLAS_C_ATLAS} ${BLAS_LIBRARY} ${ATLAS_LIB}) + if(NOT WIN32) + find_package(Threads) # not required--for example Flang + list(APPEND LAPACK_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) + endif() +endif() + +set(LAPACK_LIBRARY ${LAPACK_LIBRARY} PARENT_SCOPE) +set(LAPACK_INCLUDE_DIR ${LAPACK_INCLUDE_DIR} PARENT_SCOPE) + +endfunction(atlas_libs) + +#======================= + +function(netlib_libs) + +if(LAPACK95 IN_LIST LAPACK_FIND_COMPONENTS) + find_path(LAPACK95_INCLUDE_DIR + NAMES f95_lapack.mod + PATH_SUFFIXES include + PATHS ${LAPACK95_ROOT}) + + find_library(LAPACK95_LIBRARY + NAMES lapack95 + NAMES_PER_DIR + PATH_SUFFIXES lib + PATHS ${LAPACK95_ROOT}) + + if(LAPACK95_LIBRARY AND LAPACK95_INCLUDE_DIR) + set(LAPACK_INCLUDE_DIR ${LAPACK95_INCLUDE_DIR}) + set(LAPACK_LIBRARY ${LAPACK95_LIBRARY}) + else() + return() + endif() +endif(LAPACK95 IN_LIST LAPACK_FIND_COMPONENTS) + +set(_lapack_hints) +if(CMAKE_Fortran_COMPILER_ID STREQUAL PGI) + get_filename_component(_pgi_path ${CMAKE_Fortran_COMPILER} DIRECTORY) + set(_lapack_hints ${_pgi_path}/../) +endif() + +pkg_check_modules(pc_lapack lapack-netlib QUIET) +if(NOT pc_lapack_FOUND) + pkg_check_modules(pc_lapack lapack QUIET) # Netlib on Cygwin, Homebrew and others +endif() +find_library(LAPACK_LIB + NAMES lapack + NAMES_PER_DIR + PATHS /usr/local/opt # homebrew + HINTS ${_lapack_hints} ${pc_lapack_LIBRARY_DIRS} ${pc_lapack_LIBDIR} + PATH_SUFFIXES lib lapack lapack/lib) +if(LAPACK_LIB) + list(APPEND LAPACK_LIBRARY ${LAPACK_LIB}) +else() + return() +endif() + +if(LAPACKE IN_LIST LAPACK_FIND_COMPONENTS) + pkg_check_modules(pc_lapacke lapacke QUIET) + find_library(LAPACKE_LIBRARY + NAMES lapacke + NAMES_PER_DIR + PATHS /usr/local/opt + HINTS ${pc_lapacke_LIBRARY_DIRS} ${pc_lapacke_LIBDIR} + PATH_SUFFIXES lapack lapack/lib) + + # lapack/include for Homebrew + find_path(LAPACKE_INCLUDE_DIR + NAMES lapacke.h + PATHS /usr/local/opt + HINTS ${pc_lapacke_INCLUDE_DIRS} ${pc_lapacke_LIBDIR} + PATH_SUFFIXES lapack lapack/include) + + if(LAPACKE_LIBRARY AND LAPACKE_INCLUDE_DIR) + set(LAPACK_LAPACKE_FOUND true PARENT_SCOPE) + list(APPEND LAPACK_INCLUDE_DIR ${LAPACKE_INCLUDE_DIR}) + list(APPEND LAPACK_LIBRARY ${LAPACKE_LIBRARY}) + else() + message(WARNING "Trouble finding LAPACKE: + include: ${LAPACKE_INCLUDE_DIR} + libs: ${LAPACKE_LIBRARY}") + return() + endif() + + mark_as_advanced(LAPACKE_LIBRARY LAPACKE_INCLUDE_DIR) +endif(LAPACKE IN_LIST LAPACK_FIND_COMPONENTS) + +pkg_check_modules(pc_blas blas-netlib QUIET) +if(NOT pc_blas_FOUND) + pkg_check_modules(pc_blas blas QUIET) # Netlib on Cygwin and others +endif() +find_library(BLAS_LIBRARY + NAMES refblas blas + NAMES_PER_DIR + PATHS /usr/local/opt + HINTS ${_lapack_hints} ${pc_blas_LIBRARY_DIRS} ${pc_blas_LIBDIR} + PATH_SUFFIXES lib lapack lapack/lib blas) + +if(BLAS_LIBRARY) + list(APPEND LAPACK_LIBRARY ${BLAS_LIBRARY}) + set(LAPACK_Netlib_FOUND true PARENT_SCOPE) +else() + return() +endif() + +if(NOT WIN32) + list(APPEND LAPACK_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) +endif() + +if(LAPACK95_LIBRARY) + set(LAPACK_LAPACK95_FOUND true PARENT_SCOPE) +endif() + +set(LAPACK_LIBRARY ${LAPACK_LIBRARY} PARENT_SCOPE) +set(LAPACK_INCLUDE_DIR ${LAPACK_INCLUDE_DIR} PARENT_SCOPE) + +endfunction(netlib_libs) + +#=============================== +function(openblas_libs) + +pkg_check_modules(pc_lapack lapack-openblas QUIET) +find_library(LAPACK_LIBRARY + NAMES lapack + NAMES_PER_DIR + HINTS ${pc_lapack_LIBRARY_DIRS} ${pc_lapack_LIBDIR} + PATH_SUFFIXES openblas) + + +pkg_check_modules(pc_blas blas-openblas QUIET) +find_library(BLAS_LIBRARY + NAMES openblas blas + NAMES_PER_DIR + HINTS ${pc_blas_LIBRARY_DIRS} ${pc_blas_LIBDIR} + PATH_SUFFIXES openblas) + +find_path(LAPACK_INCLUDE_DIR + NAMES cblas-openblas.h cblas.h f77blas.h openblas_config.h + HINTS ${pc_lapack_INCLUDE_DIRS}) + +if(LAPACK_LIBRARY AND BLAS_LIBRARY) + list(APPEND LAPACK_LIBRARY ${BLAS_LIBRARY}) + set(LAPACK_OpenBLAS_FOUND true PARENT_SCOPE) +else() + message(WARNING "Trouble finding OpenBLAS: + include: ${LAPACK_INCLUDE_DIR} + libs: ${LAPACK_LIBRARY} ${BLAS_LIBRARY}") + return() +endif() + +if(NOT WIN32) + find_package(Threads) # not required--for example Flang + list(APPEND LAPACK_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) +endif() + +set(LAPACK_LIBRARY ${LAPACK_LIBRARY} PARENT_SCOPE) +set(LAPACK_INCLUDE_DIR ${LAPACK_INCLUDE_DIR} PARENT_SCOPE) + +endfunction(openblas_libs) + +#=============================== + +function(find_mkl_libs) +# https://software.intel.com/en-us/articles/intel-mkl-link-line-advisor + +set(_mkl_libs ${ARGV}) +if((UNIX AND NOT APPLE) AND CMAKE_Fortran_COMPILER_ID STREQUAL GNU) + list(INSERT _mkl_libs 0 mkl_gf_${_mkl_bitflag}lp64) +else() + list(INSERT _mkl_libs 0 mkl_intel_${_mkl_bitflag}lp64) +endif() + +# Note: Don't remove items from PATH_SUFFIXES unless you're extensively testing, +# each path is there for a specific reason! + +foreach(s ${_mkl_libs}) + find_library(LAPACK_${s}_LIBRARY + NAMES ${s} + NAMES_PER_DIR + PATHS ${MKLROOT} ENV TBBROOT + PATH_SUFFIXES + lib lib/intel64 lib/intel64_win + lib/intel64/gcc4.7 ../tbb/lib/intel64/gcc4.7 + lib/intel64/vc_mt ../tbb/lib/intel64/vc_mt + ../compiler/lib/intel64 + HINTS ${pc_mkl_LIBRARY_DIRS} ${pc_mkl_LIBDIR} + NO_DEFAULT_PATH) + + if(NOT LAPACK_${s}_LIBRARY) + message(STATUS "MKL component not found: " ${s}) + return() + endif() + + list(APPEND LAPACK_LIB ${LAPACK_${s}_LIBRARY}) +endforeach() + +if(NOT BUILD_SHARED_LIBS AND (UNIX AND NOT APPLE)) + set(LAPACK_LIB -Wl,--start-group ${LAPACK_LIB} -Wl,--end-group) +endif() + +set(BLAS_LIBRARY PARENT_SCOPE) +set(LAPACK_LIBRARY ${LAPACK_LIB} PARENT_SCOPE) +set(LAPACK_INCLUDE_DIR + ${MKLROOT}/include + ${MKLROOT}/include/intel64/${_mkl_bitflag}lp64 + PARENT_SCOPE) + # ${pc_mkl_INCLUDE_DIRS} has garbage on Windows + +endfunction(find_mkl_libs) + +# ========== main program + +if(NOT (OpenBLAS IN_LIST LAPACK_FIND_COMPONENTS + OR Netlib IN_LIST LAPACK_FIND_COMPONENTS + OR Atlas IN_LIST LAPACK_FIND_COMPONENTS + OR MKL IN_LIST LAPACK_FIND_COMPONENTS)) + if(DEFINED ENV{MKLROOT}) + list(APPEND LAPACK_FIND_COMPONENTS MKL) + else() + list(APPEND LAPACK_FIND_COMPONENTS Netlib) + endif() +endif() + +find_package(PkgConfig QUIET) + +# ==== generic MKL variables ==== + +if(MKL IN_LIST LAPACK_FIND_COMPONENTS) + # we have to sanitize MKLROOT if it has Windows backslashes (\) otherwise it will break at build time + # double-quotes are necessary per CMake to_cmake_path docs. + file(TO_CMAKE_PATH "$ENV{MKLROOT}" MKLROOT) + + list(APPEND CMAKE_PREFIX_PATH ${MKLROOT}/bin/pkgconfig) + + if(NOT WIN32) + find_package(Threads) + endif() + + if(BUILD_SHARED_LIBS) + set(_mkltype dynamic) + else() + set(_mkltype static) + endif() + + if(MKL64 IN_LIST LAPACK_FIND_COMPONENTS) + set(_mkl_bitflag i) + else() + set(_mkl_bitflag) + endif() + + unset(_mkl_libs) + if(LAPACK95 IN_LIST LAPACK_FIND_COMPONENTS) + list(APPEND _mkl_libs mkl_blas95_${_mkl_bitflag}lp64 mkl_lapack95_${_mkl_bitflag}lp64) + endif() + + unset(_tbb) + if(TBB IN_LIST LAPACK_FIND_COMPONENTS) + list(APPEND _mkl_libs mkl_tbb_thread mkl_core) + set(_tbb tbb stdc++) + if(WIN32) + list(APPEND _mkl_libs tbb.lib) + endif() + elseif(OpenMP IN_LIST LAPACK_FIND_COMPONENTS) + pkg_check_modules(pc_mkl mkl-${_mkltype}-${_mkl_bitflag}lp64-iomp QUIET) + + set(_mp iomp5) + if(WIN32) + set(_mp libiomp5md) # "lib" is indeed necessary, even on CMake 3.14.0 + endif() + list(APPEND _mkl_libs mkl_intel_thread mkl_core ${_mp}) + else() + pkg_check_modules(pc_mkl mkl-${_mkltype}-${_mkl_bitflag}lp64-seq QUIET) + list(APPEND _mkl_libs mkl_sequential mkl_core) + endif() + + find_mkl_libs(${_mkl_libs}) + + if(LAPACK_LIBRARY) + + if(NOT WIN32) + list(APPEND LAPACK_LIBRARY ${_tbb} ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS} m) + endif() + + set(LAPACK_MKL_FOUND true) + + if(MKL64 IN_LIST LAPACK_FIND_COMPONENTS) + set(LAPACK_MKL64_FOUND true) + endif() + + if(LAPACK95 IN_LIST LAPACK_FIND_COMPONENTS) + set(LAPACK_LAPACK95_FOUND true) + endif() + + if(OpenMP IN_LIST LAPACK_FIND_COMPONENTS) + set(LAPACK_OpenMP_FOUND true) + endif() + + if(TBB IN_LIST LAPACK_FIND_COMPONENTS) + set(LAPACK_TBB_FOUND true) + endif() + endif() + +elseif(Atlas IN_LIST LAPACK_FIND_COMPONENTS) + + atlas_libs() + +elseif(Netlib IN_LIST LAPACK_FIND_COMPONENTS) + + netlib_libs() + +elseif(OpenBLAS IN_LIST LAPACK_FIND_COMPONENTS) + + openblas_libs() + +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LAPACK + REQUIRED_VARS LAPACK_LIBRARY + HANDLE_COMPONENTS) + +set(BLAS_LIBRARIES ${BLAS_LIBRARY}) +set(LAPACK_LIBRARIES ${LAPACK_LIBRARY}) +set(LAPACK_INCLUDE_DIRS ${LAPACK_INCLUDE_DIR}) + +if(LAPACK_FOUND) +# need if _FOUND guard to allow project to autobuild; can't overwrite imported target even if bad + if(NOT TARGET BLAS::BLAS) + add_library(BLAS::BLAS INTERFACE IMPORTED) + set_target_properties(BLAS::BLAS PROPERTIES + INTERFACE_LINK_LIBRARIES "${BLAS_LIBRARY}" + ) + endif() + + if(NOT TARGET LAPACK::LAPACK) + add_library(LAPACK::LAPACK INTERFACE IMPORTED) + set_target_properties(LAPACK::LAPACK PROPERTIES + INTERFACE_LINK_LIBRARIES "${LAPACK_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${LAPACK_INCLUDE_DIR}" + ) + endif() +endif() + +mark_as_advanced(LAPACK_LIBRARY LAPACK_INCLUDE_DIR) diff --git a/build_tools/cmake/FindNetCDF.cmake b/build_tools/cmake/FindNetCDF.cmake new file mode 100644 index 0000000000..344714b18a --- /dev/null +++ b/build_tools/cmake/FindNetCDF.cmake @@ -0,0 +1,143 @@ +# - Try to find NetCDF +# +# This can be controlled by setting the NetCDF_PATH (or, equivalently, the +# NETCDF environment variable), or NetCDF__PATH CMake variables, where +# is the COMPONENT language one needs. +# +# Once done, this will define: +# +# NetCDF__FOUND (BOOL) - system has NetCDF +# NetCDF__IS_SHARED (BOOL) - whether library is shared/dynamic +# NetCDF__INCLUDE_DIR (PATH) - Location of the C header file +# NetCDF__INCLUDE_DIRS (LIST) - the NetCDF include directories +# NetCDF__LIBRARY (FILE) - Path to the C library file +# NetCDF__LIBRARIES (LIST) - link these to use NetCDF +# +# The available COMPONENTS are: C Fortran +# If no components are specified, it assumes only C +include (LibFind) +include (LibCheck) + +# Define NetCDF C Component +define_package_component (NetCDF DEFAULT + COMPONENT C + INCLUDE_NAMES netcdf.h + LIBRARY_NAMES netcdf) + +# Define NetCDF Fortran Component +define_package_component (NetCDF + COMPONENT Fortran + INCLUDE_NAMES netcdf.mod netcdf.inc + LIBRARY_NAMES netcdff) + +# Search for list of valid components requested +find_valid_components (NetCDF) + +#============================================================================== +# SEARCH FOR VALIDATED COMPONENTS +foreach (NCDFcomp IN LISTS NetCDF_FIND_VALID_COMPONENTS) + + # If not found already, search... + if (NOT NetCDF_${NCDFcomp}_FOUND) + + # Manually add the MPI include and library dirs to search paths + # and search for the package component + if (MPI_${NCDFcomp}_FOUND) + initialize_paths (NetCDF_${NCDFcomp}_PATHS + INCLUDE_DIRECTORIES ${MPI_${NCDFcomp}_INCLUDE_PATH} + LIBRARIES ${MPI_${NCDFcomp}_LIBRARIES}) + find_package_component(NetCDF COMPONENT ${NCDFcomp} + PATHS ${NetCDF_${NCDFcomp}_PATHS}) + else () + find_package_component(NetCDF COMPONENT ${NCDFcomp}) + endif () + + # Continue only if component found + if (NetCDF_${NCDFcomp}_FOUND) + + # Checks + if (NCDFcomp STREQUAL C) + + # Check version + check_version (NetCDF + NAME "netcdf_meta.h" + HINTS ${NetCDF_C_INCLUDE_DIRS} + MACRO_REGEX "NC_VERSION_") + + # Check for parallel support + check_macro (NetCDF_C_HAS_PARALLEL + NAME TryNetCDF_PARALLEL.c + HINTS ${CMAKE_MODULE_PATH} + DEFINITIONS -I${NetCDF_C_INCLUDE_DIR} + COMMENT "whether NetCDF has parallel support") + + # Check if logging enabled + set(CMAKE_REQUIRED_INCLUDES ${NetCDF_C_INCLUDE_DIR}) + set(CMAKE_REQUIRED_LIBRARIES ${NetCDF_C_LIBRARIES}) + CHECK_FUNCTION_EXISTS(nc_set_log_level NetCDF_C_LOGGING_ENABLED) + + endif () + + # Dependencies + if (NCDFcomp STREQUAL C AND NOT NetCDF_C_IS_SHARED) + + # DEPENDENCY: PnetCDF (if PnetCDF enabled) + check_macro (NetCDF_C_HAS_PNETCDF + NAME TryNetCDF_PNETCDF.c + HINTS ${CMAKE_MODULE_PATH} + DEFINITIONS -I${NetCDF_C_INCLUDE_DIR} + COMMENT "whether NetCDF has PnetCDF support") + if (NetCDF_C_HAS_PNETCDF) + find_package (PnetCDF COMPONENTS C) + if (CURL_FOUND) + list (APPEND NetCDF_C_INCLUDE_DIRS ${PnetCDF_C_INCLUDE_DIRS}) + list (APPEND NetCDF_C_LIBRARIES ${PnetCDF_C_LIBRARIES}) + endif () + endif () + + # DEPENDENCY: CURL (If DAP enabled) + check_macro (NetCDF_C_HAS_DAP + NAME TryNetCDF_DAP.c + HINTS ${CMAKE_MODULE_PATH} + DEFINITIONS -I${NetCDF_C_INCLUDE_DIR} + COMMENT "whether NetCDF has DAP support") + if (NetCDF_C_HAS_DAP) + find_package (CURL) + if (CURL_FOUND) + list (APPEND NetCDF_C_INCLUDE_DIRS ${CURL_INCLUDE_DIRS}) + list (APPEND NetCDF_C_LIBRARIES ${CURL_LIBRARIES}) + endif () + endif () + + # DEPENDENCY: HDF5 + find_package (HDF5 COMPONENTS HL C) + if (HDF5_C_FOUND) + list (APPEND NetCDF_C_INCLUDE_DIRS ${HDF5_C_INCLUDE_DIRS} + ${HDF5_HL_INCLUDE_DIRS}) + list (APPEND NetCDF_C_LIBRARIES ${HDF5_C_LIBRARIES} + ${HDF5_HL_LIBRARIES}) + endif () + + # DEPENDENCY: LIBDL Math + list (APPEND NetCDF_C_LIBRARIES -ldl -lm) + + elseif (NCDFcomp STREQUAL Fortran AND NOT NetCDF_Fortran_IS_SHARED) + + # DEPENDENCY: NetCDF + set (orig_comp ${NCDFcomp}) + set (orig_comps ${NetCDF_FIND_VALID_COMPONENTS}) + find_package (NetCDF COMPONENTS C) + set (NetCDF_FIND_VALID_COMPONENTS ${orig_comps}) + set (NCDFcomp ${orig_comp}) + if (NetCDF_C_FOUND) + list (APPEND NetCDF_Fortran_INCLUDE_DIRS ${NetCDF_C_INCLUDE_DIRS}) + list (APPEND NetCDF_Fortran_LIBRARIES ${NetCDF_C_LIBRARIES}) + endif () + + endif () + + endif () + + endif () + +endforeach () diff --git a/build_tools/cmake/FindPnetCDF.cmake b/build_tools/cmake/FindPnetCDF.cmake new file mode 100644 index 0000000000..b87d245cd1 --- /dev/null +++ b/build_tools/cmake/FindPnetCDF.cmake @@ -0,0 +1,68 @@ +# - Try to find PnetCDF +# +# This can be controlled by setting the PnetCDF_PATH (or, equivalently, the +# PNETCDF environment variable), or PnetCDF__PATH CMake variables, where +# is the COMPONENT language one needs. +# +# Once done, this will define: +# +# PnetCDF__FOUND (BOOL) - system has PnetCDF +# PnetCDF__IS_SHARED (BOOL) - whether library is shared/dynamic +# PnetCDF__INCLUDE_DIR (PATH) - Location of the C header file +# PnetCDF__INCLUDE_DIRS (LIST) - the PnetCDF include directories +# PnetCDF__LIBRARY (FILE) - Path to the C library file +# PnetCDF__LIBRARIES (LIST) - link these to use PnetCDF +# +# The available COMPONENTS are: C, Fortran +# If no components are specified, it assumes only C +include (LibFind) +include (LibCheck) + +# Define PnetCDF C Component +define_package_component (PnetCDF DEFAULT + COMPONENT C + INCLUDE_NAMES pnetcdf.h + LIBRARY_NAMES pnetcdf) + +# Define PnetCDF Fortran Component +define_package_component (PnetCDF + COMPONENT Fortran + INCLUDE_NAMES pnetcdf.mod pnetcdf.inc + LIBRARY_NAMES pnetcdf) + +# Search for list of valid components requested +find_valid_components (PnetCDF) + +#============================================================================== +# SEARCH FOR VALIDATED COMPONENTS +foreach (PNCDFcomp IN LISTS PnetCDF_FIND_VALID_COMPONENTS) + + # If not found already, search... + if (NOT PnetCDF_${PNCDFcomp}_FOUND) + + # Manually add the MPI include and library dirs to search paths + # and search for the package component + if (MPI_${PNCDFcomp}_FOUND) + initialize_paths (PnetCDF_${PNCDFcomp}_PATHS + INCLUDE_DIRECTORIES ${MPI_${PNCDFcomp}_INCLUDE_PATH} + LIBRARIES ${MPI_${PNCDFcomp}_LIBRARIES}) + find_package_component(PnetCDF COMPONENT ${PNCDFcomp} + PATHS ${PnetCDF_${PNCDFcomp}_PATHS}) + else () + find_package_component(PnetCDF COMPONENT ${PNCDFcomp}) + endif () + + # Continue only if component found + if (PnetCDF_${PNCDFcomp}_FOUND) + + # Check version + check_version (PnetCDF + NAME "pnetcdf.h" + HINTS ${PnetCDF_${PNCDFcomp}_INCLUDE_DIR} + MACRO_REGEX "PNETCDF_VERSION_") + + endif () + + endif () + +endforeach () diff --git a/build_tools/cmake/LibCheck.cmake b/build_tools/cmake/LibCheck.cmake new file mode 100644 index 0000000000..3f12bdf796 --- /dev/null +++ b/build_tools/cmake/LibCheck.cmake @@ -0,0 +1,104 @@ +include (CMakeParseArguments) +include (CheckFunctionExists) +#============================================================================== +# +# FUNCTIONS TO HELP WITH Check* MODULES +# +#============================================================================== + +#______________________________________________________________________________ +# - Basic function to check a property of a package using a try_compile step +# +# SYNTAX: check_macro ( +# NAME +# HINTS ... +# DEFINITIONS ... +# COMMENT ) +# +function (check_macro VARIABLE) + + # Parse the input arguments + set (oneValueArgs COMMENT NAME) + set (multiValueArgs HINTS DEFINITIONS) + cmake_parse_arguments (${VARIABLE} "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # If the return variable is defined, already, don't continue + if (NOT DEFINED ${VARIABLE}) + + message (STATUS "Checking ${${VARIABLE}_COMMENT}") + find_file (${VARIABLE}_TRY_FILE + NAMES ${${VARIABLE}_NAME} + HINTS ${${VARIABLE}_HINTS}) + if (${VARIABLE}_TRY_FILE) + try_compile (COMPILE_RESULT + ${CMAKE_CURRENT_BINARY_DIR}/try${VARIABLE} + SOURCES ${${VARIABLE}_TRY_FILE} + COMPILE_DEFINITIONS ${${VARIABLE}_DEFINITIONS} + OUTPUT_VARIABLE TryOUT) + if (COMPILE_RESULT) + message (STATUS "Checking ${${VARIABLE}_COMMENT} - yes") + else () + message (STATUS "Checking ${${VARIABLE}_COMMENT} - no") + endif () + + set (${VARIABLE} ${COMPILE_RESULT} + CACHE BOOL "${${VARIABLE}_COMMENT}") + + else () + message (STATUS "Checking ${${VARIABLE}_COMMENT} - failed") + endif () + + unset (${VARIABLE}_TRY_FILE CACHE) + endif () + +endfunction () + +#______________________________________________________________________________ +# - Basic function to check the version of a package using a try_run step +# +# SYNTAX: check_version ( +# NAME +# HINTS ... +# DEFINITIONS ...) +# +function (check_version PKG) + + # Parse the input arguments + set (oneValueArgs NAME MACRO_REGEX) + set (multiValueArgs HINTS) + cmake_parse_arguments (${PKG} "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # If the return variable is defined, already, don't continue + if (NOT DEFINED ${PKG}_VERSION) + + message (STATUS "Checking ${PKG} version") + find_file (${PKG}_VERSION_HEADER + NAMES ${${PKG}_NAME} + HINTS ${${PKG}_HINTS}) + if (${PKG}_VERSION_HEADER) + set (def) + file (STRINGS ${${PKG}_VERSION_HEADER} deflines + REGEX "^#define[ \\t]+${${PKG}_MACRO_REGEX}") + foreach (defline IN LISTS deflines) + string (REPLACE "\"" "" defline "${defline}") + string (REPLACE "." "" defline "${defline}") + string (REGEX REPLACE "[ \\t]+" ";" deflist "${defline}") + list (GET deflist 2 arg) + list (APPEND def ${arg}) + endforeach () + string (REPLACE ";" "." vers "${def}") + message (STATUS "Checking ${PKG} version - ${vers}") + set (${PKG}_VERSION ${vers} + CACHE STRING "${PKG} version string") + if (${PKG}_VERSION VERSION_LESS ${PKG}_FIND_VERSION}) + message (FATAL_ERROR "${PKG} version insufficient") + endif () + else () + message (STATUS "Checking ${PKG} version - failed") + endif () + + unset (${PKG}_VERSION_HEADER CACHE) + + endif () + +endfunction () \ No newline at end of file diff --git a/build_tools/cmake/LibFind.cmake b/build_tools/cmake/LibFind.cmake new file mode 100644 index 0000000000..7da13e3259 --- /dev/null +++ b/build_tools/cmake/LibFind.cmake @@ -0,0 +1,329 @@ +include (CMakeParseArguments) +include(FindPackageHandleStandardArgs) + +#============================================================================== +# +# FUNCTIONS TO HELP WITH Find* MODULES +# +#============================================================================== + +#______________________________________________________________________________ +# - Wrapper for finding static libraries ONLY +# +macro (find_static_library) + set (_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) + find_library(${ARGN}) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${_CMAKE_FIND_LIBRARY_SUFFIXES}) + unset (_CMAKE_FIND_LIBRARY_SUFFIXES) +endmacro () + + +#______________________________________________________________________________ +# - Wrapper for finding shared/dynamic libraries ONLY +# +macro (find_shared_library) + set (_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX}) + find_library(${ARGN}) + set (CMAKE_FIND_LIBRARY_SUFFIXES ${_CMAKE_FIND_LIBRARY_SUFFIXES}) + unset (_CMAKE_FIND_LIBRARY_SUFFIXES) +endmacro () + + +#______________________________________________________________________________ +# - Function to determine type (SHARED or STATIC) of library +# +# Input: +# LIB (FILE) +# +# Returns: +# RETURN_VAR (BOOL) +# +function (is_shared_library RETURN_VAR LIB) + get_filename_component(libext ${LIB} EXT) + if (libext MATCHES ${CMAKE_SHARED_LIBRARY_SUFFIX}) + set (${RETURN_VAR} TRUE PARENT_SCOPE) + else () + set (${RETURN_VAR} FALSE PARENT_SCOPE) + endif () +endfunction () + + +#______________________________________________________________________________ +# - Function to define a valid package component +# +# Input: +# ${PKG}_DEFAULT (BOOL) +# ${PKG}_COMPONENT (STRING) +# ${PKG}_INCLUDE_NAMES (LIST) +# ${PKG}_LIBRARY_NAMES (LIST) +# +# Returns: +# ${PKG}_DEFAULT_COMPONENT (STRING) +# ${PKG}_VALID_COMPONENTS (LIST) +# ${PKG}_${COMPONENT}_INCLUDE_NAMES (LIST) +# ${PKG}_${COMPONENT}_LIBRARY_NAMES (LIST) +# +function (define_package_component PKG) + + # Parse the input arguments + set (options DEFAULT) + set (oneValueArgs COMPONENT) + set (multiValueArgs INCLUDE_NAMES LIBRARY_NAMES) + cmake_parse_arguments (${PKG} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + if (${PKG}_COMPONENT) + set (PKGCOMP ${PKG}_${${PKG}_COMPONENT}) + else () + set (PKGCOMP ${PKG}) + endif () + + # Set return values + if (${PKG}_COMPONENT) + if (${PKG}_DEFAULT) + set (${PKG}_DEFAULT_COMPONENT ${${PKG}_COMPONENT} PARENT_SCOPE) + endif () + set (VALID_COMPONENTS ${${PKG}_VALID_COMPONENTS}) + list (APPEND VALID_COMPONENTS ${${PKG}_COMPONENT}) + set (${PKG}_VALID_COMPONENTS ${VALID_COMPONENTS} PARENT_SCOPE) + endif () + set (${PKGCOMP}_INCLUDE_NAMES ${${PKG}_INCLUDE_NAMES} PARENT_SCOPE) + set (${PKGCOMP}_LIBRARY_NAMES ${${PKG}_LIBRARY_NAMES} PARENT_SCOPE) + +endfunction () + + +#______________________________________________________________________________ +# - Function to find valid package components +# +# Assumes pre-defined variables: +# ${PKG}_FIND_COMPONENTS (LIST) +# ${PKG}_DEFAULT_COMPONENT (STRING) +# ${PKG}_VALID_COMPONENTS (LIST) +# +# Returns: +# ${PKG}_FIND_VALID_COMPONENTS (LIST) +# +function (find_valid_components PKG) + + if (NOT ${PKG}_FIND_COMPONENTS) + set (${PKG}_FIND_COMPONENTS ${${PKG}_DEFAULT_COMPONENT}) + endif () + + set (FIND_VALID_COMPONENTS) + foreach (comp IN LISTS ${PKG}_FIND_COMPONENTS) + if (";${${PKG}_VALID_COMPONENTS};" MATCHES ";${comp};") + list (APPEND FIND_VALID_COMPONENTS ${comp}) + endif () + endforeach () + + set (${PKG}_FIND_VALID_COMPONENTS ${FIND_VALID_COMPONENTS} PARENT_SCOPE) + +endfunction () + + +#______________________________________________________________________________ +# - Initialize a list of paths from a list of includes and libraries +# +# Input: +# INCLUDE_DIRECTORIES +# LIBRARIES +# +# Ouput: +# ${PATHLIST} +# +function (initialize_paths PATHLIST) + + # Parse the input arguments + set (multiValueArgs INCLUDE_DIRECTORIES LIBRARIES) + cmake_parse_arguments (INIT "" "" "${multiValueArgs}" ${ARGN}) + + set (paths) + foreach (inc IN LISTS INIT_INCLUDE_DIRECTORIES) + list (APPEND paths ${inc}) + get_filename_component (dname ${inc} NAME) + if (dname MATCHES "include") + get_filename_component (prefx ${inc} PATH) + list (APPEND paths ${prefx}) + endif () + endforeach () + foreach (lib IN LISTS INIT_LIBRARIES) + get_filename_component (libdir ${lib} PATH) + list (APPEND paths ${libdir}) + get_filename_component (dname ${libdir} PATH) + if (dname MATCHES "lib") + get_filename_component (prefx ${libdir} PATH) + list (APPEND paths ${prefx}) + endif () + endforeach () + + set (${PATHLIST} ${paths} PARENT_SCOPE) + +endfunction () + + +#______________________________________________________________________________ +# - Basic find package macro for a specific component +# +# Assumes pre-defined variables: +# ${PKG}_${COMP}_INCLUDE_NAMES or ${PKG}_INCLUDE_NAMES +# ${PKG}_${COMP}_LIBRARY_NAMES or ${PKG}_LIBRARY_NAMES +# +# Input: +# ${PKG}_COMPONENT +# ${PKG}_HINTS +# ${PKG}_PATHS +# +function (find_package_component PKG) + + # Parse the input arguments + set (options) + set (oneValueArgs COMPONENT) + set (multiValueArgs HINTS PATHS) + cmake_parse_arguments (${PKG} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set (COMP ${${PKG}_COMPONENT}) + if (COMP) + set (PKGCOMP ${PKG}_${COMP}) + else () + set (PKGCOMP ${PKG}) + endif () + string (TOUPPER ${PKG} PKGUP) + string (TOUPPER ${PKGCOMP} PKGCOMPUP) + + # Only continue if package not found already + if (NOT ${PKGCOMP}_FOUND) + + # Handle QUIET and REQUIRED arguments + if (${${PKG}_FIND_QUIETLY}) + set (${PKGCOMP}_FIND_QUIETLY TRUE) + endif () + if (${${PKG}_FIND_REQUIRED}) + set (${PKGCOMP}_FIND_REQUIRED TRUE) + endif () + + # Determine search order + set (SEARCH_DIRS) + if (${PKG}_HINTS) + list (APPEND SEARCH_DIRS ${${PKG}_HINTS}) + endif () + if (${PKGCOMP}_PATH) + list (APPEND SEARCH_DIRS ${${PKGCOMP}_PATH}) + endif () + if (${PKG}_PATH) + list (APPEND SEARCH_DIRS ${${PKG}_PATH}) + endif () + if (DEFINED ENV{${PKGCOMPUP}}) + list (APPEND SEARCH_DIRS $ENV{${PKGCOMPUP}}) + endif () + if (DEFINED ENV{${PKGUP}}) + list (APPEND SEARCH_DIRS $ENV{${PKGUP}}) + endif () + if (CMAKE_SYSTEM_PREFIX_PATH) + list (APPEND SEARCH_DIRS ${CMAKE_SYSTEM_PREFIX_PATH}) + endif () + if (${PKG}_PATHS) + list (APPEND SEARCH_DIRS ${${PKG}_PATHS}) + endif () + + # Start the search for the include file and library file + set (${PKGCOMP}_PREFIX ${PKGCOMP}_PREFIX-NOTFOUND) + set (${PKGCOMP}_INCLUDE_DIR ${PKGCOMP}_INCLUDE_DIR-NOTFOUND) + set (${PKGCOMP}_LIBRARY ${PKGCOMP}_LIBRARY-NOTFOUND) + foreach (dir IN LISTS SEARCH_DIRS) + + # Search for include file names in current dirrectory + foreach (iname IN LISTS ${PKGCOMP}_INCLUDE_NAMES) + if (EXISTS ${dir}/${iname}) + set (${PKGCOMP}_PREFIX ${dir}) + set (${PKGCOMP}_INCLUDE_DIR ${dir}) + break () + endif () + if (EXISTS ${dir}/include/${iname}) + set (${PKGCOMP}_PREFIX ${dir}) + set (${PKGCOMP}_INCLUDE_DIR ${dir}/include) + break () + endif () + endforeach () + + # Search for library file names in the found prefix only! + if (${PKGCOMP}_PREFIX) + find_library (${PKGCOMP}_LIBRARY + NAMES ${${PKGCOMP}_LIBRARY_NAMES} + PATHS ${${PKGCOMP}_PREFIX} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) + + # If found, check if library is static or dynamic + if (${PKGCOMP}_LIBRARY) + is_shared_library (${PKGCOMP}_IS_SHARED ${${PKGCOMP}_LIBRARY}) + + # If we want only shared libraries, and it isn't shared... + if (PREFER_SHARED AND NOT ${PKGCOMP}_IS_SHARED) + find_shared_library (${PKGCOMP}_SHARED_LIBRARY + NAMES ${${PKGCOMP}_LIBRARY_NAMES} + PATHS ${${PKGCOMP}_PREFIX} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) + if (${PKGCOMP}_SHARED_LIBRARY) + set (${PKGCOMP}_LIBRARY ${${PKGCOMP}_SHARED_LIBRARY}) + set (${PKGCOMP}_IS_SHARED TRUE) + endif () + + # If we want only static libraries, and it is shared... + elseif (PREFER_STATIC AND ${PKGCOMP}_IS_SHARED) + find_static_library (${PKGCOMP}_STATIC_LIBRARY + NAMES ${${PKGCOMP}_LIBRARY_NAMES} + PATHS ${${PKGCOMP}_PREFIX} + PATH_SUFFIXES lib + NO_DEFAULT_PATH) + if (${PKGCOMP}_STATIC_LIBRARY) + set (${PKGCOMP}_LIBRARY ${${PKGCOMP}_STATIC_LIBRARY}) + set (${PKGCOMP}_IS_SHARED FALSE) + endif () + endif () + endif () + + # If include dir and library both found, then we're done + if (${PKGCOMP}_INCLUDE_DIR AND ${PKGCOMP}_LIBRARY) + break () + + # Otherwise, reset the search variables and continue + else () + set (${PKGCOMP}_PREFIX ${PKGCOMP}_PREFIX-NOTFOUND) + set (${PKGCOMP}_INCLUDE_DIR ${PKGCOMP}_INCLUDE_DIR-NOTFOUND) + set (${PKGCOMP}_LIBRARY ${PKGCOMP}_LIBRARY-NOTFOUND) + endif () + endif () + + endforeach () + + # handle the QUIETLY and REQUIRED arguments and + # set NetCDF_C_FOUND to TRUE if all listed variables are TRUE + find_package_handle_standard_args (${PKGCOMP} DEFAULT_MSG + ${PKGCOMP}_LIBRARY + ${PKGCOMP}_INCLUDE_DIR) + mark_as_advanced (${PKGCOMP}_INCLUDE_DIR ${PKGCOMP}_LIBRARY) + + # HACK For bug in CMake v3.0: + set (${PKGCOMP}_FOUND ${${PKGCOMPUP}_FOUND}) + + # Set return variables + if (${PKGCOMP}_FOUND) + set (${PKGCOMP}_INCLUDE_DIRS ${${PKGCOMP}_INCLUDE_DIR}) + set (${PKGCOMP}_LIBRARIES ${${PKGCOMP}_LIBRARY}) + endif () + + # Set variables in parent scope + set (${PKGCOMP}_FOUND ${${PKGCOMP}_FOUND} PARENT_SCOPE) + set (${PKGCOMP}_INCLUDE_DIR ${${PKGCOMP}_INCLUDE_DIR} PARENT_SCOPE) + set (${PKGCOMP}_INCLUDE_DIRS ${${PKGCOMP}_INCLUDE_DIRS} PARENT_SCOPE) + set (${PKGCOMP}_LIBRARY ${${PKGCOMP}_LIBRARY} PARENT_SCOPE) + set (${PKGCOMP}_LIBRARIES ${${PKGCOMP}_LIBRARIES} PARENT_SCOPE) + set (${PKGCOMP}_IS_SHARED ${${PKGCOMP}_IS_SHARED} PARENT_SCOPE) + + endif () + +endfunction () + + + diff --git a/build_tools/cmake/TryCSizeOf.f90 b/build_tools/cmake/TryCSizeOf.f90 new file mode 100644 index 0000000000..272ed27618 --- /dev/null +++ b/build_tools/cmake/TryCSizeOf.f90 @@ -0,0 +1,6 @@ + program trycsizeof + use iso_c_binding, only : c_sizeof + integer :: b + integer :: a(5) + b = c_sizeof(a(1)) + end program trycsizeof diff --git a/build_tools/cmake/TryMPIIO.f90 b/build_tools/cmake/TryMPIIO.f90 new file mode 100644 index 0000000000..5f1fd1f47f --- /dev/null +++ b/build_tools/cmake/TryMPIIO.f90 @@ -0,0 +1,6 @@ +program mpicheck + include "mpif.h" + integer :: fh, ierr + + call mpi_file_open(mpi_comm_world, 'stupid.file',MPI_MODE_RDWR,MPI_INFO_NULL,fh,ierr) +end program diff --git a/build_tools/cmake/TryMPIMod.f90 b/build_tools/cmake/TryMPIMod.f90 new file mode 100644 index 0000000000..5fcd14632e --- /dev/null +++ b/build_tools/cmake/TryMPIMod.f90 @@ -0,0 +1,4 @@ +program mpimodcheck + use mpi, only : MPI_ROOT, MPI_OFFSET + integer,parameter:: a = MPI_ROOT +end program mpimodcheck diff --git a/build_tools/genf90.pl b/build_tools/genf90.pl new file mode 100755 index 0000000000..5d35112e95 --- /dev/null +++ b/build_tools/genf90.pl @@ -0,0 +1,387 @@ +#!/usr/bin/env perl +use strict; +my $outfile; +# Beginning with F90, Fortran has strict typing of variables based on "TKR" +# (type, kind, and rank). In many cases we want to write subroutines that +# provide the same functionality for different variable types and ranks. In +# order to do this without cut-and-paste duplication of code, we create a +# template file with the extension ".F90.in", which can be parsed by this script +# to generate F90 code for all of the desired specific types. +# +# Keywords are delimited by curly brackets: {} +# +# {TYPE} and {DIMS} are used to generate the specific subroutine names from the +# generic template +# {TYPE} : Variable type name; implemented types are character, 4 or 8 byte real, +# and 4 or 8 byte integer. +# allowed values: text, real, double, int, long, logical +# default values: text, real, double, int +# {VTYPE} : Used to generate variable declarations to match the specific type. +# if {TYPE}=double then {VTYPE} is "real(r8)" +# {ITYPE}, {ITYPENAME} : Used to generate CPP statements for the specific type. +# {MPITYPE} : Used to generate MPI types corresponding to the specific type. +# +# {DIMS} : Rank of arrays, "0" for scalar. +# allowed values: 0-7 +# default values : 0-5 +# {DIMSTR} : Generates the parenthesis and colons used for a variable +# declaration of {DIMS} dimensions. +# if {DIMS}=3 then {DIMSTR} is (:,:,:) +# {REPEAT} : Repeats an expression for each number from 1 to {DIMS}, with each +# iteration separated by commas. +# {REPEAT: foo(#, bar)} +# expands to this: +# foo(1, bar), foo(2, bar), foo(3, bar), ... + +# defaults +my @types = qw(text real double int); +my $vtype = {'text' => 'character(len=*)', + 'real' => 'real(r4)', + 'double' => 'real(r8)', + 'int' => 'integer(i4)', + 'long' => 'integer(i8)', + 'logical' => 'logical' }; +my $itype = {'text' => 100, + 'real' => 101, + 'double' => 102, + 'int' => 103, + 'long' => 104, + 'logical' => 105}; +my $itypename = {'text' => 'TYPETEXT', + 'real' => 'TYPEREAL', + 'double' => 'TYPEDOUBLE', + 'int' => 'TYPEINT', + 'long' => 'TYPELONG', + 'logical' => 'TYPELOGICAL'}; +my $mpitype = {'text' => 'MPI_CHARACTER', + 'real' => 'MPI_REAL4', + 'double' => 'MPI_REAL8', + 'int' => 'MPI_INTEGER'}; +# Netcdf C datatypes +my $nctype = {'text' => 'text', + 'real' => 'float', + 'double' => 'double', + 'int' => 'int'}; +# C interoperability types +my $ctype = {'text' => 'character(C_CHAR)', + 'real' => 'real(C_FLOAT)', + 'double' => 'real(C_DOUBLE)', + 'int' => 'integer(C_INT)'}; + + + +my @dims =(0..5); + +my $write_dtypes = "no"; +# begin + +foreach(@ARGV){ + my $infile = $_; + usage() unless($infile =~ /(.*.F90).in/); + $outfile = $1; + open(F,"$infile") || die "$0 Could not open $infile to read"; + my @parsetext; + my $cnt=0; + foreach(){ + $cnt++; + if(/^\s*contains/i){ + push(@parsetext,"# $cnt \"$infile\"\n"); + } + if(/^\s*interface/i){ + push(@parsetext,"# $cnt \"$infile\"\n"); + } + if(/^[^!]*subroutine/i){ + push(@parsetext,"# $cnt \"$infile\"\n"); + } + if(/^[^!]*function/i){ + push(@parsetext,"# $cnt \"$infile\"\n"); + } + + push(@parsetext,$_); + } + + close(F); + + my $end; + my $contains=0; + my $in_type_block=0; + my @unit; + my $unitcnt=0; + my $date = localtime(); + my $preamble = +"!=================================================== +! DO NOT EDIT THIS FILE, it was generated using $0 +! Any changes you make to this file may be lost +!===================================================\n"; + my @output ; + push(@output,$preamble); + + my $line; + my $dimmodifier; + my $typemodifier; + my $itypeflag; + my $block; + my $block_type; + my $cppunit; + foreach $line (@parsetext){ +# skip parser comments + next if($line =~ /\s*!pl/); + + $itypeflag=1 if($line =~ /{ITYPE}/); + $itypeflag=1 if($line =~ /TYPETEXT/); + $itypeflag=1 if($line =~ /TYPEREAL/); + $itypeflag=1 if($line =~ /TYPEDOUBLE/); + $itypeflag=1 if($line =~ /TYPEINT/); + $itypeflag=1 if($line =~ /TYPELONG/); + + + if($contains==0){ + if($line=~/\s*!\s*DIMS\s+[\d,]+!*/){ + $dimmodifier=$line; + next; + } + if($line=~/\s*!\s*TYPE\s+[^!]+!*$/){ + $typemodifier=$line; + next; + } + if ((defined $typemodifier or defined $dimmodifier) + and not defined $block and $line=~/^\s*#[^{]*$/) { + push(@output, $line); + next; + } + # Figure out the bounds of a type statement. + # Type blocks start with "type," "type foo" or "type::" but not + # "type(". + $in_type_block=1 if($line=~/^\s*type\s*[,:[:alpha:]]/i); + $in_type_block=0 if($line=~/^\s*end\s*type/i); + if(not defined $block) { + if ($line=~/^\s*type[^[:alnum:]_].*(\{TYPE\}|\{DIMS\})/i or + $line=~/^[^!]*(function|subroutine).*(\{TYPE\}|\{DIMS\})/i) { + $block=$line; + next; + } + if ($line=~/^\s*interface.*(\{TYPE\}|\{DIMS\})/i) { + $block_type="interface"; + $block=$line; + next; + } + } + if(not defined $block_type and + ($line=~/^\s*end\s+type\s+.*(\{TYPE\}|\{DIMS\})/i or + $line=~/^\s*end\s+(function|subroutine)\s+.*(\{TYPE\}|\{DIMS\})/i)){ + + $line = $block.$line; + undef $block; + } + if ($line=~/^\s*end\s*interface/i and + defined $block) { + $line = $block.$line; + undef $block; + undef $block_type; + } + if(defined $block){ + $block = $block.$line; + next; + } + if(defined $dimmodifier){ + $line = $dimmodifier.$line; + undef $dimmodifier; + } + if(defined $typemodifier){ + $line = $typemodifier.$line; + undef $typemodifier; + } + + push(@output, buildout($line)); + if(($line =~ /^\s*contains\s*!*/i && ! $in_type_block) or + ($line =~ /^\s*!\s*Not a module/i)){ + $contains=1; + next; + } + } + if($line=~/^\s*end module\s*/){ + $end = $line; + last; + } + + if($contains==1){ + # first parse into functions or subroutines + if($cppunit || !(defined($unit[$unitcnt]))){ + # Make cpp lines and blanks between routines units. + if($line =~ /^\s*\#(?!\s[[:digit:]]+)/ || $line =~/^\s*$/ || $line=~/^\s*!(?!\s*(TYPE|DIMS))/){ + push(@{$unit[$unitcnt]},$line); + $cppunit=1; + next; + } else { + $cppunit=0; + $unitcnt++; + } + } + + + push(@{$unit[$unitcnt]},$line); + if ($line=~/^\s*interface/i) { + $block_type="interface"; + $block=$line; + } + if ($line=~/^\s*end\s*interface/i) { + undef $block_type; + undef $block; + } + unless(defined $block){ + if($line =~ /\s*end function/i or $line =~ /\s*end subroutine/i){ + $unitcnt++; + } + } + } + } + my $i; + + + for($i=0;$i<$unitcnt;$i++){ + if(defined($unit[$i])){ + my $func = join('',@{$unit[$i]}); + push(@output, buildout($func)); + } + } + push(@output,@{$unit[$#unit]}) if($unitcnt==$#unit); + push(@output, $end); + if($itypeflag==1){ + my $str; + $str.="#include \"dtypes.h\"\n"; + $write_dtypes = "yes"; + print $str; + } + print @output; + writedtypes() if(!(-e "dtypes.h") && $write_dtypes == "yes"); + + +} + + +sub usage{ + die("$0 Expected input filename of the form .*.F90.in"); +} + +sub build_repeatstr{ + my($dims) = @_; + # Create regex to repeat expression DIMS times. + my $repeatstr; + for(my $i=1;$i<=$dims;$i++){ + $repeatstr .="\$\{1\}$i\$\{2\},&\n"; + } + if(defined $repeatstr){ + $repeatstr="\"$repeatstr"; + chop $repeatstr; + chop $repeatstr; + chop $repeatstr; + $repeatstr.="\""; + }else{ + $repeatstr=''; + } +} + +sub writedtypes{ + open(F,">dtypes.h"); + print F +"#define TYPETEXT 100 +#define TYPEREAL 101 +#define TYPEDOUBLE 102 +#define TYPEINT 103 +#define TYPELONG 104 +#define TYPELOGICAL 105 +"; + close(F); +} + +sub buildout{ + my ($func) = @_; + + my $outstr; + my(@ldims, @ltypes); + + if($func=~/\s*!\s*DIMS\s+([\d,]+)\s*/){ + @ldims = split(/,/,$1); + }else{ + @ldims = @dims; + } + if($func=~/\s*!\s*TYPE\s+([^!\s]+)\s*/){ + @ltypes = split(/,/,$1); +# print ">$func<>@ltypes<\n"; + }else{ + @ltypes = @types; + } + + + if(($func =~ /{TYPE}/ && $func =~ /{DIMS}/) ){ + my ($type, $dims); + foreach $type (@ltypes){ + foreach $dims (@ldims){ + my $dimstr; + for(my $i=1;$i<=$dims;$i++){ + $dimstr .=':,'; + } + if(defined $dimstr){ + $dimstr="($dimstr"; + chop $dimstr; + $dimstr.=')'; + }else{ + $dimstr=''; + } + + my $repeatstr = build_repeatstr($dims); + + my $str = $func; + $str =~ s/{TYPE}/$type/g; + $str =~ s/{VTYPE}/$vtype->{$type}/g; + $str =~ s/{ITYPE}/$itype->{$type}/g; + $str =~ s/{MPITYPE}/$mpitype->{$type}/g; + $str =~ s/{NCTYPE}/$nctype->{$type}/g; + $str =~ s/{CTYPE}/$ctype->{$type}/g; + $str =~ s/{DIMS}/$dims/g; + $str =~ s/{DIMSTR}/$dimstr/g; + $str =~ s/{REPEAT:([^#}]*)#([^#}]*)}/$repeatstr/eeg; + $outstr .= $str; + } + } + }elsif($func =~ /{DIMS}/){ + my $dims; + foreach $dims (@ldims){ + my $dimstr; + for(my $i=1;$i<=$dims;$i++){ + $dimstr .=':,'; + } + if(defined $dimstr){ + $dimstr="($dimstr"; + chop $dimstr; + $dimstr.=')'; + }else{ + $dimstr=''; + } + + my $repeatstr = build_repeatstr($dims); + + my $str = $func; + $str =~ s/{DIMS}/$dims/g; + $str =~ s/{DIMSTR}/$dimstr/g; + $str =~ s/{REPEAT:([^#}]*)#([^#}]*)}/$repeatstr/eeg; + $outstr .= $str; + } + }elsif($func =~ /{TYPE}/){ + my ($type); + foreach $type (@ltypes){ + my $str = $func; + $str =~ s/{TYPE}/$type/g; + $str =~ s/{VTYPE}/$vtype->{$type}/g; + $str =~ s/{ITYPE}/$itype->{$type}/g; + $str =~ s/{MPITYPE}/$mpitype->{$type}/g; + $str =~ s/{NCTYPE}/$nctype->{$type}/g; + $str =~ s/{CTYPE}/$ctype->{$type}/g; + $outstr.=$str; + } + }else{ + $outstr=$func; + } + + return $outstr; +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000000..88e74eafc0 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,49 @@ +cmake_minimum_required (VERSION 3.16.3) + +project (clm5-tsmp LANGUAGES C Fortran) + +# Set global compile features and definitions +if(UNIX AND NOT APPLE) + add_compile_definitions(LINUX) +endif() + +if(CMAKE_C_COMPILER_ID STREQUAL "GNU") + set(cdefs CPRGNU) + set(cflags -O -std=gnu99) +elseif(CMAKE_C_COMPILER_ID STREQUAL "Intel") + set(cdefs CPRINTEL) + set(cflags -qno-opt-dynamic-align -std=gnu99 -O2 "SHELL:-fp-model precise -debug minimal") +endif() + +if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU") + set(fdefs CPRGNU) + set(fflags -ffree-form -ffree-line-length-none -fconvert=big-endian -O) +elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") + set(fdefs CPRINTEL) + set(fflags -free -qno-opt-dynamic-align -ftz -traceback -O2 "SHELL:-convert big_endian -assume byterecl -assume realloc_lhs -fp-model source -debug minimal") +endif() + +add_compile_options("$<$:${fflags}>") +add_compile_options("$<$:${cflags}>") +add_compile_definitions("$<$:${fdefs}>") +add_compile_definitions($<$:${cdefs}>) + +add_subdirectory(externals) +add_subdirectory(csm_share) +add_subdirectory(clm5) +add_subdirectory(stub_comps) +add_subdirectory(datm) +add_subdirectory(mosart) +add_subdirectory(cesm) + +# Make sure that the required libraries are always found +# independent from LD_LIBRARY_PATH and the install location. +# https://gitlab.kitware.com/cmake/community/-/wikis/doc/cmake/RPATH-handling#always-full-rpath +set(CMAKE_SKIP_BUILD_RPATH FALSE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) +set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" isSystemDir) +if("${isSystemDir}" STREQUAL "-1") + set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") +endif("${isSystemDir}" STREQUAL "-1") \ No newline at end of file diff --git a/src/cesm/CMakeLists.txt b/src/cesm/CMakeLists.txt index d18de153ec..5b11d47ada 100644 --- a/src/cesm/CMakeLists.txt +++ b/src/cesm/CMakeLists.txt @@ -1,9 +1,38 @@ -list(APPEND drv_sources - component_type_mod.F90 - map_glc2lnd_mod.F90 - map_lnd2rof_irrig_mod.F90 - seq_map_mod.F90 - seq_map_type_mod.F90 - ) +project (cesm LANGUAGES Fortran) -sourcelist_to_parent(drv_sources) +add_executable(${PROJECT_NAME}) +set_target_properties(${PROJECT_NAME} PROPERTIES SUFFIX ".exe") + +target_sources(${PROJECT_NAME} + PRIVATE + cime_comp_mod.F90 + cime_driver.F90 + component_mod.F90 + component_type_mod.F90 + cplcomp_exchange_mod.F90 + map_glc2lnd_mod.F90 + map_lnd2glc_mod.F90 + map_lnd2rof_irrig_mod.F90 + mrg_mod.F90 + prep_aoflux_mod.F90 + prep_atm_mod.F90 + prep_glc_mod.F90 + prep_ice_mod.F90 + prep_lnd_mod.F90 + prep_ocn_mod.F90 + prep_rof_mod.F90 + prep_wav_mod.F90 + seq_diag_mct.F90 + seq_domain_mct.F90 + seq_flux_mct.F90 + seq_frac_mct.F90 + seq_hist_mod.F90 + seq_io_mod.F90 + seq_map_mod.F90 + seq_map_type_mod.F90 + seq_rest_mod.F90 + t_driver_timers_mod.F90 +) + +target_link_libraries(${PROJECT_NAME} PRIVATE clm datm mosart ocn wav ice glc esp csm_share) +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/clm5/CMakeLists.txt b/src/clm5/CMakeLists.txt new file mode 100644 index 0000000000..2127ebda58 --- /dev/null +++ b/src/clm5/CMakeLists.txt @@ -0,0 +1,319 @@ +project (clm LANGUAGES Fortran) + +add_library(${PROJECT_NAME} STATIC) + +target_compile_definitions(${PROJECT_NAME} PRIVATE NDEBUG) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +# Fortran template files transformed through genf90.pl +set(F90IN_FILES + dyn_subgrid/dynVarTimeInterpMod.F90.in + dyn_subgrid/dynVarMod.F90.in + dyn_subgrid/dynVarTimeUninterpMod.F90.in + init_interp/initInterp2dvar.F90.in + main/ncdio_pio.F90.in + utils/restUtilMod.F90.in + utils/array_utils.F90.in +) + +set(PREPROCESSED_F90_FILES "") +foreach(f90in_file IN LISTS F90IN_FILES) + get_filename_component(filename_noext ${f90in_file} NAME_WE) + set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${filename_noext}.F90") + add_custom_command( + OUTPUT ${outfile} + COMMAND ${GENF90_PATH}/genf90.pl ${CMAKE_CURRENT_SOURCE_DIR}/${f90in_file} > ${outfile} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${f90in_file} + ) + list(APPEND PREPROCESSED_F90_FILES ${outfile}) +endforeach() + +target_sources(${PROJECT_NAME} PRIVATE ${PREPROCESSED_F90_FILES}) +target_sources(${PROJECT_NAME} + PRIVATE + ${CMAKE_CURRENT_BINARY_DIR}/ncdio_pio.F90 + biogeochem/ch4FInundatedStreamType.F90 + biogeochem/ch4Mod.F90 + biogeochem/ch4varcon.F90 + biogeochem/CNAnnualUpdateMod.F90 + biogeochem/CNBalanceCheckMod.F90 + biogeochem/CNC14DecayMod.F90 + biogeochem/CNCIsoAtmTimeSeriesReadMod.F90 + biogeochem/CNCIsoFluxMod.F90 + biogeochem/CNCStateUpdate1Mod.F90 + biogeochem/CNCStateUpdate2Mod.F90 + biogeochem/CNCStateUpdate3Mod.F90 + biogeochem/CNDriverMod.F90 + biogeochem/CNDVDriverMod.F90 + biogeochem/CNDVEstablishmentMod.F90 + biogeochem/CNDVLightMod.F90 + biogeochem/CNDVType.F90 + biogeochem/CNFireBaseMod.F90 + biogeochem/CNFireEmissionsMod.F90 + biogeochem/CNFireFactoryMod.F90 + biogeochem/CNFireLi2014Mod.F90 + biogeochem/CNFireLi2016Mod.F90 + biogeochem/CNFireMethodMod.F90 + biogeochem/CNFireNoFireMod.F90 + biogeochem/CNFUNMod.F90 + biogeochem/CNGapMortalityMod.F90 + biogeochem/CNGRespMod.F90 + biogeochem/CNMRespMod.F90 + biogeochem/CNNDynamicsMod.F90 + biogeochem/CNNStateUpdate1Mod.F90 + biogeochem/CNNStateUpdate2Mod.F90 + biogeochem/CNNStateUpdate3Mod.F90 + biogeochem/CNPhenologyMod.F90 + biogeochem/CNPrecisionControlMod.F90 + biogeochem/CNProductsMod.F90 + biogeochem/CNRootDynMod.F90 + biogeochem/CNSharedParamsMod.F90 + biogeochem/CNSpeciesMod.F90 + biogeochem/CNVegCarbonFluxType.F90 + biogeochem/CNVegCarbonStateType.F90 + biogeochem/CNVegComputeSeedMod.F90 + biogeochem/CNVegetationFacade.F90 + biogeochem/CNVegNitrogenFluxType.F90 + biogeochem/CNVegNitrogenStateType.F90 + biogeochem/CNVegStateType.F90 + biogeochem/CNVegStructUpdateMod.F90 + biogeochem/CropType.F90 + biogeochem/DryDepVelocity.F90 + biogeochem/DUSTMod.F90 + biogeochem/dynCNDVMod.F90 + biogeochem/dynConsBiogeochemMod.F90 + biogeochem/dynHarvestMod.F90 + biogeochem/EDBGCDynMod.F90 + biogeochem/FireEmisFactorsMod.F90 + biogeochem/MEGANFactorsMod.F90 + biogeochem/NutrientCompetitionCLM45defaultMod.F90 + biogeochem/NutrientCompetitionFactoryMod.F90 + biogeochem/NutrientCompetitionFlexibleCNMod.F90 + biogeochem/NutrientCompetitionMethodMod.F90 + biogeochem/SatellitePhenologyMod.F90 + biogeochem/SpeciesBaseType.F90 + biogeochem/SpeciesIsotopeType.F90 + biogeochem/SpeciesNonIsotopeType.F90 + biogeochem/VOCEmissionMod.F90 + biogeophys/ActiveLayerMod.F90 + biogeophys/AerosolMod.F90 + biogeophys/BalanceCheckMod.F90 + biogeophys/BandDiagonalMod.F90 + biogeophys/BareGroundFluxesMod.F90 + biogeophys/CanopyFluxesMod.F90 + biogeophys/CanopyHydrologyMod.F90 + biogeophys/CanopyStateType.F90 + biogeophys/CanopyTemperatureMod.F90 + biogeophys/DaylengthMod.F90 + biogeophys/EnergyFluxType.F90 + biogeophys/FrictionVelocityMod.F90 + biogeophys/GlacierSurfaceMassBalanceMod.F90 + biogeophys/HumanIndexMod.F90 + biogeophys/HydrologyDrainageMod.F90 + biogeophys/HydrologyNoDrainageMod.F90 + biogeophys/IrrigationMod.F90 + biogeophys/LakeCon.F90 + biogeophys/LakeFluxesMod.F90 + biogeophys/LakeHydrologyMod.F90 + biogeophys/LakeStateType.F90 + biogeophys/LakeTemperatureMod.F90 + biogeophys/LunaMod.F90 + biogeophys/OzoneBaseMod.F90 + biogeophys/OzoneFactoryMod.F90 + biogeophys/OzoneMod.F90 + biogeophys/OzoneOffMod.F90 + biogeophys/PhotosynthesisMod.F90 + biogeophys/QSatMod.F90 + biogeophys/RootBiophysMod.F90 + biogeophys/SnowHydrologyMod.F90 + biogeophys/SnowSnicarMod.F90 + biogeophys/SoilFluxesMod.F90 + biogeophys/SoilHydrologyInitTimeConstMod.F90 + biogeophys/SoilHydrologyMod.F90 + biogeophys/SoilHydrologyType.F90 + biogeophys/SoilMoistStressMod.F90 + biogeophys/SoilMoistureStreamMod.F90 + biogeophys/SoilStateInitTimeConstMod.F90 + biogeophys/SoilStateType.F90 + biogeophys/SoilTemperatureMod.F90 + biogeophys/SoilWaterMovementMod.F90 + biogeophys/SoilWaterPlantSinkMod.F90 + biogeophys/SoilWaterRetentionCurveClappHornberg1978Mod.F90 + biogeophys/SoilWaterRetentionCurveFactoryMod.F90 + biogeophys/SoilWaterRetentionCurveMod.F90 + biogeophys/SoilWaterRetentionCurveVanGenuchten1980Mod.F90 + biogeophys/SolarAbsorbedType.F90 + biogeophys/SurfaceAlbedoMod.F90 + biogeophys/SurfaceAlbedoType.F90 + biogeophys/SurfaceRadiationMod.F90 + biogeophys/SurfaceResistanceMod.F90 + biogeophys/TemperatureType.F90 + biogeophys/TotalWaterAndHeatMod.F90 + biogeophys/TridiagonalMod.F90 + biogeophys/UrbanAlbedoMod.F90 + biogeophys/UrbanFluxesMod.F90 + biogeophys/UrbanParamsType.F90 + biogeophys/UrbanRadiationMod.F90 + biogeophys/UrbanTimeVarType.F90 + biogeophys/UrbBuildTempOleson2015Mod.F90 + biogeophys/WaterfluxType.F90 + biogeophys/WaterStateType.F90 + cpl/clm_cpl_indices.F90 + cpl/lnd_comp_mct.F90 + cpl/lnd_import_export.F90 + dyn_subgrid/dynColumnStateUpdaterMod.F90 + dyn_subgrid/dynColumnTemplateMod.F90 + dyn_subgrid/dynConsBiogeophysMod.F90 + dyn_subgrid/dyncropFileMod.F90 + dyn_subgrid/dynEDMod.F90 + dyn_subgrid/dynFileMod.F90 + dyn_subgrid/dynInitColumnsMod.F90 + dyn_subgrid/dynLandunitAreaMod.F90 + dyn_subgrid/dynPatchStateUpdaterMod.F90 + dyn_subgrid/dynpftFileMod.F90 + dyn_subgrid/dynPriorWeightsMod.F90 + dyn_subgrid/dynSubgridControlMod.F90 + dyn_subgrid/dynSubgridDriverMod.F90 + dyn_subgrid/dynTimeInfoMod.F90 + fates/biogeochem/EDCanopyStructureMod.F90 + fates/biogeochem/EDCohortDynamicsMod.F90 + fates/biogeochem/EDLoggingMortalityMod.F90 + fates/biogeochem/EDMortalityFunctionsMod.F90 + fates/biogeochem/EDPatchDynamicsMod.F90 + fates/biogeochem/EDPhysiologyMod.F90 + fates/biogeochem/FatesAllometryMod.F90 + fates/biogeochem/FatesLitterMod.F90 + fates/biogeophys/EDAccumulateFluxesMod.F90 + fates/biogeophys/EDBtranMod.F90 + fates/biogeophys/EDSurfaceAlbedoMod.F90 + fates/biogeophys/FatesBstressMod.F90 + fates/biogeophys/FatesPlantHydraulicsMod.F90 + fates/biogeophys/FatesPlantRespPhotosynthMod.F90 + fates/fire/SFMainMod.F90 + fates/fire/SFParamsMod.F90 + fates/main/ChecksBalancesMod.F90 + fates/main/EDInitMod.F90 + fates/main/EDMainMod.F90 + fates/main/EDParamsMod.F90 + fates/main/EDPftvarcon.F90 + fates/main/EDTypesMod.F90 + fates/main/FatesConstantsMod.F90 + fates/main/FatesGlobals.F90 + fates/main/FatesHistoryInterfaceMod.F90 + fates/main/FatesHistoryVariableType.F90 + fates/main/FatesHydraulicsMemMod.F90 + fates/main/FatesIntegratorsMod.F90 + fates/main/FatesInterfaceMod.F90 + fates/main/FatesInventoryInitMod.F90 + fates/main/FatesIODimensionsMod.F90 + fates/main/FatesIOVariableKindMod.F90 + fates/main/FatesParameterDerivedMod.F90 + fates/main/FatesParametersInterface.F90 + fates/main/FatesRestartInterfaceMod.F90 + fates/main/FatesRestartVariableType.F90 + fates/main/FatesSizeAgeTypeIndicesMod.F90 + fates/main/FatesSynchronizedParamsMod.F90 + fates/main/FatesUtilsMod.F90 + fates/parteh/PRTAllometricCarbonMod.F90 + fates/parteh/PRTGenericMod.F90 + fates/parteh/PRTLossFluxesMod.F90 + init_interp/initInterp1dData.F90 + init_interp/initInterpBounds.F90 + init_interp/initInterp.F90 + init_interp/initInterpMindist.F90 + init_interp/initInterpMultilevelBase.F90 + init_interp/initInterpMultilevelContainer.F90 + init_interp/initInterpMultilevelCopy.F90 + init_interp/initInterpMultilevelInterp.F90 + init_interp/initInterpMultilevelSnow.F90 + init_interp/initInterpMultilevelSplit.F90 + init_interp/initInterpUtils.F90 + main/abortutils.F90 + main/accumulMod.F90 + main/atm2lndMod.F90 + main/atm2lndType.F90 + main/clm_driver.F90 + main/clm_initializeMod.F90 + main/clm_instMod.F90 + main/clm_varcon.F90 + main/clm_varctl.F90 + main/clm_varpar.F90 + main/clm_varsur.F90 + main/ColumnType.F90 + main/column_varcon.F90 + main/controlMod.F90 + main/decompInitMod.F90 + main/decompMod.F90 + main/filterColMod.F90 + main/filterMod.F90 + main/FuncPedotransferMod.F90 + main/GetGlobalValuesMod.F90 + main/glc2lndMod.F90 + main/glcBehaviorMod.F90 + main/GridcellType.F90 + main/histFileMod.F90 + main/initGridCellsMod.F90 + main/init_hydrology.F90 + main/initSubgridMod.F90 + main/initVerticalMod.F90 + main/LandunitType.F90 + main/landunit_varcon.F90 + main/lnd2atmMod.F90 + main/lnd2atmType.F90 + main/lnd2glcMod.F90 + main/ncdio_utils.F90 + main/ndepStreamMod.F90 + main/organicFileMod.F90 + main/paramUtilMod.F90 + main/PatchType.F90 + main/pftconMod.F90 + main/readParamsMod.F90 + main/restFileMod.F90 + main/reweightMod.F90 + main/subgridAveMod.F90 + main/subgridMod.F90 + main/subgridRestMod.F90 + main/subgridWeightsMod.F90 + main/surfrdMod.F90 + main/surfrdUtilsMod.F90 + main/TopoMod.F90 + soilbiogeochem/SoilBiogeochemCarbonFluxType.F90 + soilbiogeochem/SoilBiogeochemCarbonStateType.F90 + soilbiogeochem/SoilBiogeochemCompetitionMod.F90 + soilbiogeochem/SoilBiogeochemDecompCascadeBGCMod.F90 + soilbiogeochem/SoilBiogeochemDecompCascadeCNMod.F90 + soilbiogeochem/SoilBiogeochemDecompCascadeConType.F90 + soilbiogeochem/SoilBiogeochemDecompMod.F90 + soilbiogeochem/SoilBiogeochemLittVertTranspMod.F90 + soilbiogeochem/SoilBiogeochemNitrifDenitrifMod.F90 + soilbiogeochem/SoilBiogeochemNitrogenFluxType.F90 + soilbiogeochem/SoilBiogeochemNitrogenStateType.F90 + soilbiogeochem/SoilBiogeochemNitrogenUptakeMod.F90 + soilbiogeochem/SoilBiogeochemNLeachingMod.F90 + soilbiogeochem/SoilBiogeochemNStateUpdate1Mod.F90 + soilbiogeochem/SoilBiogeochemPotentialMod.F90 + soilbiogeochem/SoilBiogeochemPrecisionControlMod.F90 + soilbiogeochem/SoilBiogeochemStateType.F90 + soilbiogeochem/SoilBiogeochemVerticalProfileMod.F90 + utils/AnnualFluxDribbler.F90 + utils/clmfates_interfaceMod.F90 + utils/clmfates_paraminterfaceMod.F90 + utils/clm_nlUtilsMod.F90 + utils/clm_time_manager.F90 + utils/clm_varorb.F90 + utils/domainMod.F90 + utils/fileutils.F90 + utils/getdatetime.F90 + utils/quadraticMod.F90 + utils/SimpleMathMod.F90 + utils/spmdGathScatMod.F90 + utils/spmdMod.F90 +) + +find_package(LAPACK) +if(LAPACK_FOUND) + target_link_libraries(${PROJECT_NAME} PRIVATE LAPACK::LAPACK) +endif() +target_link_libraries(${PROJECT_NAME} PRIVATE csm_share) +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/clm5/main/findHistFields.pl b/src/clm5/main/findHistFields.pl index e9952e869d..cf08a4f089 100644 --- a/src/clm5/main/findHistFields.pl +++ b/src/clm5/main/findHistFields.pl @@ -544,3 +544,550 @@ sub XML_Footer { &XML_Footer( $outfh ); close( $outfh ); + +#!/usr/bin/env perl +# +# This perl script reads in the histFldsMod.F90 file to find the total list of history +# fields that can be added for this model version, regardless of namelist options, or +# CPP processing. +# +use strict; +#use warnings; +#use diagnostics; + +use Cwd; +use English; +use Getopt::Long; +use IO::File; +use File::Glob ':glob'; + +# Set the directory that contains the CLM configuration scripts. If the command was +# issued using a relative or absolute path, that path is in $ProgDir. Otherwise assume +# the +# command was issued from the current working directory. + +(my $ProgName = $0) =~ s!(.*)/!!; # name of this script +my $ProgDir = $1; # name of directory containing this script -- may be a + # relative or absolute path, or null if the script + # is in + # the user's PATH +my $cmdline = "@ARGV"; # Command line arguments to script +my $cwd = getcwd(); # current working directory +my $cfgdir; # absolute pathname of directory that contains this script +my $nm = "${ProgName}::"; # name to use if script dies +if ($ProgDir) { + $cfgdir = $ProgDir; +} else { + $cfgdir = $cwd; +} +# The namelist definition file contains entries for all namelist variables that +# can be output by build-namelist. +my $nl_definition_file = "$cfgdir/../../bld/namelist_files/namelist_definition_clm4_5.xml"; +(-f "$nl_definition_file") or die <<"EOF"; +** $ProgName - Cannot find namelist definition file \"$nl_definition_file\" ** +EOF +print "Using namelist definition file $nl_definition_file\n"; + +# The Build::NamelistDefinition module provides utilities to get the list of +# megan compounds + +#The root directory to cesm utils Tools +my $cesm_tools = "$cfgdir/../../../../cime/utils/"; + +if ( ! -f "$cesm_tools/perl5lib/Build/NamelistDefinition.pm") { + $cesm_tools = "$cfgdir/../../cime/utils/"; + if ( ! -f "$cesm_tools/perl5lib/Build/NamelistDefinition.pm") { + die <<"EOF"; +** $ProgName - Cannot find perl module \"Build/NamelistDefinition.pm\" in directory + \"$cesm_tools/perl5lib\" ** +EOF + } +} +# Add $cfgdir/perl5lib to the list of paths that Perl searches for modules +my @dirs = ( $cfgdir, "$cesm_tools/perl5lib"); +unshift @INC, @dirs; +require Build::NamelistDefinition; +# Create a namelist definition object. This object provides a method for verifying that +# the +# output namelist variables are in the definition file, and are output in the correct +# namelist groups. +my $definition = Build::NamelistDefinition->new($nl_definition_file); + + +my $mxname = 0; +my $mxlongn = 0; +my %fields; +my $fldnamevar = "fieldname_var"; + +sub matchKeyword { +# +# Match a keyword +# + my $keyword = shift; + my $line = shift; + my $fh = shift; + + my $match = undef; + if ( $line =~ /$keyword/ ) { + if ( $line =~ /$keyword\s*=\s*['"]([^'"]+)(['"&])/ ) { + $match = $1; + if ( $2 eq "&" ) { + $line = <$fh>; + if ( $line =~ /&([^'"]+)['"]/ ) { + $match .= $1; + } + } + } elsif ( $line =~ /$keyword\s*=\s*&\s*$/ ) { + $line = <$fh>; + if ( $line =~ /^\s*['"]([^'"]+)['"]/ ) { + $match = $1; + } else { + die "ERROR: Trouble getting keyword string\n Line: $line"; + } + } else { + if ( $line =~ /fname\s*=\s*fieldname/ ) { + print STDERR "Found variable used for fieldname = $line\n"; + $match = $fldnamevar; + } elsif ( $line =~ /fname\s*=\s*trim\(fname\)/ ) { + $match = undef; + } elsif ( $line =~ /units\s*=\s*units/ ) { + $match = undef; + } elsif ( $line =~ /long_name\s*=\s*long_name/ ) { + $match = undef; + } elsif ( $line =~ /long_name\s*=\s*lname/ ) { + $match = undef; + } elsif ( $line =~ /long_name\s*=\s*longname/ ) { + print STDERR "Found variable used for longname = $line\n"; + $match = "longname_var"; + } else { + die "ERROR: Still have a match on $keyword\n Line: $line"; + } + } + } + return( $match ); +} + +sub getFieldInfo { +# +# Get field Information +# + my $fh = shift; + my $line = shift; + + my $fname = undef; + my $units = undef; + my $longn = undef; + my $endin = undef; + do { + if ( $line =~ /MEG_/ ) { + $line =~ s|'//'_'|_'|g; + $line =~ s|'//trim\(meg_cmp\%name\)|megancmpd'|gi; + if ( $line =~ /meg_cmp\%name/ ) { + die "ERROR: Still have meg_cmp in a line\n"; + } + } + if ( $line =~ /^(.+)this\%species\%hist_fname\(\s*['"]*([^'"]+)['"]\s*[,]*([^\)]*)\)(.*)/ ) { + my $suffix = ""; + my $beg = $1; + my $mid = $2; + my $suf = $3; + my $end = $4; + if ( $suf =~ /suffix\s*=\s*['"]([^'"]+)['"]/ ) { + $suffix=$1; + } + $line = "$beg 'ISO_${mid}ELEM_${suffix}' $end"; + } + $line -~ s|['"]\s*\/\/\s*this\%species\%get_species\(\)\s*\/\/\s*['"]|ELEM_|g; + if ( ! defined($fname) ) { + $fname = &matchKeyword( "fname", $line, $fh ); + } + if ( ! defined($units) ) { + $units = &matchKeyword( "units", $line, $fh ); + } + if ( ! defined($longn) ) { + $longn = &matchKeyword( "long_name", $line, $fh ); + } + if ( $line =~ /\)\s*$/ ) { + $endin = 1; + } + if ( ! defined($endin) ) { $line = <$fh>; } + + } until( (defined($fname) && defined($units) && defined($longn)) || + ! defined($line) || defined($endin) ); + if ( ! defined($fname) ) { + print "fname: $fname units: $units longn: $longn endin: $endin\n"; + die "ERROR: name undefined for field ending with: $line\n"; + } + return( $fname, $longn, $units ); +} + +sub setField { +# +# Set the field +# + my $name = shift; + my $longn = shift; + my $units = shift; + + if ( defined($name) && $name ne $fldnamevar ) { + if ( length($name) > $mxname ) { $mxname = length($name); } + if ( length($longn) > $mxlongn ) { $mxlongn = length($longn); } + my $len; + if ( length($longn) > 90 ) { + $len = 110; + } elsif ( length($longn) > 60 ) { + $len = 90; + } else { + $len = 60; + } + $fields{$name}{'field'} = sprintf( "%-${len}s\t(%s)", $longn, $units ); + $fields{$name}{'longn'} = $longn; + $fields{$name}{'units'} = $units; + } +} + +sub XML_Header { +# +# Write out header to history fields file +# + my $outfh = shift; + my $outfilename = shift; + + print STDERR " Write out header to history fields file to: $outfilename\n"; + my $svnurl = '$URL: https://svn-ccsm-models.cgd.ucar.edu/clm2/trunk_tags/clm4_0_40/models/lnd/clm/src/main/findHistFields.pl $'; + my $svnid = '$Id: findHistFields.pl 34757 2012-02-15 18:38:05Z erik $'; + print $outfh <<"EOF"; + + +\<\?xml-stylesheet type="text\/xsl" href="history_fields.xsl"\?\> + +\<\!-- + List of history file field names, long-names and units for all the fields output + by CLM. + SVN version information: + $svnurl + $svnid +--\> + +\ +EOF +} + +sub XML_Footer { +# +# Write out footer to history fields file +# + my $outfh = shift; + + print STDERR " Write out footer to history fields file\n"; + print $outfh "\n\n"; +} + +my $pwd = `pwd`; +chomp( $pwd ); +my @megcmpds = $definition->get_valid_values( "megan_cmpds", 'noquotes'=>1 ); +my @filenames = glob( "$pwd/*.F90" ); +push( @filenames, glob( "$pwd/../biogeochem/*.F90" ) ); +push( @filenames, glob( "$pwd/../biogeophys/*.F90" ) ); +push( @filenames, glob( "$pwd/../soilbiogeochem/*.F90" ) ); +push( @filenames, glob( "$pwd/../biogeophys/*.F90" ) ); +push( @filenames, glob( "$pwd/../fates/main/*.F90" ) ); + +# +# Loop over all files that have hist_addfld calls in them +# +foreach my $filename ( @filenames ) { + + if ( $filename =~ /histFileMod.F90$/ ) { + next; + } + my $fh = IO::File->new($filename, '<') or die "** $ProgName - can't open history Fields file: $filename\n"; + print( "Filename: $filename\n" ); + # + # Read in the list of fields from the source file + # + while (my $line = <$fh>) { + + # Comments + if ($line =~ /(.*)\!/) { + $line = $1; + } + #if ($line =~ /end subroutine/) { + # last; + #} + my $format = "\n\n"; + if ($line =~ /call\s*hist_addfld/i ) { + (my $name, my $longn, my $units) = &getFieldInfo( $fh, $line ); + if ( $name ne "MEG_megancmpd" && $name =~ /ISO_/ ) { + &setField( $name, $longn, $units ); + printf( , $format, $name, $units, $longn ); + } elsif ( $name =~ /ISO_/ ) { + foreach my $iso ( "C12", "C13", "C14", "N" ) { + my $elem = substr( $iso, 0, 1 ); + $name =~ s/ISO_/$iso/g; + $name =~ s/ELEM_/$elem/g; + $longn =~ s/ISO_/$elem/g; + $longn =~ s/ELEM_/$elem/g; + $units =~ s/ISO_/$iso/g; + $units =~ s/ELEM_/$elem/g; + &setField( $name, $longn, $units ); + printf( , $format, $name, $units, $longn ); + } + } elsif ( $name eq "MEG_megancmpd" ) { + foreach my $megcmpd ( @megcmpds ) { + my $name = "MEG_${megcmpd}"; + &setField( $name, $longn, $units ); + printf( , $format, $name, $units, $longn ); + } + } + } + } + close( $fh ); +} +print STDERR " mxname = $mxname\n"; +print STDERR " mxlongn = $mxlongn\n"; +my %pool_name = ( + L1=> { hist=>'LITR1', long=>'litter 1' }, + L2=> { hist=>'LITR2', long=>'litter 2' }, + L3=> { hist=>'LITR3', long=>'litter 3' }, + CWD=>{ hist=>'CWD', long=>'coarse woody debris' }, + S1=> { hist=>'SOIL1', long=>'soil 1' }, + S2=> { hist=>'SOIL2', long=>'soil 2' }, + S3=> { hist=>'SOIL3', long=>'soil 3' }, + S4=> { hist=>'SOIL4', long=>'soil 4' }, + atm=>{ hist=>'atmosphere', long=>'atmosphere' }, + ); + +my %vrt_suffix = ( C=>" C", "C_vr"=>" C (vertically resolved)", C_1m=>" C to 1 meter", + C_30cm=>" C to 30 cm", C_activelayer=>" C in active layer", + N=>" C", "N_vr"=>" N (vertically resolved)", N_1m=>" N to 1 meter", + N_30cm=>" N to 30 cm", N_activelayer=>" N in active layer", + ); +my %firelist = ( + C_TO_FIRE=>" C fire loss", C_TO_FIRE_vr=>" C fire loss", + N_TO_FIRE=>" N fire loss", N_TO_FIRE_vr=>" N fire loss", + ); +my %leechlist = ( + C_TO_LEACHING=>" C leaching loss", C_TNDNCY_VERT_TRANSPORT=>" C tendency due to vertical transport", + N_TO_LEACHING=>" N leaching loss", N_TNDNCY_VERT_TRANSPORT=>" N tendency due to vertical transport", + ); +# +# Add fields that are looped over +# +my $name, my $longn, my $units; +foreach my $pool ( keys(%pool_name) ) { + my $fname = $pool_name{$pool}{'hist'}; + foreach my $fld ( keys(%vrt_suffix) ) { + $name = $fname . $fld; + $longn = $pool_name{$pool}{'hist'} . $vrt_suffix{$fld}; + $units; + if ( $fld eq "C_vr" ) { + $units = "gC/m^3"; + } elsif ( $fld eq "N_vr" ) { + $units = "gN/m^3"; + } elsif ( $fld =~ /^N/) { + $units = "gN/m^2"; + } else { + $units = "gC/m^2"; + } + &setField( $name, $longn, $units ); + if ( $fld eq "C" || $fld eq "C_vr" ) { + foreach my $ciso ( "C13", "C14" ) { + $name = $ciso."_".$fname . $fld; + $longn = $ciso." ".$pool_name{$pool}{'long'} . $vrt_suffix{$fld}; + if ( $fld eq "C_vr" ) { + $units = "g${ciso}m^3"; + } else { + $units = "g${ciso}/m^2"; + } + &setField( $name, $longn, $units ); + } + } + if ( $fld =~ "C_1m" || $fld eq "C_30m" || $fld eq "C_activelayer" ) { + foreach my $ciso ( "C14" ) { + $name = $ciso."_".$fname . $fld; + $longn = $ciso." ".$pool_name{$pool}{'long'} . $vrt_suffix{$fld}; + $units = "g${ciso}/m^2"; + &setField( $name, $longn, $units ); + } + } + } + # Fire list + if ( $fname =~ /^CWD/ || $fname =~ /^LIT/ ) { + foreach my $fld ( keys(%firelist) ) { + $name = "M_".$fname . $fld; + $longn = $firelist{$fname}; + $units; + if ( $fld =~ /_vr$/ ) { + $units = "gC/m^3"; + } else { + $units = "gC/m^2"; + } + &setField( $name, $longn, $units ); + # Carbon isotopes (C13/C14) + if ( $fld =~ /^C/ ) { + foreach my $ciso ( "C13", "C14" ) { + $name = "${ciso}_M_".$fname . $fld; + $longn = $ciso.$firelist{$fname}; + if ( $fld =~ /_vr$/ ) { + $units = "g${ciso}/m^3"; + } else { + $units = "g${ciso}/m^2"; + } + &setField( $name, $longn, $units ); + } + } + } + } + # Potential loss coefficient + $name = "K_".$fname; + $longn = $pool_name{$pool}{'long'} . " potential loss coefficient"; + $units = "1/s"; + &setField( $name, $longn, $units ); + # + # Not CWD + # + if ( $fname !~ /^CWD/ ) { + foreach my $fld ( keys(%leechlist) ) { + $name = "M_".$fname . $fld; + $longn = $leechlist{$fname}; + my $elm; + if ( $fld =~ /^N/ ) { + $elm = "N"; + } else { + $elm = "C"; + } + if ( $fld =~ /VERT$/ ) { + $units = "g${elm}/m^3"; + } else { + $units = "g${elm}/m^2"; + } + &setField( $name, $longn, $units ); + } + } +} +my %translist = ( + # CN transitions + L1S1 =>{d=>"L1", r=>"S1"}, L2S2 =>{d=>"L2", r=>"S2"}, + L3S3 =>{d=>"L3", r=>"S3"}, S1S2 =>{d=>"S1", r=>"S2"}, + S2S3 =>{d=>"S2", r=>"S3"}, S3S4 =>{d=>"S3", r=>"S4"}, + S4 =>{d=>"S4", r=>"atm"}, + CWDL2=>{d=>"CWD", r=>"L2"}, CWDL3=>{d=>"CWD", r=>"L3"}, + # CENTURY transitions NOT already given above + L2S1 =>{d=>"L2", r=>"S1"}, L3S2 =>{d=>"L3", r=>"S2"}, + S1S3 =>{d=>"S1", r=>"S3"}, S2S1 =>{d=>"S2", r=>"S1"}, + S3S1 =>{d=>"S3", r=>"S1"}, + ); +# +# Transition list (NOT complete) +# +my $unitsvr; +foreach my $trans ( keys(%translist) ) { + my $donor = $translist{$trans}{'d'}; + my $rcvr = $translist{$trans}{'r'}; + if ( $trans ne "${donor}${rcvr}" && ($rcvr ne "atm" || $trans ne $donor) ) { + die "ERROR: Either bad transition name: $trans or bad donor: $donor or receiver: +$rcvr\n"; + } + # Carbon isotopes + foreach my $ciso ( "", "C13", "C14" ) { + if ( $ciso eq "" ) { + $units = "gC/m^2/s"; + $unitsvr = "gC/m^3/s"; + } else { + $units = "g${ciso}/m^2/s"; + $unitsvr = "g${ciso}/m^3/s"; + } + if ( $donor ne "CWD" ) { + my $ii = 0; + foreach my $trans2 ( keys(%translist) ) { + if ($donor eq $translist{$trans}{'d'} ) { $ii = $ii + 1; } + } + # HR + if ( $ii == 1 ) { + $name = $pool_name{$donor}{'hist'}."_HR"; + } else { + $name = $pool_name{$donor}{'hist'}."_HR_$rcvr"; + } + if ( $ciso ne "" ) { + $name = "${ciso}$name"; + } + $longn = 'Het. Resp. from '.$pool_name{$donor}{'long'}; + # vertically integrated fluxes + &setField( $name, $longn, $units ); + # vertically resolved version + &setField( "${name}_vr", $longn, $unitsvr ); + } + if ( $rcvr ne "atm" ) { + # transfer + $name = $pool_name{$donor}{'hist'}. "C_TO_" . + $pool_name{$rcvr}{'hist'}. "C"; + $longn = "decomp of " . $pool_name{$donor}{'long'}. " C to " . + $pool_name{$rcvr}{'long'}. " C"; + if ( $ciso ne "" ) { + $name = "${ciso}$name"; + } + # vertically integrated fluxes + &setField( $name, $longn, $units ); + # vertically resolved version + &setField( "${name}_vr", $longn, $unitsvr ); + } + } + + #-- mineralization/immobilization fluxes (none from CWD) + if ( $donor ne "CWD" ) { + $units = "gN/m^2/s"; + $unitsvr = "gN/m^3/s"; + if ( $rcvr ne "atm" ) { + $name = "SMINN_TO_".$pool_name{$rcvr}{'hist'}. "N_$donor"; + } else { + $name = $pool_name{$donor}{'hist'}. "N_TO_SMINN"; + } + $longn = "mineral N flux for decomp. of " . $pool_name{$donor}{'hist'}; + # vertically integrated fluxes + &setField( $name, $longn, $units ); + # vertically resolved fluxes + &setField( "${name}_vr", $longn, $unitsvr ); + # transfer fluxes + if ( $rcvr ne "atm" ) { + $name = $pool_name{$donor}{'hist'}. "N_TO_" . + $pool_name{$rcvr}{'hist'}. "N"; + $longn = "decomp of " . $pool_name{$donor}{'long'}. " N to " . + $pool_name{$rcvr}{'long'}. " N"; + # vertically integrated fluxes + &setField( $name, $longn, $units ); + # vertically resolved fluxes + &setField( "${name}_vr", $longn, $unitsvr ); + } + # NITRIF_DENITRIF + $name = "SMINN_TO_DENIT_$trans"; + $longn = "denitrification for decomp. of " . $pool_name{$donor}{'long'} . + "to ". $pool_name{$rcvr}{'hist'}; + &setField( $name, $longn, "gN/m^2" ); + # vertically resolved fluxes + &setField( "${name}_vr", $longn, "gN/m^3" ); + } +} + +# +# List the fields in a neatly ordered list +# And Output to an XML file +# +my $outfilename = "$pwd/../../bld/namelist_files/history_fields_clm4_5.xml"; + +my $outfh = IO::File->new($outfilename, '>') or die "** $ProgName - can't open output history Fields XML file: $outfilename\n"; +&XML_Header( $outfh, $outfilename ); +foreach my $name ( sort(keys(%fields)) ) { + my $len; + if ( length($name) > 20 ) { + $len = 40; + } else { + $len = 20; + } + printf( "%-${len}s = %s\n", $name, $fields{$name}{'field'} ); + printf( $outfh "\n\n", + $name, $fields{$name}{'units'}, $fields{$name}{'longn'} ); +} + +&XML_Footer( $outfh ); +close( $outfh ); diff --git a/src/csm_share/CMakeLists.txt b/src/csm_share/CMakeLists.txt new file mode 100644 index 0000000000..4e7c3c1aea --- /dev/null +++ b/src/csm_share/CMakeLists.txt @@ -0,0 +1,109 @@ +project (csm_share LANGUAGES C Fortran) + +add_library(${PROJECT_NAME} STATIC) + +target_compile_definitions(${PROJECT_NAME} + PUBLIC + NDEBUG + FORTRANUNDERSCORE + NUM_COMP_INST_ATM=1 + NUM_COMP_INST_LND=1 + NUM_COMP_INST_ICE=1 + NUM_COMP_INST_OCN=1 + NUM_COMP_INST_ROF=1 + NUM_COMP_INST_GLC=1 + NUM_COMP_INST_WAV=1 + NUM_COMP_INST_ESP=1 + HAVE_IEEE_ARITHMETIC +) + +target_include_directories(${PROJECT_NAME} PRIVATE esmf_wrf_timemgr) +target_include_directories(${PROJECT_NAME} PUBLIC include) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +set(F90IN_FILES util/shr_assert_mod.F90.in + util/shr_frz_mod.F90.in + util/shr_infnan_mod.F90.in) + +set(PREPROCESSED_F90_FILES "") +foreach(f90in_file IN LISTS F90IN_FILES) + get_filename_component(filename_noext ${f90in_file} NAME_WE) + set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${filename_noext}.F90") + add_custom_command( + OUTPUT ${outfile} + COMMAND ${GENF90_PATH}/genf90.pl ${CMAKE_CURRENT_SOURCE_DIR}/${f90in_file} > ${outfile} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${f90in_file} + ) + list(APPEND PREPROCESSED_F90_FILES ${outfile}) +endforeach() + +target_sources(${PROJECT_NAME} PRIVATE ${PREPROCESSED_F90_FILES}) +target_sources(${PROJECT_NAME} PRIVATE + esmf_wrf_timemgr/ESMF_AlarmClockMod.F90 + esmf_wrf_timemgr/ESMF_AlarmMod.F90 + esmf_wrf_timemgr/ESMF_BaseMod.F90 + esmf_wrf_timemgr/ESMF_BaseTimeMod.F90 + esmf_wrf_timemgr/ESMF_CalendarMod.F90 + esmf_wrf_timemgr/ESMF_ClockMod.F90 + esmf_wrf_timemgr/ESMF.F90 + esmf_wrf_timemgr/ESMF_FractionMod.F90 + esmf_wrf_timemgr/ESMF_ShrTimeMod.F90 + esmf_wrf_timemgr/ESMF_Stubs.F90 + esmf_wrf_timemgr/ESMF_TimeIntervalMod.F90 + esmf_wrf_timemgr/ESMF_TimeMod.F90 + esmf_wrf_timemgr/MeatMod.F90 + esmf_wrf_timemgr/wrf_error_fatal.F90 + esmf_wrf_timemgr/wrf_message.F90 + mct/glc_elevclass_mod.F90 + mct/seq_cdata_mod.F90 + mct/seq_comm_mct.F90 + mct/seq_drydep_mod.F90 + mct/seq_flds_mod.F90 + mct/seq_infodata_mod.F90 + mct/seq_io_read_mod.F90 + mct/seq_timemgr_mod.F90 + mct/shr_carma_mod.F90 + mct/shr_expr_parser_mod.F90 + mct/shr_fire_emis_mod.F90 + mct/shr_megan_mod.F90 + mct/shr_ndep_mod.F90 + streams/shr_dmodel_mod.F90 + streams/shr_strdata_mod.F90 + streams/shr_stream_mod.F90 + streams/shr_tInterp_mod.F90 + util/mct_mod.F90 + util/shr_abort_mod.F90 + util/shr_cal_mod.F90 + util/shr_const_mod.F90 + util/shr_file_mod.F90 + util/shr_flds_mod.F90 + util/shr_flux_mod.F90 + util/shr_kind_mod.F90 + util/shr_log_mod.F90 + util/shr_map_mod.F90 + util/shr_mct_mod.F90 + util/shr_mem_mod.F90 + util/shr_mpi_mod.F90 + util/shr_msg_mod.F90 + util/shr_ncread_mod.F90 + util/shr_nl_mod.F90 + util/shr_orb_mod.F90 + util/shr_pcdf_mod.F90 + util/shr_pio_mod.F90 + util/shr_precip_mod.F90 + util/shr_reprosum_mod.F90 + util/shr_reprosumx86.c + util/shr_scam_mod.F90 + util/shr_spfn_mod.F90 + util/shr_strconvert_mod.F90 + util/shr_string_mod.F90 + util/shr_sys_mod.F90 + util/shr_timer_mod.F90 + util/shr_vmath_mod.F90 + util/shr_wv_sat_mod.F90 + util/water_isotopes.F90 + util/water_types.F90 +) + +target_link_libraries(${PROJECT_NAME} PUBLIC pio mct gptl) +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/src/csm_share/esmf_wrf_timemgr/unittests/go.csh b/src/csm_share/esmf_wrf_timemgr/unittests/go.csh index 77641ffb35..d229313866 100644 --- a/src/csm_share/esmf_wrf_timemgr/unittests/go.csh +++ b/src/csm_share/esmf_wrf_timemgr/unittests/go.csh @@ -12,3 +12,18 @@ echo "diffs vs baseline = $nd" + +#!/bin/csh + +rm -f ./test +gmake +rm -f ./test.out +./test >& test.out + +tail -5 test.out +set nd = `diff test.out.base test.out | wc -l` + +echo "diffs vs baseline = $nd" + + + diff --git a/src/csm_share/include/dynamic_vector_procdef.inc b/src/csm_share/include/dynamic_vector_procdef.inc new file mode 100644 index 0000000000..788c74e31f --- /dev/null +++ b/src/csm_share/include/dynamic_vector_procdef.inc @@ -0,0 +1,587 @@ +! Type-bound procedures for a dynamic vector. + +#ifdef USE_PURE +#define PURE pure +#else +#define PURE +#endif + +! Construct an empty vector. +PURE function new_vector_default() result(new_vec) + ! Create an empty vector + type( VECTOR_NAME ) :: new_vec + + ! Currently, this does nothing. But some compilers may do weird things if + ! you don't "define" new_vec somehow, and clearing the vector is safe. + call new_vec%clear() + +end function new_vector_default + +! Construct a vector from another vector. +PURE function new_vector_copy(vec) result(new_vec) + ! Create a vector from a pre-existing array. + type( VECTOR_NAME ), intent(in) :: vec + type( VECTOR_NAME ) :: new_vec + + new_vec = vec + +end function new_vector_copy + +! Construct a vector from an array. +PURE function new_vector_array(array) result(new_vec) + ! Create a vector from a pre-existing array. + TYPE_NAME, intent(in) :: array(:) + type( VECTOR_NAME ) :: new_vec + + new_vec = array + +end function new_vector_array + +! Query if the vector is empty. +PURE function empty_vec(self) result(is_empty) + class( VECTOR_NAME ), intent(in) :: self + logical :: is_empty + + is_empty = (self%vec_size == 0) + +end function empty_vec + +! Get size of the vector. +PURE function size_vec(self) result(vec_size) + class( VECTOR_NAME ), intent(in) :: self + integer :: vec_size + + vec_size = self%vec_size + +end function size_vec + +! Get maximum size the vector can have. +PURE function max_size_vec(self) result(max_size) + class( VECTOR_NAME ), intent(in) :: self + integer :: max_size + + ! The only theoretical limitation that can be determined without a system + ! call is the maximum size of an integer. + max_size = huge(self%vec_size) + +end function max_size_vec + +! Query current memory capacity of vector. +PURE function capacity_vec(self) result(capacity) + class( VECTOR_NAME ), intent(in) :: self + integer :: capacity + + if (allocated(self%data)) then + capacity = size(self%data) + else + capacity = 0 + end if + +end function capacity_vec + +! Get one item based on an index. +PURE function get_single_vec(self, index) result(item) + class( VECTOR_NAME ), intent(in) :: self + integer, intent(in) :: index + TYPE_NAME, allocatable :: item + + if (index > self%vec_size .or. index < 1) then + THROW(OOBMsg("get", [1, self%vec_size], index)) + ! Purely to satisfy uninitialized data checks. + allocate(item) + return + end if + + allocate(item, source=self%data(index)) + +end function get_single_vec + +! Get items within a certain range. +PURE function get_range_vec(self, begin, end, stride) result(items) + class( VECTOR_NAME ), intent(in) :: self + integer, intent(in) :: begin + integer, intent(in) :: end + integer, intent(in), optional :: stride + + ! Have to use an allocatable, because we have to check if stride is + ! present before we know what the size should be. + TYPE_NAME, allocatable :: items(:) + + ! An oddity: since in Fortran function results must be "defined", we have + ! to allocate "items" to portably avoid a segfault and allow the user to + ! recover from an error. This is true regardless of what the function + ! result is assigned to. + if (end > self%vec_size) then + allocate(items(0)) + THROW(OOBMsg("get", [1, self%vec_size], end)) + return + end if + if (begin < 1) then + allocate(items(0)) + THROW(OOBMsg("get", [1, self%vec_size], begin)) + return + end if + + if (present(stride)) then + ! For strided access, the number of elements is the size over the + ! stride, but rounded up, rather than down as in typical integer + ! division. The shortcut for this is that (x-1)/y + 1 is the same as + ! x/y rounded up. + allocate(items((end-begin)/stride + 1)) + items = self%data(begin:end:stride) + else + allocate(items(end+1-begin)) + items = self%data(begin:end) + end if + +end function get_range_vec + +! Get an array containing a copy of the vector's elements. +! If array is not allocated, returns a size zero array. +PURE function get_array_vec(self) result(array) + class( VECTOR_NAME ), intent(in) :: self + TYPE_NAME :: array(self%vec_size) + + if (allocated(self%data)) then + array = self%data(:self%vec_size) + end if + +end function get_array_vec + +! Get first item in the array +PURE function front_vec(self) result(item) + class( VECTOR_NAME ), intent(in) :: self + TYPE_NAME :: item + + item = self%get(1) + +end function front_vec + +! Get last item in the array +PURE function back_vec(self) result(item) + class( VECTOR_NAME ), intent(in) :: self + TYPE_NAME :: item + + item = self%get(self%vec_size) + +end function back_vec + +! Declare the vector to have zero size. +! Does not change vector capacity. +PURE subroutine clear_vec(self) + class( VECTOR_NAME ), intent(inout) :: self + + call self%resize(0) + +end subroutine clear_vec + +! Declare the vector to have different size. +! Does not reduce vector capacity, but will enforce size <= capacity by +! growing array if necessary. +! Resizing to negative value is equivalent to resizing to 0. +PURE subroutine resize_vec(self, new_size, fill_value) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: new_size + TYPE_NAME, intent(in), optional :: fill_value + + integer :: request_capacity + + ! If not big enough, request capacity twice as big + ! as we have now (or 4 or 8 or... times, if necessary). + if (new_size > self%capacity()) then + request_capacity = max(self%capacity(),1) + + do while (request_capacity < new_size) + request_capacity = request_capacity * 2 + end do + + call self%reserve(request_capacity) + end if + + if (present(fill_value)) then + self%data((self%vec_size+1):new_size) = fill_value + end if + + self%vec_size = max(new_size,0) + +end subroutine resize_vec + +! Set one item based on an index. +PURE subroutine set_single_vec(self, item, index) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: item + integer, intent(in) :: index + + if (index > self%vec_size .or. index < 1) then + THROW(OOBMsg("set", [1, self%vec_size], index)) + return + end if + + self%data(index) = item + +end subroutine set_single_vec + +! Set range in array. +PURE subroutine set_range_vec(self, array, begin, end, stride) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: array(:) + integer, intent(in) :: begin + integer, intent(in) :: end + integer, intent(in), optional :: stride + + if (end > self%vec_size) then + THROW(OOBMsg("set", [1, self%vec_size], end)) + return + end if + if (begin < 1) then + THROW(OOBMsg("set", [1, self%vec_size], begin)) + return + end if + + if (present(stride)) then + self%data(begin:end:stride) = array + else + self%data(begin:end) = array + end if + +end subroutine set_range_vec + +! Set range in array with a fill value. +PURE subroutine set_range_fill_vec(self, fill_value, begin, end, stride) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: fill_value + integer, intent(in) :: begin + integer, intent(in) :: end + integer, intent(in), optional :: stride + + if (end > self%vec_size) then + THROW(OOBMsg("set", [1, self%vec_size], end)) + return + end if + if (begin < 1) then + THROW(OOBMsg("set", [1, self%vec_size], begin)) + return + end if + + if (present(stride)) then + self%data(begin:end:stride) = fill_value + else + self%data(begin:end) = fill_value + end if + +end subroutine set_range_fill_vec + +! Set array from an array. +PURE subroutine set_array_vec(self, array) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: array(:) + + if (size(array) /= self%vec_size) then + THROW("Input array is not the same size as the vector it sets.") + end if + + if (self%vec_size > 0) then + self%data(:self%vec_size) = array(:self%vec_size) + end if + +end subroutine set_array_vec + +! Set array from a fill value. +! Bounds-checking unnecessary; empty arrays are left empty. +PURE subroutine set_fill_vec(self, fill_value) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: fill_value + + if (allocated(self%data)) then + self%data(:self%vec_size) = fill_value + end if + +end subroutine set_fill_vec + +! Add new object as last element. +PURE subroutine push_back_vec(self, item) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: item + + call self%resize(self%vec_size+1) + + call self%set(item, self%vec_size) + +end subroutine push_back_vec + +! Remove last element. +PURE subroutine pop_back_vec(self) + class( VECTOR_NAME ), intent(inout) :: self + + if (self%empty()) then + THROW("Attempted to pop an element from an empty vector.") + end if + + call self%resize(self%vec_size-1) + +end subroutine pop_back_vec + +! Insert element +! Valid values are 1 to self%vec_size+1. +! Inserting at self%vec_size+1 is equivalent to push_back. +PURE subroutine insert_single_vec(self, index, item) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: index + TYPE_NAME, intent(in) :: item + + if (index > self%vec_size+1 .or. index < 1) then + THROW(OOBMsg("insert", [1, self%vec_size], index)) + return + end if + + call self%resize(self%vec_size+1) + + ! Move everything forward + self%data(index+1:self%vec_size) = & + self%data(index:self%vec_size-1) + + call self%set(item, index) + +end subroutine insert_single_vec + +! Insert array +PURE subroutine insert_array_vec(self, index, items) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: index + TYPE_NAME, intent(in) :: items(:) + + if (index > self%vec_size+1 .or. index < 1) then + THROW(OOBMsg("insert", [1, self%vec_size], index)) + return + end if + + call self%resize(self%vec_size+size(items)) + + ! Move everything forward + self%data(index+size(items):self%vec_size) = & + self%data(index:self%vec_size-size(items)) + + call self%set(items, index, index+size(items)-1) + +end subroutine insert_array_vec + +! Insert repeated value +PURE subroutine insert_repeat_vec(self, index, item, repeats) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: index + TYPE_NAME, intent(in) :: item + integer, intent(in) :: repeats + + if (index > self%vec_size+1 .or. index < 1) then + THROW(OOBMsg("insert", [1, self%vec_size], index)) + return + end if + + call self%resize(self%vec_size+repeats) + + ! Move everything forward + self%data(index+repeats:self%vec_size) = & + self%data(index:self%vec_size-repeats) + + call self%set(item, index, index+repeats-1) + +end subroutine insert_repeat_vec + +! Erase element +PURE subroutine erase_single_vec(self, index) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: index + + if (index > self%vec_size .or. index < 1) then + THROW(OOBMsg("erase", [1, self%vec_size], index)) + return + end if + + ! Move everything back + self%data(index:(self%vec_size-1)) = self%data((index+1):self%vec_size) + + call self%pop_back() + +end subroutine erase_single_vec + +! Erase "repeats" elements at index. +PURE subroutine erase_range_vec(self, begin, end) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: begin + integer, intent(in) :: end + + if (end > self%vec_size) then + THROW(OOBMsg("erase", [1, self%vec_size], end)) + return + end if + if (begin < 1) then + THROW(OOBMsg("erase", [1, self%vec_size], begin)) + return + end if + + ! Move everything back + self%data(begin:self%vec_size-end+begin-1) = & + self%data(end+1:self%vec_size) + + call self%resize(self%vec_size - end + begin-1) + +end subroutine erase_range_vec + +! Shrink vector to minimum size necessary to hold all elements. +PURE subroutine shrink_to_fit_vec(self) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, allocatable :: tmp_array(:) + + ! Don't do anything unless we have to. + if (self%vec_size < self%capacity()) then + ! If size is zero, just deallocate array. + if (self%vec_size == 0) then + if (allocated(self%data)) deallocate(self%data) + else + ! Allocate temporary at minimum size + allocate(tmp_array(self%vec_size)) + tmp_array = self%data(:self%vec_size) + + deallocate(self%data) + call move_alloc(tmp_array, self%data) + end if + end if + +end subroutine shrink_to_fit_vec + +! Reserve a certain size, if vector is not already that big. +PURE subroutine reserve_vec(self, capacity) + class( VECTOR_NAME ), intent(inout) :: self + integer, intent(in) :: capacity + + TYPE_NAME, allocatable :: tmp_array(:) + + ! Only do anything if we need to get bigger. + if (capacity > self%capacity()) then + + if (self%empty()) then + ! No data to copy + if (allocated(self%data)) deallocate(self%data) + allocate(self%data(capacity)) + else + ! Allocate new size + allocate(tmp_array(capacity)) + ! Copy data + tmp_array(:self%vec_size) = self%data(:self%vec_size) + + ! Replace array with new copy. + deallocate(self%data) + call move_alloc(tmp_array, self%data) + end if + end if + +end subroutine reserve_vec + +! Move allocatable array into self +! Note: Declaring self as intent(out) automatically empties the vector the +! moment we enter this procedure! +PURE subroutine move_in_vec(self, array) + class( VECTOR_NAME ), intent(out) :: self + TYPE_NAME, allocatable, intent(inout) :: array(:) + + if (allocated(array)) then + call move_alloc(array, self%data) + self%vec_size = size(self%data) + end if + +end subroutine move_in_vec + +! Move self into output allocatable array. +! For empty vector, do not allocate output. +PURE subroutine move_out_vec(self, array) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, allocatable, intent(out) :: array(:) + + call self%shrink_to_fit() + + if (.not. self%empty()) then + call move_alloc(self%data, array) + end if + + call self%clear() + +end subroutine move_out_vec + +! Efficient swapping (no de/reallocation) +PURE subroutine swap_vec(self, other) + class( VECTOR_NAME ), intent(inout) :: self + class( VECTOR_NAME ), intent(inout) :: other + + integer :: tmp_size + TYPE_NAME, allocatable :: tmp_array(:) + + ! The following order is designed to work even if self and other are the + ! same vector. + if (allocated(other%data)) then + call move_alloc(other%data, tmp_array) + end if + + if (allocated(self%data)) then +#ifndef CPRPGI + call move_alloc(self%data, other%data) +#else + ! The above should work, but a PGI bug forces us to copy and + ! deallocate. + allocate(other%data, source=self%data) + deallocate(self%data) +#endif + end if + + if (allocated(tmp_array)) then +#ifndef CPRPGI + call move_alloc(tmp_array, self%data) +#else + ! The above should work, but a PGI bug forces us to copy and + ! deallocate. + allocate(self%data, source=tmp_array) + deallocate(tmp_array) +#endif + end if + + tmp_size = other%vec_size + other%vec_size = self%vec_size + self%vec_size = tmp_size + +end subroutine swap_vec + +! Assign self from an array +PURE subroutine array_assign_vec(self, array) + class( VECTOR_NAME ), intent(inout) :: self + TYPE_NAME, intent(in) :: array(:) + + call self%resize(size(array)) + + call self%set(array) + +end subroutine array_assign_vec + +! Assign self from another vector. +! Copy-and-swap is used to ensure that at most one copy of the array is +! performed. +! This would allow assignment to self in other languages, but Fortran 2003 +! is vague about whether this should work, since "other" must be +! "intent(in)" for an assignment, and this routine would modify it if it is +! the same as "self". +! Use of the "target" attribute is intended to mitigate the risk of a +! problem, warning the compiler that the two objects may overlap with other +! variables. +PURE subroutine vector_assign_vec(self, other) + class( VECTOR_NAME ), intent(inout), target :: self + class( VECTOR_NAME ), intent(in), target :: other + + class( VECTOR_NAME ), allocatable :: temp + + allocate(temp, source=other) + + call self%swap(temp) + + deallocate(temp) + +end subroutine vector_assign_vec + +#undef PURE diff --git a/src/csm_share/include/dynamic_vector_typedef.inc b/src/csm_share/include/dynamic_vector_typedef.inc new file mode 100644 index 0000000000..d9cd1b3a2b --- /dev/null +++ b/src/csm_share/include/dynamic_vector_typedef.inc @@ -0,0 +1,266 @@ +! +! Clone of C++ standard library vectors +! +! This type is a wrapper for an allocatable array, which provides +! efficient utilities for dynamic array operations, such as appending new +! elements, truncation, and reserving/retaining memory independently from +! changes to the array's apparent size. +! +! Dynamic arrays allocate a somewhat larger buffer of contiguous memory +! (the "capacity") than is actually being used at any given time (the +! "size"). This allow elements to be efficiently added to one end, with the +! object automatically reallocating a new buffer as necessary whenever the +! current capacity is exhausted. The capacity increases geometrically, +! wasting O(N) space, but requiring only O(1) time (amortized) to add each +! element. +! +! One downside is that this wrapper class does not support many of +! Fortran's intrinsic array operations. For instance, if you have a +! vector of reals, and you want to take the sine, you have to either +! iterate in a loop (slow), or set the upper bound yourself (without the +! safety of bounds checking). The latter looks like this: +! +! x = sin(vec%data(:vec%size())) +! +! Because of this, it's probably preferable to use a standard array instead +! of a vector of reals for numerical work. +! +! Because this type uses an allocatable instead of a pointer, it should not +! cause a memory leak. However, deallocation can be forced by using "clear" +! followed by "shrink_to_fit", or by explicit deallocation of the data +! component. +! +! How to create a vector type: +! ---------------------------- +! +! Define VECTOR_NAME and TYPE_NAME in a module, then include this file +! to create the type. Include this file before "contains" in the module, +! and the "procdef" file afterward. +! +! There must be a function in scope called OOBMsg (or a function macro of +! this name). This must accept a string representing the operation, a size +! 2 integer array representing the bounds of the array, and an integer +! representing an index into the array. It should return a string +! representing an error message for out-of-bounds access. +! +! Finally, define the function macro THROW to an error handling mechanism. +! THROW accepts one argument, a string representing an error message. +! +! Some tips: +! ---------- +! +! - Do not directly use the "data" component, unless it's unavoidable to +! get decent efficiency. +! +! - The data is assumed to always have lower bound 1. +! +! - If you are finished with adding/removing elements, you can convert +! this type into a standard allocatable array with the "move_out" +! method. (You can do the reverse conversion cheaply with "move_in".) +! +! - Don't include these files twice in the same module, as this will cause +! name clashes. +! +! - Don't use this type if you need pointers into the array to remain +! valid as you add and remove elements. As with the C++ type, the array +! is often reallocated if you are adding elements, and this invalidates +! pointers into it. +! +! Advanced features: +! ------------------ +! +! - Define the macro "USE_PURE" if you need to mark all methods as pure. +! This effectively requires errors to be silent (because THROW cannot do +! anything useful if it has no side effects). +! +! Developer's notes: +! ------------------ +! +! 1) The main difference from the C++ types is that we use Fortran array +! indexing conventions: +! - Indexing starts at 1, not 0. +! - The last element of a range is included in the range. E.g. using +! "vec%erase(2,3)" erases two elements, not just one. +! - When an array size would be negative, it is treated as size 0. +! - When an operation's ending index is smaller than the beginning +! index, it is a no-op (unless a negative stride is provided). +! +! 2) We could have iterator types like in C++, but they don't really give +! you anything more than integer indices. Other types, like linked +! lists, will likely require companion iterator types. +! +! 3) For access with bounds-checking, C++ uses the "at" method to provide +! references to individual elements. To avoid working with pointers, and +! to provide an interface somewhat closer to Fortran array conventions, +! this type uses set/get methods instead. +! +! These methods are likely to produce extra copies, which may negatively +! impact performance compared to direct access of the underlying data. +! This is one reason why the data component is public, not private. +! +! 4) All vectors with vec_size = 0 are valid empty vectors, regardless of +! whether or not "data" is allocated, and regardless of its size. This +! slightly complicates some of the methods. However, it means that the +! user does not have to initialize vectors, or treat empty vectors +! differently depending on how they became empty. +! +! 5) Dynamic arrays have a time/space tradeoff parameter, which is the +! factor by which the array's capacity grows whenever it is +! automatically reallocated to hold more elements. In this code, the +! factor is 2, which is a common, simple, and reasonably fast choice. +! +! If there is too much wasted memory over a wide range of use cases, +! however, it may be reasonable to consider using 1.5 or even lower +! (with appropriate attention given to rounding issues). + +type VECTOR_NAME + + TYPE_NAME, allocatable :: data(:) + + integer, private :: vec_size = 0 + contains + + !------------------------ + ! Query functions + !------------------------ + + ! Test whether there are any elements present + procedure, pass(self) :: empty => empty_vec + ! Return current size + procedure, pass(self) :: vsize => size_vec + ! Estimate maximum possible size + procedure, pass(self) :: max_size => max_size_vec + ! Return maximum number of elements that can be held before the data + ! array will be reallocated to a larger size. + procedure, pass(self) :: capacity => capacity_vec + + !------------------------ + ! Retrieving data + !------------------------ + + ! Get the value of the element at a particular index + procedure, pass(self), private :: get_single_vec + ! Get an array of values of all the elements within a range + procedure, pass(self), private :: get_range_vec + ! Get a copy of all the data + procedure, pass(self), private :: get_array_vec + ! Generic for all of the above. + generic :: get => get_single_vec, get_range_vec, get_array_vec + + ! Get the value of the first element + procedure, pass(self) :: front => front_vec + ! Get the value of the last element + procedure, pass(self) :: back => back_vec + + !------------------------ + ! Modifying data + !------------------------ + + ! Reset the vector to size 0 (without changing capacity) + procedure, pass(self) :: clear => clear_vec + + ! Resize the vector (will not reduce capacity) + ! Resizing to a larger size than the capacity causes reallocation of the + ! data array. + procedure, pass(self) :: resize => resize_vec + + ! None of the "set" routines below will grow the array. Setting elements + ! past the end of the vector will result in an out-of-bounds error; use + ! "insert", "push_back", or explicit resizing to add elements. + + ! Set the element at a particular index + procedure, pass(self), private :: set_single_vec + ! Set the elements in a range from an array + procedure, pass(self), private :: set_range_vec + ! Fill all the elements in a range with a scalar value + procedure, pass(self), private :: set_range_fill_vec + ! Set the data to a copy of some array + procedure, pass(self), private :: set_array_vec + ! Fill the data will a scalar value + procedure, pass(self), private :: set_fill_vec + ! Generic for all of the above. + generic :: set => set_single_vec, set_range_vec, set_range_fill_vec, & + set_array_vec, set_fill_vec + + ! Add an element to the back of the vector + procedure, pass(self) :: push_back => push_back_vec + ! Remove the element at the back of the vector + procedure, pass(self) :: pop_back => pop_back_vec + + ! All of the insert routines add elements; the vector will be expanded + ! and data shuffled to ensure that this is non-destructive. For a vector + ! of size n, new elements can be inserted anywhere from 1 to n+1. + ! Inserting at point n+1 is equivalent to adding the new elements one- + ! by-one with push_back. + + ! Insert one element at a particular point + procedure, pass(self), private :: insert_single_vec + ! Insert all elements from an array at a particular point + procedure, pass(self), private :: insert_array_vec + ! Insert multiple copies of the same value at a particular point. + procedure, pass(self), private :: insert_repeat_vec + ! Generic for all of the above. + generic :: insert => insert_single_vec, insert_array_vec, insert_repeat_vec + + ! Erase the element at a particular point + procedure, pass(self), private :: erase_single_vec + ! Erase all the elements between two points (inclusive) + procedure, pass(self), private :: erase_range_vec + ! Generic for all of the above. + generic :: erase => erase_single_vec, erase_range_vec + + !------------------------ + ! Adjusting capacity + !------------------------ + + ! Shrink the vector's capacity to fit its size, releasing unneeded + ! memory + procedure, pass(self) :: shrink_to_fit => shrink_to_fit_vec + + ! Expand the vector to have at least as much capacity as requested + ! Mostly useful to avoid unnecessary reallocation when you know that the + ! data is unlikely to exceed some upper bound on its size. + procedure, pass(self) :: reserve => reserve_vec + + !------------------------ + ! Move operations + !------------------------ + + ! Convert an allocatable array into a dynamic vector + ! No copies or reallocations are performed, but afterward the array is + ! no longer allocated. + procedure, pass(self) :: move_in => move_in_vec + + ! Convert a dynamic vector to an allocatable array + ! An empty vector is converted to an unallocated array. A reallocation + ! and copy is often performed otherwise. Afterward the vector is empty. + procedure, pass(self) :: move_out => move_out_vec + + ! Swap the contents of this vector with another one + ! No copies or reallocations are performed. + procedure, pass(self) :: swap => swap_vec + + !------------------------ + ! Copy/assignment + !------------------------ + + ! Overwrite contents of this vector with those of an array + procedure, pass(self), private :: array_assign_vec + ! Overwrite contents of this vector with those of another vector + procedure, pass(self), private :: vector_assign_vec + generic :: assignment(=) => array_assign_vec, vector_assign_vec + +end type VECTOR_NAME + +!------------------------ +! Constructors +!------------------------ + +interface VECTOR_NAME + ! Construct empty vector + module procedure new_vector_default + ! Construct vector as a copy of another vector + module procedure new_vector_copy + ! Construct vector with contents from an array + module procedure new_vector_array +end interface diff --git a/src/csm_share/include/shr_assert.h b/src/csm_share/include/shr_assert.h new file mode 100644 index 0000000000..b09e0d1271 --- /dev/null +++ b/src/csm_share/include/shr_assert.h @@ -0,0 +1,22 @@ +#ifdef NDEBUG +#define SHR_ASSERT(assert, msg) +#define SHR_ASSERT_FL(assert, file, line) +#define SHR_ASSERT_MFL(assert, msg, file, line) +#define SHR_ASSERT_ALL(assert, msg) +#define SHR_ASSERT_ALL_FL(assert, file, line) +#define SHR_ASSERT_ALL_MFL(assert, msg, file, line) +#define SHR_ASSERT_ANY(assert, msg) +#define SHR_ASSERT_ANY_FL(assert, file, line) +#define SHR_ASSERT_ANY_MFL(assert, msg, file, line) +#else +#define SHR_ASSERT(assert, my_msg) call shr_assert(assert, msg=my_msg) +#define SHR_ASSERT_FL(assert, my_file, my_line) call shr_assert(assert, file=my_file, line=my_line) +#define SHR_ASSERT_MFL(assert, my_msg, my_file, my_line) call shr_assert(assert, msg=my_msg, file=my_file, line=my_line) +#define SHR_ASSERT_ALL(assert, my_msg) call shr_assert_all(assert, msg=my_msg) +#define SHR_ASSERT_ALL_FL(assert, my_file, my_line) call shr_assert_all(assert, file=my_file, line=my_line) +#define SHR_ASSERT_ALL_MFL(assert, my_msg, my_file, my_line) call shr_assert_all(assert, msg=my_msg, file=my_file, line=my_line) +#define SHR_ASSERT_ANY(assert, my_msg) call shr_assert_any(assert, msg=my_msg) +#define SHR_ASSERT_ANY_FL(assert, my_file, my_line) call shr_assert_any(assert, file=my_file, line=my_line) +#define SHR_ASSERT_ANY_MFL(assert, my_msg, my_file, my_line) call shr_assert_any(assert, msg=my_msg, file=my_file, line=my_line) +#endif +use shr_assert_mod diff --git a/src/datm/CMakeLists.txt b/src/datm/CMakeLists.txt new file mode 100644 index 0000000000..be7117e364 --- /dev/null +++ b/src/datm/CMakeLists.txt @@ -0,0 +1,15 @@ +project (datm LANGUAGES Fortran) + +add_library(${PROJECT_NAME} STATIC) + +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +target_sources(${PROJECT_NAME} + PRIVATE + datm_shr_mod.F90 + datm_comp_mod.F90 + atm_comp_mct.F90 +) +target_link_libraries(${PROJECT_NAME} PRIVATE csm_share) +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/externals/CMakeLists.txt b/src/externals/CMakeLists.txt new file mode 100644 index 0000000000..ce1ef9bac3 --- /dev/null +++ b/src/externals/CMakeLists.txt @@ -0,0 +1,62 @@ +cmake_minimum_required (VERSION 3.16.3) +project(externals) +include(ExternalProject) + +# =========== +# GPTL +# =========== +set(GPTL_BLD_DIR ${CMAKE_CURRENT_BINARY_DIR}/gptl) +file(MAKE_DIRECTORY ${GPTL_BLD_DIR}/include ${GPTL_BLD_DIR}/lib) +ExternalProject_Add(gptl_external + PREFIX gptl + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gptl + BUILD_IN_SOURCE FALSE + CONFIGURE_COMMAND echo "CPPDEFS := $(CPPDEFS) -DFORTRANUNDERSCORE" > ${GPTL_BLD_DIR}/Macros.make + BUILD_COMMAND make install -f ${CMAKE_CURRENT_SOURCE_DIR}/gptl/Makefile + MACFILE=${GPTL_BLD_DIR}/Macros.make + MPICC=${CMAKE_C_COMPILER} + MPIFC=${CMAKE_Fortran_COMPILER} + GPTL_DIR=${CMAKE_CURRENT_SOURCE_DIR}/gptl + SHAREDPATH=${GPTL_BLD_DIR} + INSTALL_COMMAND "" + BUILD_ALWAYS YES + BUILD_BYPRODUCTS ${GPTL_BLD_DIR}/lib/libgptl.a +) + +add_library(gptl INTERFACE IMPORTED GLOBAL) +target_include_directories(gptl INTERFACE ${GPTL_BLD_DIR}/include) +target_link_directories(gptl INTERFACE ${GPTL_BLD_DIR}/lib) +target_link_libraries(gptl INTERFACE libgptl.a) +add_dependencies(gptl gptl_external) +install (FILES ${GPTL_BLD_DIR}/lib/libgptl.a + TYPE LIB) + +# =========== +# MCT +# =========== +set(MCT_BLD_DIR ${CMAKE_CURRENT_BINARY_DIR}/mct) +file(MAKE_DIRECTORY ${MCT_BLD_DIR}/include ${MCT_BLD_DIR}/lib) +ExternalProject_Add(mct_external + PREFIX mct + SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/mct + BUILD_IN_SOURCE TRUE + CONFIGURE_COMMAND ./configure + --prefix=${MCT_BLD_DIR} + --exec-prefix=${MCT_BLD_DIR} + CC=${CMAKE_C_COMPILER} + FC=${CMAKE_Fortran_COMPILER} + BUILD_ALWAYS YES + BUILD_BYPRODUCTS ${GPTL_BLD_DIR}/lib/libmct.a ${GPTL_BLD_DIR}/lib/libmpeu.a +) +add_library(mct INTERFACE IMPORTED GLOBAL) +target_include_directories(mct INTERFACE ${MCT_BLD_DIR}/include) +target_link_directories(mct INTERFACE ${MCT_BLD_DIR}/lib) +target_link_libraries(mct INTERFACE libmct.a libmpeu.a) +add_dependencies(mct mct_external) +install (FILES ${MCT_BLD_DIR}/lib/libmct.a ${MCT_BLD_DIR}/lib/libmpeu.a + TYPE LIB) + +# =========== +# PIO +# =========== +add_subdirectory(pio1) diff --git a/src/externals/pio1/CMakeLists.txt b/src/externals/pio1/CMakeLists.txt index facdbaceb2..d942f80036 100644 --- a/src/externals/pio1/CMakeLists.txt +++ b/src/externals/pio1/CMakeLists.txt @@ -2,10 +2,10 @@ IF( NOT GENF90_PATH) SET (GENF90_PATH ${CMAKE_CURRENT_SOURCE_DIR}/bin) ENDIF() -PROJECT(PIO C Fortran) -ENABLE_LANGUAGE(Fortran) -#INCLUDE(FortranCInterface) -CMAKE_MINIMUM_REQUIRED(VERSION 2.8.5) +PROJECT(pio C Fortran) +ADD_LIBRARY(${PROJECT_NAME} STATIC) + + IF (USER_CMAKE_MODULE_PATH) SET (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${USER_CMAKE_MODULE_PATH}) ELSE() @@ -15,8 +15,8 @@ find_file( TESTFILE NAMES TryCSizeOf.f90 PATHS ${CMAKE_MODULE_PATH} NO_DEFAULT_P get_filename_component( TESTFILEPATH ${TESTFILE} PATH) -SET(pio_include_dirs_ ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) -SET(PIO_LIB_DIR ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} CACHE STRING "") +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) #SET(bld_PIO_DEFINITIONS) @@ -25,7 +25,7 @@ TRY_COMPILE(WITH_CSIZEOF ${CMAKE_CURRENT_BINARY_DIR}/tryCompileCSIZEOF #MESSAGE(STATUS "c_sizeof test ${WITH_CSIZEOF}") IF(${WITH_CSIZEOF} STREQUAL FALSE) MESSAGE(STATUS "Fortran compiler does not support c_sizeof function") - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DNO_C_SIZEOF) + target_compile_definitions(${PROJECT_NAME} PRIVATE NO_C_SIZEOF) endif() # Netcdf is required @@ -33,8 +33,8 @@ endif() FIND_PACKAGE(NetCDF "4.3.3" COMPONENTS C Fortran) IF (${NetCDF_Fortran_FOUND}) MESSAGE("Building PIO with netcdf support ") - SET(pio_include_dirs_ ${pio_include_dirs_} ${NetCDF_Fortran_INCLUDE_DIR}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} ${NetCDF_Fortran_DEFINITIONS}) + target_include_directories(${PROJECT_NAME} PUBLIC ${NetCDF_Fortran_INCLUDE_DIR}) + target_compile_definitions(${PROJECT_NAME} PRIVATE ${NetCDF_Fortran_DEFINITIONS}) ELSE() MESSAGE("Building PIO without netcdf support ${NetCDF_C_FOUND} ${NetCDF_Fortran_FOUND}") ENDIF () @@ -65,27 +65,26 @@ ENDIF() IF (PIO_FILESYSTEM_HINTS STREQUAL "lustre") MESSAGE(STATUS "PIO using lustre filesystem hints") - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DPIO_LUSTRE_HINTS) + target_compile_definitions(${PROJECT_NAME} PRIVATE PIO_LUSTRE_HINTS) ELSEIF(PIO_FILESYSTEM_HINTS STREQUAL "gpfs") MESSAGE(STATUS "PIO using gpfs filesystem hints") - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DPIO_GPFS_HINTS) + target_compile_definitions(${PROJECT_NAME} PRIVATE PIO_GPFS_HINTS) ELSEIF(NOT "${PIO_FILESYSTEM_HINTS}" STREQUAL "") MESSAGE(WARNING "${PIO_FILESYSTEM_HINTS} not valid option for PIO_FILESYSTEM_HINTS; use gpfs or lustre.") ENDIF() IF(NetCDF_Fortran_FOUND) - SET(pio_include_dirs_ ${pio_include_dirs_} ${NetCDF_Fortran_INCLUDE_DIR}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NETCDF ${NetCDF_Fortran_DEFINITIONS}) + target_compile_definitions(${PROJECT_NAME} PUBLIC _NETCDF) if (${NetCDF_C_HAS_PARALLEL}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NETCDF4) + target_compile_definitions(${PROJECT_NAME} PUBLIC _NETCDF4) ENDIF() ELSE() - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NONETCDF) + target_compile_definitions(${PROJECT_NAME} PUBLIC _NONETCDF) ENDIF() IF(PnetCDF_Fortran_FOUND) - SET(pio_include_dirs_ ${pio_include_dirs_} ${PnetCDF_Fortran_INCLUDE_DIRS}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_PNETCDF) + target_include_directories(${PROJECT_NAME} PUBLIC ${PnetCDF_Fortran_INCLUDE_DIRS}) + target_compile_definitions(${PROJECT_NAME} PUBLIC _PNETCDF) ELSE() - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NOPNETCDF) + target_compile_definitions(${PROJECT_NAME} PUBLIC _NOPNETCDF) ENDIF() OPTION(PIO_USE_MPIIO "Enable support for MPI-IO (default auto detect)" ON) @@ -100,16 +99,16 @@ IF (PIO_USE_MPIIO) ENDIF() ENDIF() IF (${PIO_USE_MPIIO}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DUSEMPIIO) + target_compile_definitions(${PROJECT_NAME} PUBLIC USEMPIIO) ENDIF() -SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NOUSEMCT) +target_compile_definitions(${PROJECT_NAME} PRIVATE _NOUSEMCT) OPTION(PIO_USE_BOX "" ON) if(PIO_USE_BOX) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_USEBOX) + target_compile_definitions(${PROJECT_NAME} PRIVATE _USEBOX) else() - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -D_NOUSEBOX) + target_compile_definitions(${PROJECT_NAME} PRIVATE _NOUSEBOX) endif() OPTION(PIO_USE_MPIMOD "Use Fortran MPI module (default auto detect)" ON) @@ -125,23 +124,17 @@ IF (PIO_USE_MPIMOD) ENDIF() IF (NOT ${PIO_USE_MPIMOD}) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DNO_MPIMOD) + target_compile_definitions(${PROJECT_NAME} PRIVATE NO_MPIMOD) ENDIF() OPTION(PIO_BUILD_TIMING "" OFF) if(${PIO_BUILD_TIMING}) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../timing) - SET(bld_PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} -DTIMING -I ${CMAKE_CURRENT_BINARY_DIR}/timing) + target_compile_definitions(${PROJECT_NAME} PRIVATE TIMING -I ${CMAKE_CURRENT_BINARY_DIR}/timing) ADD_SUBDIRECTORY(../timing timing) endif() endif() -SET(PIO_DEFINITIONS ${bld_PIO_DEFINITIONS} CACHE STRING "") -ADD_DEFINITIONS(${PIO_DEFINITIONS}) - -SET(PIO_INCLUDE_DIRS ${pio_include_dirs_} CACHE STRING "") -INCLUDE_DIRECTORIES(${PIO_INCLUDE_DIRS}) - SET(SRCS_C topology.c) SET(SRCS_F90 pio.F90 pio_kinds.F90 nf_mod.F90 ionf_mod.F90 pio_types.F90 @@ -174,11 +167,17 @@ if("${CMAKE_Fortran_COMPILER_ID}" STREQUAL "GNU") SET(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -ffree-line-length-none") endif() -ADD_LIBRARY(pio ${SRCS_F90} ${SRCS_C} ${TEMPSRCF90}) -TARGET_LINK_LIBRARIES(pio ${PnetCDF_Fortran_LIBRARIES}) -TARGET_LINK_LIBRARIES(pio ${NetCDF_Fortran_LIBRARIES}) -TARGET_LINK_LIBRARIES(pio ${ADDITIONAL_LIBS}) - +target_sources(${PROJECT_NAME} PRIVATE ${SRCS_F90} ${SRCS_C} ${TEMPSRCF90}) +if(NetCDF_Fortran_FOUND) + add_library(NetCDF::NetCDF UNKNOWN IMPORTED) + set_target_properties(NetCDF::NetCDF PROPERTIES IMPORTED_LOCATION "${NetCDF_Fortran_LIBRARY}") + target_link_libraries(${PROJECT_NAME} PUBLIC NetCDF::NetCDF) +endif() +if(PnetCDF_Fortran_FOUND) + add_library(PnetCDF::PnetCDF UNKNOWN IMPORTED) + set_target_properties(PnetCDF::PnetCDF PROPERTIES IMPORTED_LOCATION "${PnetCDF_Fortran_LIBRARY}") + target_link_libraries(${PROJECT_NAME} PUBLIC PnetCDF::PnetCDF) +endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../unittests) ADD_SUBDIRECTORY(../unittests unittests) endif() @@ -186,4 +185,4 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../testpio) ADD_SUBDIRECTORY(../testpio testpio) endif() - +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/mosart/CMakeLists.txt b/src/mosart/CMakeLists.txt new file mode 100644 index 0000000000..013d23dda0 --- /dev/null +++ b/src/mosart/CMakeLists.txt @@ -0,0 +1,26 @@ +project (mosart LANGUAGES Fortran) + +add_library(${PROJECT_NAME} STATIC) + +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) +target_link_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + +target_sources(${PROJECT_NAME} PRIVATE + src/cpl/rof_cpl_indices.F90 + src/cpl/rof_comp_mct.F90 + src/riverroute/RtmIO.F90 + src/riverroute/MOSART_physics_mod.F90 + src/riverroute/RtmRestFile.F90 + src/riverroute/RtmMod.F90 + src/riverroute/RtmDateTime.F90 + src/riverroute/RtmFileUtils.F90 + src/riverroute/RtmVar.F90 + src/riverroute/RtmHistFile.F90 + src/riverroute/RtmSpmd.F90 + src/riverroute/RtmHistFlds.F90 + src/riverroute/RunoffMod.F90 + src/riverroute/RtmTimeManager.F90 +) + +target_link_libraries(${PROJECT_NAME} PRIVATE csm_share) +install (TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) \ No newline at end of file diff --git a/src/stub_comps/CMakeLists.txt b/src/stub_comps/CMakeLists.txt new file mode 100644 index 0000000000..903e3f4ac8 --- /dev/null +++ b/src/stub_comps/CMakeLists.txt @@ -0,0 +1,20 @@ +project (stub_comps LANGUAGES Fortran) + +foreach(component glc wav ice esp ocn) + add_library(${component} STATIC) + target_include_directories(${component} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + target_link_directories(${component} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) + if(${component} STREQUAL glc) + target_sources(glc PRIVATE glc_comp_mct.F90) + elseif(${component} STREQUAL wav) + target_sources(wav PRIVATE wav_comp_mct.F90) + elseif(${component} STREQUAL ice) + target_sources(ice PRIVATE ice_comp_mct.F90) + elseif(${component} STREQUAL esp) + target_sources(esp PRIVATE esp_comp_mct.F90) + elseif(${component} STREQUAL ocn) + target_sources(ocn PRIVATE ocn_comp_mct.F90) + endif() + target_link_libraries(${component} PRIVATE csm_share) + install (TARGETS ${component} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endforeach(component) \ No newline at end of file