Skip to content

Commit

Permalink
Merge pull request #434 from climbfuji/feature/unit_conversions_in_ca…
Browse files Browse the repository at this point in the history
…pgen

feature/capgen: add new tests for unit conversions
  • Loading branch information
climbfuji authored Feb 28, 2022
2 parents 0c69a9c + a4f2883 commit 195ee07
Show file tree
Hide file tree
Showing 18 changed files with 1,353 additions and 1 deletion.
12 changes: 11 additions & 1 deletion scripts/metavar.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,19 @@ class Var:
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'ino'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseSyntaxError: Invalid intent variable property, 'ino', at <standard input>:1
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm/s', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in', 'optional' : 'false'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +IGNORE_EXCEPTION_DETAIL
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm s-1', 'dimensions' : '()', 'type' : 'real', 'intent' : 'in', 'optional' : 'false'}, ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV) #doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
ParseSyntaxError: Invalid variable property name, 'optional', at <standard input>:1
# Check that two variables that differ in their units - m vs km - are compatible
>>> Var({'local_name' : 'foo', 'standard_name' : 'hi_mom', 'units' : 'm', \
'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, \
ParseSource('vname', 'SCHEME', ParseContext()), \
_MVAR_DUMMY_RUN_ENV).compatible(Var({'local_name' : 'bar', \
'standard_name' : 'hi_mom', 'units' : 'km', \
'dimensions' : '()', 'type' : 'real', 'intent' : 'in'}, \
ParseSource('vname', 'SCHEME', ParseContext()), _MVAR_DUMMY_RUN_ENV), \
_MVAR_DUMMY_RUN_ENV) #doctest: +ELLIPSIS
<var_props.VarCompatObj object at ...>
"""

## Prop lists below define all the allowed CCPP Metadata attributes
Expand Down
26 changes: 26 additions & 0 deletions scripts/var_props.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,32 @@ class VarCompatObj:
"real", "kind_phys", "m", ['horizontal_loop_extent'], \
"var2_lname", _DOCTEST_RUNENV) #doctest: +ELLIPSIS
<__main__.VarCompatObj object at 0x...>
# Test that a 2-D var with unit conversion m->km works
>>> VarCompatObj("var_stdname", "real", "kind_phys", "m", \
['horizontal_dimension'], "var1_lname", "var_stdname", \
"real", "kind_phys", "km", ['horizontal_dimension'], \
"var2_lname", _DOCTEST_RUNENV) #doctest: +ELLIPSIS
<__main__.VarCompatObj object at 0x...>
# Test that a 2-D var with unit conversion m->km works and that it
# produces the correct forward transformation
>>> VarCompatObj("var_stdname", "real", "kind_phys", "m", \
['horizontal_dimension'], "var1_lname", "var_stdname", \
"real", "kind_phys", "km", ['horizontal_dimension'], \
"var2_lname", _DOCTEST_RUNENV).forward_transform( \
"var1_lname", "var2_lname", ('i'))
'var1_lname(i) = 1.0E-3_kind_phys*var2_lname(i)'
# Test that a 3-D var with unit conversion m->km and vertical flipping
# works and that it produces the correct reverse transformation
>>> VarCompatObj("var_stdname", "real", "kind_phys", "m", \
['horizontal_dimension', 'vertical_layer_dimension'], \
"var1_lname", "var_stdname", "real", "kind_phys", "km",\
['horizontal_dimension', 'vertical_layer_dimension'], \
"var2_lname", _DOCTEST_RUNENV).reverse_transform( \
"var1_lname", "var2_lname", ('i','k'), flip_vdim='nk')
'var1_lname(i,nk-k+1) = 1.0E+3_kind_phys*var2_lname(i,k)'
"""

def __init__(self, var1_stdname, var1_type, var1_kind, var1_units,
Expand Down
8 changes: 8 additions & 0 deletions test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ if [ $res -ne 0 ]; then
echo "Failure running advection test"
fi

# Run var_action test
./var_action_test/run_test
res=$?
errcnt=$((errcnt + res))
if [ $res -ne 0 ]; then
echo "Failure running var_action test"
fi

# Run doctests
./run_doctest.sh
res=$?
Expand Down
1 change: 1 addition & 0 deletions test/var_action_test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
187 changes: 187 additions & 0 deletions test/var_action_test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
PROJECT(test_host)
ENABLE_LANGUAGE(Fortran)

include(CMakeForceCompiler)

SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/modules)

#------------------------------------------------------------------------------
#
# Set where the CCPP Framework lives
#
#------------------------------------------------------------------------------
get_filename_component(TEST_ROOT "${CMAKE_SOURCE_DIR}" DIRECTORY)
get_filename_component(CCPP_ROOT "${TEST_ROOT}" DIRECTORY)
#------------------------------------------------------------------------------
#
# Create list of SCHEME_FILES, HOST_FILES, and SUITE_FILES
# Paths should be relative to CMAKE_SOURCE_DIR (this file's directory)
#
#------------------------------------------------------------------------------
LIST(APPEND SCHEME_FILES "var_action_files.txt")
LIST(APPEND HOST_FILES "test_host_data" "test_host_mod")
LIST(APPEND SUITE_FILES "var_action_suite.xml")
# HOST is the name of the executable we will build.
# We assume there are files ${HOST}.meta and ${HOST}.F90 in CMAKE_SOURCE_DIR
SET(HOST "${CMAKE_PROJECT_NAME}")

#------------------------------------------------------------------------------
#
# End of project-specific input
#
#------------------------------------------------------------------------------

# By default, no verbose output
SET(VERBOSITY 0 CACHE STRING "Verbosity level of output (default: 0)")
# By default, generated caps go in ccpp subdir
SET(CCPP_CAP_FILES "${CMAKE_BINARY_DIR}/ccpp" CACHE
STRING "Location of CCPP-generated cap files")

SET(CCPP_FRAMEWORK ${CCPP_ROOT}/scripts)

# Use rpaths on MacOSX
set(CMAKE_MACOSX_RPATH 1)

#------------------------------------------------------------------------------
# Set a default build type if none was specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
#message(STATUS "Setting build type to 'Debug' as none was specified.")
#set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE)
message(STATUS "Setting build type to 'Release' as none was specified.")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)

# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
"MinSizeRel" "RelWithDebInfo")
endif()

