-
Notifications
You must be signed in to change notification settings - Fork 360
YAKL and SAMXX CIME and CMake Integration Notes
Here, we work with files in E3SM/components/eam/cime_config
and E3SM/components/eam/bld
.
To add samxx to CIME and the CAM configure, we added the following to cime_config/config_componenet.xml
in the <entry id="CAM_CONFIG_OPTS">
and <values modifier='additive'>
sections:
<value compset="_EAM%MMFXX">-crm samxx</value>
Then, in cime_config/config_componenet.xml
, we added the following in the <description>
section:
<desc compset="_EAM%MMFXX">E3SM-MMF (C++) with 64x1km CRM, RRTMGP radiation, 1-mom micro, prescribed aerosol</desc>
These tell CIME what to add to the CAM configure line based on having MMFXX
in the compset string. Next, we define alises and compset strings in cime_config/config_compsets.xml
with the following:
<compset>
<alias>F-MMFXX-AQP1</alias>
<lname>2000_EAM%MMFXX-AQUA_SLND_SICE_DOCN%AQP1_SROF_SGLC_SWAV</lname>
</compset>
<compset>
<alias>F-MMFXX-RCEMIP</alias>
<lname>2000_EAM%MMFXX-RCE_SLND_SICE_DOCN%AQPCONST_SROF_SGLC_SWAV</lname>
</compset>
<compset>
<alias>F-MMFXX</alias>
<lname>2000_EAM%MMFXX_ELM%SP_CICE%PRES_DOCN%DOM_SROF_SGLC_SWAV</lname>
</compset>
Finally, we work in the E3SM/components/eam/bld/configure
script to specify what the samxx parameter passed to the configure script does to the CPP definitions and the source directories automatically GLOB
'd into the EAM CMake scripts. First, we add a definition for the crm
XML value and its valid parameters in E3SM/components/eam/bld/config_files/definition.xml
:
<entry id="crm" valid_values="sam,samomp,samxx" value="sam">
MMF CRM model
</entry>
Next, we add documentation to the CAM configure command: -crm <model> CRM model [sam | samomp | samxx]
. We then pass a CPP definition if -crm samxx
is passed to the configure command with:
if (defined $opts{'use_MMF'}) {
...
if ($crm eq 'sam') {
$cfg_cppdefs .= " -DMMF_SAM "
} elsif ($crm eq 'samomp') {
$cfg_cppdefs .= " -DMMF_SAMOMP"
} elsif ($crm eq 'samxx') {
$cfg_cppdefs .= " -DMMF_SAMXX "
}
...
}
Typically there are source directories to add to the CAM CMake GLOB
commands to search for source files, but since samxx does it's own custom CMake build, this is skipped, i.e.:
} elsif ($crm eq 'samxx') {
#####################################################
## -crm samxx will add_subdirectory in CMake rather
## than GLOBing.
#####################################################
}
The E3SM CMake files are located in E3SM/components
and E3SM/components/cmake
.
The USE_CUDA
variable needs to be visible at the highest level CMakeLists.txt
, so it has to be specified through CIME using the Macros.cmake
file in E3SM/cime_config/machines/config_compilers.xml
. Similarly, the CUDA_FLAGS
variable is compiler dependent and therefore will have to come from the same source through the same route into CMake.
To make these variables available to add to that file, however, they had to first be added to the XML schema in cime/config/xml_schemas/config_compilers_v2.xsd
. It was added in the <xs:group name="compilerVars">
group as a "choice" as follows:
<xs:element name="CUDA_FLAGS" type="flagsVar"/>
<xs:element name="USE_CUDA" type="upperBoolean"/>
Then they are added to appropriate machine, compiler configurations in config_compilers.xml
. An example is below for Summit with GNU + CUDA:
<CUDA_FLAGS>
<append> -O3 -arch sm_70 --use_fast_math </append>
</CUDA_FLAGS>
<USE_CUDA>TRUE</USE_CUDA>
Now, when CMake starts its top-level CMakeLists.txt
for E3SM, it sees these things set in the local Macros.cmake
. This still isn't quite enough, though because the main CMakeLists.txt
only looks at the Macros.cmake
file in a local setting inside a CMake function called set_compilers_e3sm
. To make it visible to the overall project, you have to set it in parent scope inside that scope as follows:
# USE_CUDA is set through Macros.cmake / config_compilers.xml
# If it exists, then set parent's scope to true; otherwise to false
if (USE_CUDA)
set(USE_CUDA TRUE PARENT_SCOPE)
else()
set(USE_CUDA FALSE PARENT_SCOPE)
endif()
Finally, we use this at the top level to turn on the CUDA language. CMake doesn't like doing this at a lower scope, so it needs to be done at the beginning of the project declaration:
project(E3SM C CXX Fortran)
if(USE_CUDA)
enable_language(CUDA)
endif()
Go to E3SM/components/cmake/common_setup.cmake
, and add new variables there. Use existing code to get the feel for where to test for adding a given variable. CAM often hooks into CIME through the ${CAM_CONFIG_OPTS}
variable that comes from the CAM configure command. USE_YAKL
and USE_SAMXX
are created with the code below in cmake/build_model.cmake
# Look for -crm samxx in the CAM_CONFIG_OPTS CIME variable
# If it's found, then enable USE_SAMXX
string(FIND "${CAM_CONFIG_OPTS}" "-crm samxx" HAS_SAMXX)
if (NOT HAS_SAMXX EQUAL -1)
# The following is for the SAMXX code:
set(USE_SAMXX TRUE)
endif()
# If samxx is being used, then YAKL must be used as well
set(USE_YAKL ${USE_SAMXX})
# If YAKL is being used, then we need to enable USE_CXX
if (${USE_YAKL})
set(USE_CXX TRUE)
endif()
We then add the YAKL and SAMXX builds into the cmake/build_model.cmake
file inside if (COMP_NAME STREQUAL "eam")
with the following code:
if (COMP_NAME STREQUAL "eam")
...
# If YAKL is needed, then set YAKL CMake vars
if (USE_YAKL)
# YAKL_ARCH can be CUDA, HIP, SYCL, OPENMP45, or empty
# USE_CUDA is set through Macros.cmake / config_compilers.xml
if (USE_CUDA)
set(YAKL_ARCH "CUDA")
# CUDA_FLAGS is set through Macros.cmake / config_compilers.xml
set(YAKL_CUDA_FLAGS "${CPPDEFS} ${CUDA_FLAGS}")
else()
# For CPU C++ compilers duplicate flags are fine, the last ones win typically
set(YAKL_CXX_FLAGS "${CPPDEFS} ${CXXFLAGS}")
set(YAKL_ARCH "")
endif()
message(STATUS "Building YAKL")
# Build YAKL as a static library
# YAKL_HOME is YAKL's source directlry
set(YAKL_HOME ${CMAKE_CURRENT_SOURCE_DIR}/../../../externals/YAKL)
# YAKL_BIN is where we're placing the YAKL library
set(YAKL_BIN ${CMAKE_CURRENT_BINARY_DIR}/yakl)
# Build the YAKL static library
add_subdirectory(${YAKL_HOME} ${YAKL_BIN})
# Add the YAKL bin directory, mainly due to a Fortran module if it's needed
include_directories(${YAKL_BIN})
endif()
# if samxx is needed, build samxx as a static library
if (USE_SAMXX)
message(STATUS "Building SAMXX")
# SAMXX_HOME is where the samxx source code lives
set(SAMXX_HOME ${CMAKE_CURRENT_SOURCE_DIR}/../../eam/src/physics/crm/samxx)
# SAMXX_BIN is where the samxx library will live
set(SAMXX_BIN ${CMAKE_CURRENT_BINARY_DIR}/samxx)
# Build the static samxx library
add_subdirectory(${SAMXX_HOME} ${SAMXX_BIN})
# Add samxx F90 files to the main E3SM build
set(SOURCES ${SOURCES} cmake/atm/../../eam/src/physics/crm/samxx/cpp_interface_mod.F90
cmake/atm/../../eam/src/physics/crm/samxx/params.F90
cmake/atm/../../eam/src/physics/crm/crm_ecpp_output_module.F90 )
endif()
endif()
From here, each component (YAKL and SAMXX) can fend for itself with its own CMakeLists.txt
, which will build a library. Finally, we link the library into the EAM component's library with the following code near the end of cmake/build_model.cmake
:
if (COMP_NAME STREQUAL "eam")
if (USE_YAKL)
target_link_libraries(${TARGET_NAME} PRIVATE yakl)
endif()
if (USE_SAMXX)
target_link_libraries(${TARGET_NAME} PRIVATE samxx)
endif()
endif()
The samxx CMake subdirectory has the following CMakeLists.txt
:
###############################################################################
## This assumes you have already run add_subdirectory(${YAKL_HOME} ${YAKL_BIN})
## It also assumes you've enabled CXX and C as languages (and CUDA if used)
###############################################################################
file(GLOB F90_SRC cpp_interface_mod.F90 fft.F90 params.F90 fftpack5.F90 fftpack5_1d.F90)
file(GLOB CXX_SRC *.cpp)
file(GLOB SAMXX_SRC ${CXX_SRC} ${F90_SRC})
# samxx will be a static library
add_library(samxx STATIC ${SAMXX_SRC})
if ("${YAKL_ARCH}" STREQUAL "CUDA")
# samxx will be CUDA-linked with device symbols resolved at library creation
set_target_properties(samxx PROPERTIES LINKER_LANGUAGE CUDA CUDA_SEPARABLE_COMPILATION OFF CUDA_RESOLVE_DEVICE_SYMBOLS ON)
endif()
# samxx needs to link with the yakl library
target_link_libraries(samxx yakl)
target_compile_features(samxx PUBLIC cxx_std_14)
# Set fortran compiler flags
set_source_files_properties(${F90_SRC} PROPERTIES COMPILE_FLAGS "${CPPDEFS} ${FFLAGS}")
# Set YAKL compiler flags
include(${YAKL_HOME}/process_cxx_source_files.cmake)
# Have to quote the list of files in order to pass it into a macro
process_cxx_source_files("${CXX_SRC}")
message(STATUS "YAKL Flags: ${YAKL_COMPILER_FLAGS}")
# Include YAKL source and library directories
include_directories(${YAKL_BIN})