ADD_COMPILE_OPTIONS(-O0)

if (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU")
# gfortran
# MESSAGE("gfortran being used.")
ADD_COMPILE_OPTIONS(-fcheck=all)
ADD_COMPILE_OPTIONS(-fbacktrace)
ADD_COMPILE_OPTIONS(-ffpe-trap=zero)
ADD_COMPILE_OPTIONS(-finit-real=nan)
ADD_COMPILE_OPTIONS(-ggdb)
ADD_COMPILE_OPTIONS(-ffree-line-length-none)
ADD_COMPILE_OPTIONS(-cpp)
elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "Intel")
# ifort
# MESSAGE("ifort being used.")
#ADD_COMPILE_OPTIONS(-check all)
ADD_COMPILE_OPTIONS(-fpe0)
ADD_COMPILE_OPTIONS(-warn)
ADD_COMPILE_OPTIONS(-traceback)
ADD_COMPILE_OPTIONS(-debug extended)
ADD_COMPILE_OPTIONS(-fpp)
elseif (${CMAKE_Fortran_COMPILER_ID} MATCHES "PGI")
# pgf90
# MESSAGE("pgf90 being used.")
ADD_COMPILE_OPTIONS(-g)
ADD_COMPILE_OPTIONS(-Mipa=noconst)
ADD_COMPILE_OPTIONS(-traceback)
ADD_COMPILE_OPTIONS(-Mfree)
ADD_COMPILE_OPTIONS(-Mfptrap)
ADD_COMPILE_OPTIONS(-Mpreprocess)
else (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU")
message (WARNING "This program has only been compiled with gfortran, pgf90 and ifort. If another compiler is needed, the appropriate flags SHOULD be added in ${CMAKE_SOURCE_DIR}/CMakeLists.txt")
endif (${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU")

#------------------------------------------------------------------------------
# CMake Modules
# Set the CMake module path
list(APPEND CMAKE_MODULE_PATH "${CCPP_FRAMEWORK}/cmake")
#------------------------------------------------------------------------------
# Set OpenMP flags for C/C++/Fortran
if (OPENMP)
include(detect_openmp)
detect_openmp()
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
set (CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} ${OpenMP_Fortran_FLAGS}")
message(STATUS "Enable OpenMP support for C/C++/Fortran compiler")
else(OPENMP)
message (STATUS "Disable OpenMP support for C/C++/Fortran compiler")
endif()

# Create metadata and source file lists
FOREACH(FILE ${SCHEME_FILES})
FILE(STRINGS ${FILE} FILENAMES)
LIST(APPEND SCHEME_FILENAMES ${FILENAMES})
ENDFOREACH(FILE)
string(REPLACE ";" "," SCHEME_METADATA "${SCHEME_FILES}")

FOREACH(FILE ${SCHEME_FILENAMES})
# target_sources prefers absolute pathnames
string(REPLACE ".meta" ".F90" TEMP "${FILE}")
get_filename_component(ABS_PATH "${TEMP}" ABSOLUTE)
list(APPEND LIBRARY_LIST ${ABS_PATH})
ENDFOREACH(FILE)

FOREACH(FILE ${HOST_FILES})
LIST(APPEND HOST_METADATA "${FILE}.meta")
# target_sources prefers absolute pathnames
get_filename_component(ABS_PATH "${FILE}.F90" ABSOLUTE)
LIST(APPEND HOST_SOURCE "${ABS_PATH}")
ENDFOREACH(FILE)
list(APPEND LIBRARY_LIST ${HOST_SOURCE})
string(REPLACE ";" ".meta," HOST_METADATA "${HOST_FILES}")
set(HOST_METADATA "${HOST_METADATA}.meta,${HOST}.meta")

string(REPLACE ";" "," SUITE_XML "${SUITE_FILES}")

# Run ccpp_capgen
set(CAPGEN_CMD "${CCPP_FRAMEWORK}/ccpp_capgen.py")
list(APPEND CAPGEN_CMD "--host-files")
list(APPEND CAPGEN_CMD "${HOST_METADATA}")
list(APPEND CAPGEN_CMD "--scheme-files")
list(APPEND CAPGEN_CMD "${SCHEME_METADATA}")
list(APPEND CAPGEN_CMD "--suites")
list(APPEND CAPGEN_CMD "${SUITE_XML}")
list(APPEND CAPGEN_CMD "--host-name")
list(APPEND CAPGEN_CMD "test_host")
list(APPEND CAPGEN_CMD "--output-root")
list(APPEND CAPGEN_CMD "${CCPP_CAP_FILES}")
while (VERBOSITY GREATER 0)
list(APPEND CAPGEN_CMD "--verbose")
MATH(EXPR VERBOSITY "${VERBOSITY} - 1")
endwhile ()
string(REPLACE ";" " " CAPGEN_STRING "${CAPGEN_CMD}")
MESSAGE(STATUS "Running: ${CAPGEN_STRING}")
EXECUTE_PROCESS(COMMAND ${CAPGEN_CMD} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE CAPGEN_OUT ERROR_VARIABLE CAPGEN_OUT RESULT_VARIABLE RES)
MESSAGE(STATUS "${CAPGEN_OUT}")
if (RES EQUAL 0)
MESSAGE(STATUS "CCPP cap generation completed")
else(RES EQUAL 0)
MESSAGE(FATAL_ERROR "CCPP cap generation FAILED: result = ${RES}")
endif(RES EQUAL 0)

# Retrieve the list of files from datatable.xml and set to CCPP_CAPS
set(DTABLE_CMD "${CCPP_FRAMEWORK}/ccpp_datafile.py")
list(APPEND DTABLE_CMD "${CCPP_CAP_FILES}/datatable.xml")
list(APPEND DTABLE_CMD "--ccpp-files")
list(APPEND DTABLE_CMD "--separator=\\;")
string(REPLACE ";" " " DTABLE_STRING "${DTABLE_CMD}")
MESSAGE(STATUS "Running: ${DTABLE_STRING}")
EXECUTE_PROCESS(COMMAND ${DTABLE_CMD} OUTPUT_VARIABLE CCPP_CAPS
RESULT_VARIABLE RES
OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_STRIP_TRAILING_WHITESPACE)
message(STATUS "CCPP_CAPS = ${CCPP_CAPS}")
if (RES EQUAL 0)
MESSAGE(STATUS "CCPP cap files retrieved")
else(RES EQUAL 0)
MESSAGE(FATAL_ERROR "CCPP cap file retrieval FAILED: result = ${RES}")
endif(RES EQUAL 0)
list(APPEND LIBRARY_LIST ${CCPP_CAPS})
add_library(TESTLIB OBJECT ${LIBRARY_LIST})
ADD_EXECUTABLE(${HOST} ${HOST}.F90 $<TARGET_OBJECTS:TESTLIB>)

INCLUDE_DIRECTORIES(${CCPP_CAP_FILES})

set_target_properties(${HOST} PROPERTIES
COMPILE_FLAGS "${CMAKE_Fortran_FLAGS}"
LINK_FLAGS "${CMAKE_Fortran_FLAGS}")
6 changes: 6 additions & 0 deletions test/var_action_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
var_action test
================

To build and run the var_action test, run ./run_test
This script will build and run the test.
The exit code is zero (0) on PASS and non-zero on FAIL.
46 changes: 46 additions & 0 deletions test/var_action_test/effr_calc.F90
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
!Test unit conversions for intent in, inout, out variables
!

module effr_calc

use ccpp_kinds, only: kind_phys

implicit none
private

public :: effr_calc_run

contains

!> \section arg_table_effr_calc_run Argument Table
!! \htmlinclude arg_table_effr_calc_run.html
!!
subroutine effr_calc_run(ncol, nlev, effrr_in, effrl_inout, &
effri_out, effrs_inout, errmsg, errflg)

integer, intent(in) :: ncol
integer, intent(in) :: nlev
real(kind_phys), intent(in) :: effrr_in(:,:)
real(kind_phys), intent(inout) :: effrl_inout(:,:)
real(kind_phys), intent(out) :: effri_out(:,:)
real(kind_phys), intent(inout) :: effrs_inout(:,:)
character(len=512), intent(out) :: errmsg
integer, intent(out) :: errflg
!----------------------------------------------------------------

real(kind_phys), parameter :: re_qc_min = 2.5 ! microns
real(kind_phys), parameter :: re_qc_max = 50. ! microns
real(kind_phys), parameter :: re_qi_avg = 75. ! microns
real(kind_phys) :: effrr_local(ncol,nlev)

errmsg = ''
errflg = 0

effrr_local = effrr_in
effrl_inout = min(max(effrl_inout,re_qc_min),re_qc_max)
effri_out = re_qi_avg
effrs_inout = effrs_inout + 10.0 ! in micrometer

end subroutine effr_calc_run

end module effr_calc
66 changes: 66 additions & 0 deletions test/var_action_test/effr_calc.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[ccpp-table-properties]
name = effr_calc
type = scheme
dependencies =
[ccpp-arg-table]
name = effr_calc_run
type = scheme
[ ncol ]
standard_name = horizontal_loop_extent
type = integer
units = count
dimensions = ()
intent = in
[ nlev ]
standard_name = vertical_layer_dimension
type = integer
units = count
dimensions = ()
intent = in
[effrr_in]
standard_name = effective_radius_of_stratiform_cloud_rain_particle
long_name = effective radius of cloud rain particle in micrometer
units = um
dimensions = (horizontal_loop_extent,vertical_layer_dimension)
type = real
kind = kind_phys
intent = in
[effrl_inout]
standard_name = effective_radius_of_stratiform_cloud_liquid_water_particle
long_name = effective radius of cloud liquid water particle in micrometer
units = um
dimensions = (horizontal_loop_extent,vertical_layer_dimension)
type = real
kind = kind_phys
intent = inout
[effri_out]
standard_name = effective_radius_of_stratiform_cloud_ice_particle
long_name = effective radius of cloud ice water particle in micrometer
units = um
dimensions = (horizontal_loop_extent,vertical_layer_dimension)
type = real
kind = kind_phys
intent = out
[effrs_inout]
standard_name = effective_radius_of_stratiform_cloud_snow_particle
long_name = effective radius of cloud snow particle in micrometer
units = um
dimensions = (horizontal_loop_extent,vertical_layer_dimension)
type = real
kind = kind_phys
intent = inout
[ errmsg ]
standard_name = ccpp_error_message
long_name = Error message for error handling in CCPP
units = none
dimensions = ()
type = character
kind = len=512
intent = out
[ errflg ]
standard_name = ccpp_error_code
long_name = Error flag for error handling in CCPP
units = 1
dimensions = ()
type = integer
intent = out
Loading

0 comments on commit 195ee07

Please sign in to comment.