diff --git a/.gitignore b/.gitignore index 04af4cbc..7d6f4be2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ test_driver_*.sh *~ .#* \#*# +**/.vscode/ diff --git a/src/control/cam_comp.F90 b/src/control/cam_comp.F90 index 8e12e405..0a8b1b63 100644 --- a/src/control/cam_comp.F90 +++ b/src/control/cam_comp.F90 @@ -84,25 +84,27 @@ subroutine cam_init(caseid, ctitle, model_doi_url, & ! !----------------------------------------------------------------------- - use cam_initfiles, only: cam_initfiles_open - use dyn_grid, only: model_grid_init - use phys_comp, only: phys_init - use phys_comp, only: phys_register - use dyn_comp, only: dyn_init -! use cam_restart, only: cam_read_restart - use camsrfexch, only: hub2atm_alloc, atm2hub_alloc - use cam_history, only: history_init_files -! use history_scam, only: scm_intht - use cam_pio_utils, only: init_pio_subsystem - use cam_instance, only: inst_suffix -! use history_defaults, only: initialize_iop_history - use stepon, only: stepon_init - use air_composition, only: air_composition_init - use cam_ccpp_cap, only: cam_ccpp_initialize_constituents - use physics_grid, only: columns_on_task - use vert_coord, only: pver - use phys_vars_init_check, only: mark_as_initialized - use tropopause_climo_read, only: tropopause_climo_read_file + use cam_initfiles, only: cam_initfiles_open + use dyn_grid, only: model_grid_init + use phys_comp, only: phys_init + use phys_comp, only: phys_register + use dyn_comp, only: dyn_init +! use cam_restart, only: cam_read_restart + use camsrfexch, only: hub2atm_alloc, atm2hub_alloc + use cam_history, only: history_init_files +! use history_scam, only: scm_intht + use cam_pio_utils, only: init_pio_subsystem + use cam_instance, only: inst_suffix +! use history_defaults, only: initialize_iop_history + use stepon, only: stepon_init + use air_composition, only: air_composition_init + use cam_ccpp_cap, only: cam_ccpp_initialize_constituents + use physics_grid, only: columns_on_task + use vert_coord, only: pver + use phys_vars_init_check, only: mark_as_initialized + use tropopause_climo_read, only: tropopause_climo_read_file + use musica_ccpp_dependencies, only: musica_ccpp_dependencies_init + use orbital_data, only: orbital_data_init ! Arguments character(len=cl), intent(in) :: caseid ! case ID @@ -259,6 +261,16 @@ subroutine cam_init(caseid, ctitle, model_doi_url, & ! end if call history_init_files(model_doi_url, caseid, ctitle) + ! Temporary: Prescribe realistic but inaccurate physical quantities + ! necessary for MUSICA that are currently unavailable in CAM-SIMA. + ! + ! Remove this when MUSICA input data are available from CAM-SIMA or + ! other physics schemes. + call musica_ccpp_dependencies_init(columns_on_task, pver, iulog) + + ! Initialize orbital data + call orbital_data_init(columns_on_task) + end subroutine cam_init ! @@ -271,8 +283,16 @@ subroutine cam_timestep_init() ! !----------------------------------------------------------------------- - use phys_comp, only: phys_timestep_init - use stepon, only: stepon_timestep_init + use phys_comp, only: phys_timestep_init + use physics_grid, only: lat_rad, lon_rad + use orbital_data, only: orbital_data_advance + use stepon, only: stepon_timestep_init + + ! Update current fractional calendar day. Needs to be updated at every timestep. + calday = get_curr_calday() + + ! Update the orbital data + call orbital_data_advance(calday, lat_rad, lon_rad) ! Update timestep flags in physics state is_first_timestep = is_first_step() @@ -294,9 +314,6 @@ subroutine cam_timestep_init() ! call phys_timestep_init() - ! Update current fractional calendar day. Needs to be updated at every timestep. - calday = get_curr_calday() - end subroutine cam_timestep_init ! !----------------------------------------------------------------------- diff --git a/src/data/registry.xml b/src/data/registry.xml index 91658e20..f1e02fbc 100644 --- a/src/data/registry.xml +++ b/src/data/registry.xml @@ -9,6 +9,8 @@ $SRCROOT/src/control/camsrfexch.meta $SRCROOT/src/control/runtime_obj.meta $SRCROOT/src/data/physconst.meta + $SRCROOT/src/physics/utils/orbital_data.meta + $SRCROOT/src/physics/utils/musica_ccpp_dependencies.meta $SRCROOT/src/physics/utils/physics_grid.meta $SRCROOT/src/physics/utils/cam_constituents.meta $SRCROOT/src/physics/utils/tropopause_climo_read.meta diff --git a/src/physics/ncar_ccpp b/src/physics/ncar_ccpp index 491e5624..45c37530 160000 --- a/src/physics/ncar_ccpp +++ b/src/physics/ncar_ccpp @@ -1 +1 @@ -Subproject commit 491e56247815ef23bfd8dba65d1e3c3b78ba164a +Subproject commit 45c37530ead4f5b93621e3d979e972ea91217923 diff --git a/src/physics/utils/musica_ccpp_dependencies.F90 b/src/physics/utils/musica_ccpp_dependencies.F90 new file mode 100644 index 00000000..3bd53167 --- /dev/null +++ b/src/physics/utils/musica_ccpp_dependencies.F90 @@ -0,0 +1,197 @@ +! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research +! SPDX-License-Identifier: Apache-2.0 +module musica_ccpp_dependencies +!-------------------------------------------------------------------------- +! +! This module temporarily provides data that MUSICA chemistry consumes but +! does not produce. The values are realistic but are not based on the +! actual model state. These should be removed as the producers of this data +! are added to CAM-SIMA or as CCPP-compliant physics schemes. +! +! IMPORTANT: This module must be completely removed before doing any actual +! science with MUSICA chemistry in CAM-SIMA. +! +!-------------------------------------------------------------------------- + + use ccpp_kinds, only: kind_phys + + implicit none + private + + public :: musica_ccpp_dependencies_init + + !> \section arg_table_musica_ccpp_dependencies Argument Table + !! \htmlinclude arg_table_musica_ccpp_dependencies.html + !! + integer, public, protected :: photolysis_wavelength_grid_section_dimension = 102 + integer, public, protected :: photolysis_wavelength_grid_interface_dimension = 103 + real(kind_phys), allocatable, public, protected :: photolysis_wavelength_grid_interfaces(:) + real(kind_phys), allocatable, public, protected :: extraterrestrial_radiation_flux(:) + real(kind_phys), allocatable, public, protected :: surface_albedo(:) + real(kind_phys), allocatable, public, protected :: blackbody_temperature_at_surface(:) + real(kind_phys), allocatable, public, protected :: cloud_area_fraction(:,:) + + ! local parameters + character(len=*), parameter :: module_name = '(musica_ccpp_dependencies)' + +!============================================================================== +contains +!============================================================================== + + subroutine musica_ccpp_dependencies_init(horizontal_dimension, & + vertical_layer_dimension, log_file_unit) + + use cam_abortutils, only: check_allocate + + !----------------------------------------------------------------------- + ! + ! Initialize the MUSICA scheme dependencies. + ! + !----------------------------------------------------------------------- + + integer, intent(in) :: horizontal_dimension + integer, intent(in) :: vertical_layer_dimension + integer, intent(in) :: log_file_unit + + integer :: error_code + character(len=*), parameter :: subroutine_name = & + trim(module_name)//':(musica_ccpp_dependencies_init)' + + write(log_file_unit,*) 'WARNING: Using placeholder data for MUSICA chemistry.' + + allocate(photolysis_wavelength_grid_interfaces(photolysis_wavelength_grid_interface_dimension), & + stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'photolysis_wavelength_grid_interfaces(photolysis_wavelength_grid_interface_dimension)', & + file=__FILE__, line=__LINE__) + allocate(extraterrestrial_radiation_flux(photolysis_wavelength_grid_section_dimension), & + stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'extraterrestrial_radiation_flux(photolysis_wavelength_grid_section_dimension)', & + file=__FILE__, line=__LINE__) + allocate(surface_albedo(horizontal_dimension), stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'surface_albedo(horizontal_dimension)', & + file=__FILE__, line=__LINE__) + allocate(blackbody_temperature_at_surface(horizontal_dimension), stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'blackbody_temperature_at_surface(horizontal_dimension)', & + file=__FILE__, line=__LINE__) + allocate(cloud_area_fraction(horizontal_dimension, vertical_layer_dimension), stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'cloud_area_fraction(horizontal_dimension, vertical_layer_dimension)', & + file=__FILE__, line=__LINE__) + + surface_albedo(:) = 0.1_kind_phys + blackbody_temperature_at_surface(:) = 292.3_kind_phys + cloud_area_fraction(:,:) = 0.7_kind_phys + extraterrestrial_radiation_flux(:) = 1.0e14_kind_phys + photolysis_wavelength_grid_interfaces = (/ & + 120.0e-9_kind_phys, & + 121.4e-9_kind_phys, & + 121.9e-9_kind_phys, & + 123.5e-9_kind_phys, & + 124.3e-9_kind_phys, & + 125.5e-9_kind_phys, & + 126.3e-9_kind_phys, & + 127.1e-9_kind_phys, & + 130.1e-9_kind_phys, & + 131.1e-9_kind_phys, & + 135.0e-9_kind_phys, & + 140.0e-9_kind_phys, & + 145.0e-9_kind_phys, & + 150.0e-9_kind_phys, & + 155.0e-9_kind_phys, & + 160.0e-9_kind_phys, & + 165.0e-9_kind_phys, & + 168.0e-9_kind_phys, & + 171.0e-9_kind_phys, & + 173.0e-9_kind_phys, & + 174.4e-9_kind_phys, & + 175.4e-9_kind_phys, & + 177.0e-9_kind_phys, & + 178.6e-9_kind_phys, & + 180.2e-9_kind_phys, & + 181.8e-9_kind_phys, & + 183.5e-9_kind_phys, & + 185.2e-9_kind_phys, & + 186.9e-9_kind_phys, & + 188.7e-9_kind_phys, & + 190.5e-9_kind_phys, & + 192.3e-9_kind_phys, & + 194.2e-9_kind_phys, & + 196.1e-9_kind_phys, & + 198.0e-9_kind_phys, & + 200.0e-9_kind_phys, & + 202.0e-9_kind_phys, & + 204.1e-9_kind_phys, & + 206.2e-9_kind_phys, & + 208.0e-9_kind_phys, & + 211.0e-9_kind_phys, & + 214.0e-9_kind_phys, & + 217.0e-9_kind_phys, & + 220.0e-9_kind_phys, & + 223.0e-9_kind_phys, & + 226.0e-9_kind_phys, & + 229.0e-9_kind_phys, & + 232.0e-9_kind_phys, & + 235.0e-9_kind_phys, & + 238.0e-9_kind_phys, & + 241.0e-9_kind_phys, & + 244.0e-9_kind_phys, & + 247.0e-9_kind_phys, & + 250.0e-9_kind_phys, & + 253.0e-9_kind_phys, & + 256.0e-9_kind_phys, & + 259.0e-9_kind_phys, & + 263.0e-9_kind_phys, & + 267.0e-9_kind_phys, & + 271.0e-9_kind_phys, & + 275.0e-9_kind_phys, & + 279.0e-9_kind_phys, & + 283.0e-9_kind_phys, & + 287.0e-9_kind_phys, & + 291.0e-9_kind_phys, & + 295.0e-9_kind_phys, & + 298.5e-9_kind_phys, & + 302.5e-9_kind_phys, & + 305.5e-9_kind_phys, & + 308.5e-9_kind_phys, & + 311.5e-9_kind_phys, & + 314.5e-9_kind_phys, & + 317.5e-9_kind_phys, & + 322.5e-9_kind_phys, & + 327.5e-9_kind_phys, & + 332.5e-9_kind_phys, & + 337.5e-9_kind_phys, & + 342.5e-9_kind_phys, & + 347.5e-9_kind_phys, & + 350.0e-9_kind_phys, & + 355.0e-9_kind_phys, & + 360.0e-9_kind_phys, & + 365.0e-9_kind_phys, & + 370.0e-9_kind_phys, & + 375.0e-9_kind_phys, & + 380.0e-9_kind_phys, & + 385.0e-9_kind_phys, & + 390.0e-9_kind_phys, & + 395.0e-9_kind_phys, & + 400.0e-9_kind_phys, & + 405.0e-9_kind_phys, & + 410.0e-9_kind_phys, & + 415.0e-9_kind_phys, & + 420.0e-9_kind_phys, & + 430.0e-9_kind_phys, & + 440.0e-9_kind_phys, & + 450.0e-9_kind_phys, & + 500.0e-9_kind_phys, & + 550.0e-9_kind_phys, & + 600.0e-9_kind_phys, & + 650.0e-9_kind_phys, & + 700.0e-9_kind_phys, & + 750.0e-9_kind_phys & + /) + + end subroutine musica_ccpp_dependencies_init + +end module musica_ccpp_dependencies \ No newline at end of file diff --git a/src/physics/utils/musica_ccpp_dependencies.meta b/src/physics/utils/musica_ccpp_dependencies.meta new file mode 100644 index 00000000..0d14908e --- /dev/null +++ b/src/physics/utils/musica_ccpp_dependencies.meta @@ -0,0 +1,48 @@ +[ccpp-table-properties] + name = musica_ccpp_dependencies + type = module +[ccpp-arg-table] + name = musica_ccpp_dependencies + type = module +[ photolysis_wavelength_grid_section_dimension ] + standard_name = photolysis_wavelength_grid_section_dimension + units = count + type = integer + dimensions = () + protected = True +[ photolysis_wavelength_grid_interface_dimension ] + standard_name = photolysis_wavelength_grid_interface_dimension + units = count + type = integer + dimensions = () + protected = True +[ photolysis_wavelength_grid_interfaces ] + standard_name = photolysis_wavelength_grid_interfaces + units = m + type = real | kind = kind_phys + dimensions = (photolysis_wavelength_grid_interface_dimension) + protected = True +[ extraterrestrial_radiation_flux ] + standard_name = extraterrestrial_radiation_flux + units = photons cm-2 s-1 nm-1 + type = real | kind = kind_phys + dimensions = (photolysis_wavelength_grid_section_dimension) + protected = True +[ surface_albedo ] + standard_name = surface_albedo_due_to_UV_and_VIS_direct + units = none + type = real | kind = kind_phys + dimensions = (horizontal_dimension) + protected = True +[ blackbody_temperature_at_surface ] + standard_name = blackbody_temperature_at_surface + type = real | kind = kind_phys + units = K + dimensions = (horizontal_dimension) + protected = True +[ cloud_area_fraction ] + standard_name = cloud_area_fraction + type = real | kind = kind_phys + units = fraction + dimensions = (horizontal_dimension,vertical_layer_dimension) + protected = True diff --git a/src/physics/utils/orbital_data.F90 b/src/physics/utils/orbital_data.F90 new file mode 100644 index 00000000..df9f852d --- /dev/null +++ b/src/physics/utils/orbital_data.F90 @@ -0,0 +1,90 @@ +! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research +! SPDX-License-Identifier: Apache-2.0 +module orbital_data +!-------------------------------------------------------------------------- +! +! Provides access to conditions calculated based on the Earth's orbit. +! +!-------------------------------------------------------------------------- + + use ccpp_kinds, only: kind_phys + use shr_orb_mod, only: FILL_R8 => SHR_ORB_UNDEF_REAL + + implicit none + private + + public :: orbital_data_init, orbital_data_advance + + !> \section arg_table_orbital_data Argument Table + !! \htmlinclude arg_table_orbital_data.html + !! + real(kind_phys), protected, public :: solar_declination = FILL_R8 ! Solar declination angle [radians] + real(kind_phys), protected, public :: earth_sun_distance = FILL_R8 ! Earth-sun distance [AU] + real(kind_phys), allocatable, protected, public :: solar_zenith_angle(:) ! Solar zenith angle (column) [radians] + + ! Local parameters + character(len=*), parameter :: module_name = '(orbital_data)' + +!======================================================================= +contains +!======================================================================= + + subroutine orbital_data_init(number_of_columns) + + use cam_abortutils, only: check_allocate + + !----------------------------------------------------------------------- + ! + ! Initialize the orbital data module. + ! + !----------------------------------------------------------------------- + + integer, intent(in) :: number_of_columns + + integer :: error_code + character(len=*), parameter :: subroutine_name = & + trim(module_name)//':(orbital_data_init)' + + allocate(solar_zenith_angle(number_of_columns), source=FILL_R8, & + stat=error_code) + call check_allocate(error_code, subroutine_name, & + 'solar_zenith_angle(number_of_columns)', & + file=__FILE__, line=__LINE__) + + end subroutine orbital_data_init + + !======================================================================= + + subroutine orbital_data_advance(calendar_day, latitudes, longitudes) + + !----------------------------------------------------------------------- + ! + ! Advance the orbital data to the current simulation time. + ! + !----------------------------------------------------------------------- + + use shr_orb_mod, only: shr_orb_decl, shr_orb_cosz + use cam_control_mod, only: eccen, mvelpp, lambm0, obliqr + + real(kind_phys), intent(in) :: calendar_day ! Fractional Julian calendar day (1.xx to 365.xx) + real(kind_phys), intent(in) :: latitudes(:) ! Centered latitude (column) [radians] + real(kind_phys), intent(in) :: longitudes(:) ! Centered longitude (column) [radians] + + integer :: i + + ! Compute the solar declination angle [radians] and Earth-sun distance [AU] + call shr_orb_decl(calendar_day, eccen, mvelpp, lambm0, obliqr, & + solar_declination, earth_sun_distance) + + ! Compute the solar zenith angle [radians] + do i = 1, size(latitudes) + solar_zenith_angle(i) = acos(shr_orb_cosz(calendar_day, latitudes(i), & + longitudes(i), solar_declination)) + end do + + end subroutine orbital_data_advance + + !======================================================================= + +end module orbital_data + diff --git a/src/physics/utils/orbital_data.meta b/src/physics/utils/orbital_data.meta new file mode 100644 index 00000000..611f7535 --- /dev/null +++ b/src/physics/utils/orbital_data.meta @@ -0,0 +1,21 @@ +[ccpp-table-properties] + name = orbital_data + type = module +[ccpp-arg-table] + name = orbital_data + type = module +[solar_declination] + standard_name = solar_declination + units = rad + type = real | kind = kind_phys + dimensions = () +[earth_sun_distance] + standard_name = earth_sun_distance + units = AU + type = real | kind = kind_phys + dimensions = () +[solar_zenith_angle] + standard_name = solar_zenith_angle + units = rad + type = real | kind = kind_phys + dimensions = (horizontal_dimension) \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 00000000..21b46f77 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,25 @@ +cmake_minimum_required(VERSION 3.21) + +project( + cam-sima-tests + VERSION 0.0.0 + LANGUAGES Fortran +) + +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH};${CMAKE_CURRENT_LIST_DIR}/cmake) +set(CMAKE_USER_MAKE_RULES_OVERRIDE ${CMAKE_MODULE_PATH}/SetDefaults.cmake) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +option(CCPP_ENABLE_MEMCHECK "Enable memory checks in tests" OFF) + +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) + +set(CAM_SIMA_SRC_PATH ${CMAKE_SOURCE_DIR}/../src) +set(CAM_SIMA_TEST_PATH ${CMAKE_SOURCE_DIR}) + +include(TestUtils) +include(CTest) +enable_testing() + +add_subdirectory(unit) \ No newline at end of file diff --git a/test/cmake/SetDefaults.cmake b/test/cmake/SetDefaults.cmake new file mode 100644 index 00000000..39e5d1d5 --- /dev/null +++ b/test/cmake/SetDefaults.cmake @@ -0,0 +1,4 @@ +# Overwrite the init values choosen by CMake +if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + set(CMAKE_Fortran_FLAGS_DEBUG_INIT "-g") +endif() diff --git a/test/cmake/TestUtils.cmake b/test/cmake/TestUtils.cmake new file mode 100644 index 00000000..df31b9cd --- /dev/null +++ b/test/cmake/TestUtils.cmake @@ -0,0 +1,23 @@ +################################################################################ +# Utility functions for creating tests + +if(CCPP_ENABLE_MEMCHECK) + find_program(MEMORYCHECK_COMMAND "valgrind") + + # Set the Valgrind suppressions file for tests + set(MEMCHECK_SUPPRESS "--suppressions=${CMAKE_SOURCE_DIR}/valgrind.supp") +endif() + +################################################################################ +# Runs a test with memory checking if enabled + +function(add_memory_check_test test_name test_binary test_args working_dir) + if(CCPP_ENABLE_MEMCHECK) + add_test(NAME memcheck_${test_name} + COMMAND mpirun -v -np 1 ${MEMORYCHECK_COMMAND} --leak-check=full --error-exitcode=1 --trace-children=yes --gen-suppressions=all ${MEMCHECK_SUPPRESS} + ${test_binary} ${test_args} + WORKING_DIRECTORY ${working_dir}) + endif() +endfunction(add_memory_check_test) + +################################################################################ \ No newline at end of file diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile new file mode 100644 index 00000000..474be7e5 --- /dev/null +++ b/test/docker/Dockerfile @@ -0,0 +1,40 @@ +# This Dockerfile is designed for running CAM-SIMA fortran unit tests and integration tests. +FROM ubuntu:22.04 + +ARG BUILD_TYPE=Debug + +RUN apt update \ + && apt install -y sudo \ + && useradd -m test_user \ + && echo "test_user ALL=(root) NOPASSWD: ALL" >> /etc/sudoers.d/test_user \ + && chmod 0440 /etc/sudoers.d/test_user + +USER test_user +WORKDIR /home/test_user + +RUN sudo apt update \ + && sudo apt -y install \ + cmake \ + cmake-curses-gui \ + gfortran \ + git \ + libopenmpi-dev \ + make \ + openmpi-bin \ + valgrind \ + vim \ + && sudo apt clean + +ENV FC=mpif90 +ENV FFLAGS="-I/usr/include/" + +COPY . cam_sima +RUN sudo chown -R test_user:test_user cam_sima + +RUN cd cam_sima/test \ + && cmake -S . -B build \ + -D CMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -D CCPP_ENABLE_MEMCHECK=ON \ + && cmake --build ./build + +WORKDIR /home/test_user/cam_sima/test/build \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt new file mode 100644 index 00000000..e220ee79 --- /dev/null +++ b/test/unit/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(physics) \ No newline at end of file diff --git a/test/unit/physics/CMakeLists.txt b/test/unit/physics/CMakeLists.txt new file mode 100644 index 00000000..47202fc6 --- /dev/null +++ b/test/unit/physics/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(utils) \ No newline at end of file diff --git a/test/unit/physics/utils/CMakeLists.txt b/test/unit/physics/utils/CMakeLists.txt new file mode 100644 index 00000000..abf72100 --- /dev/null +++ b/test/unit/physics/utils/CMakeLists.txt @@ -0,0 +1,17 @@ +# Test orbital_data module +add_executable(test_physics_utils_orbital_data test_orbital_data.F90 test_orbital_data_stubs.F90) + +target_sources(test_physics_utils_orbital_data + PUBLIC + ${CAM_SIMA_SRC_PATH}/physics/utils/orbital_data.F90 +) + +target_compile_options(test_physics_utils_orbital_data PRIVATE -ffree-line-length-none) + +add_test( + NAME test_physics_utils_orbital_data + COMMAND $ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} +) + +add_memory_check_test(test_physics_utils_orbital_data $ "" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) \ No newline at end of file diff --git a/test/unit/physics/utils/test_orbital_data.F90 b/test/unit/physics/utils/test_orbital_data.F90 new file mode 100644 index 00000000..d04c5573 --- /dev/null +++ b/test/unit/physics/utils/test_orbital_data.F90 @@ -0,0 +1,57 @@ +! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research +! SPDX-License-Identifier: Apache-2.0 +!-------------------------------------------------------------------------- +! +! Tests of the orbital_data module. +! +!-------------------------------------------------------------------------- + +! Assert macros +#define ASSERT(x) if (.not.(x)) then; write(*,*) "Assertion failed[", __FILE__, ":", __LINE__, "]: x"; stop 1; endif +#define ASSERT_NEAR( a, b, abs_error ) if( (abs(a - b) >= abs_error) .and. (abs(a - b) /= 0.0) ) then; write(*,*) "Assertion failed[", __FILE__, ":", __LINE__, "]: a, b"; stop 1; endif + +program test_orbital_data + + implicit none + + call test_orbital_data_functions() + +contains + + subroutine test_orbital_data_functions + + use orbital_data + use shr_kind_mod, only: R8 => SHR_KIND_R8 + implicit none + + integer, parameter :: NUMBER_OF_COLUMNS = 3 + real(kind=R8) :: calendar_day + real(kind=R8) :: latitudes(NUMBER_OF_COLUMNS) + real(kind=R8) :: longitudes(NUMBER_OF_COLUMNS) + integer :: i + + calendar_day = 10.5_R8 + latitudes = [1.0_R8, 2.0_R8, 3.0_R8] + longitudes = [4.0_R8, 5.0_R8, 6.0_R8] + + call orbital_data_init(NUMBER_OF_COLUMNS) + + ASSERT(allocated(solar_zenith_angle)) + ASSERT(size(solar_zenith_angle) == NUMBER_OF_COLUMNS) + ASSERT(all(solar_zenith_angle == -1.0_R8)) + ASSERT(solar_declination == -1.0_R8) + ASSERT(earth_sun_distance == -1.0_R8) + + call orbital_data_advance(calendar_day, latitudes, longitudes) + + ASSERT(allocated(solar_zenith_angle)) + ASSERT(size(solar_zenith_angle) == NUMBER_OF_COLUMNS) + ASSERT_NEAR(solar_zenith_angle(1), acos(latitudes(1) + longitudes(1) + calendar_day * 21.0_R8), 1.0E-6_R8) + ASSERT_NEAR(solar_zenith_angle(2), acos(latitudes(2) + longitudes(2) + calendar_day * 21.0_R8), 1.0E-6_R8) + ASSERT_NEAR(solar_zenith_angle(3), acos(latitudes(3) + longitudes(3) + calendar_day * 21.0_R8), 1.0E-6_R8) + ASSERT(solar_declination == calendar_day * 2.0_R8) + ASSERT(earth_sun_distance == calendar_day * 3.0_R8) + + end subroutine test_orbital_data_functions + +end program test_orbital_data \ No newline at end of file diff --git a/test/unit/physics/utils/test_orbital_data_stubs.F90 b/test/unit/physics/utils/test_orbital_data_stubs.F90 new file mode 100644 index 00000000..7822a140 --- /dev/null +++ b/test/unit/physics/utils/test_orbital_data_stubs.F90 @@ -0,0 +1,65 @@ +! Copyright (C) 2024 National Science Foundation-National Center for Atmospheric Research +! SPDX-License-Identifier: Apache-2.0 +!-------------------------------------------------------------------------- +! +! Stub modules for the orbital_data module tests. +! +!-------------------------------------------------------------------------- + +module shr_kind_mod + implicit none + integer, parameter :: SHR_KIND_R8 = selected_real_kind(15) +end module shr_kind_mod +module ccpp_kinds + implicit none + integer, parameter :: kind_phys = selected_real_kind(15) +end module ccpp_kinds +module cam_abortutils + implicit none + public :: check_allocate +contains + subroutine check_allocate(error_code, subroutine_name, variable_name, file, line) + integer, intent(in) :: error_code + character(len=*), intent(in) :: subroutine_name + character(len=*), intent(in) :: variable_name + character(len=*), intent(in) :: file + integer, intent(in) :: line + if (error_code /= 0) then + write(*,*) "Allocation of '", variable_name, "' failed with code ", error_code, " in ", subroutine_name, " at ", file, ":", line + stop 3 + end if + end subroutine check_allocate +end module cam_abortutils +module shr_orb_mod + use shr_kind_mod, only: R8 => SHR_KIND_R8 + implicit none + real(kind=R8), parameter :: SHR_ORB_UNDEF_REAL = -1.0_R8 + public :: shr_orb_decl, shr_orb_cosz +contains + subroutine shr_orb_decl(calendar_day, eccen, mvelpp, lambm0, obliqr, solar_declination, earth_sun_distance) + real(kind=R8), intent(in) :: calendar_day + real(kind=R8), intent(in) :: eccen + real(kind=R8), intent(in) :: mvelpp + real(kind=R8), intent(in) :: lambm0 + real(kind=R8), intent(in) :: obliqr + real(kind=R8), intent(out) :: solar_declination + real(kind=R8), intent(out) :: earth_sun_distance + solar_declination = calendar_day * 2.0 + earth_sun_distance = calendar_day * 3.0 + end subroutine shr_orb_decl + real(R8) pure function shr_orb_cosz(calendar_day, latitude, longitude, solar_declination) + real(kind=R8), intent(in) :: calendar_day + real(kind=R8), intent(in) :: latitude + real(kind=R8), intent(in) :: longitude + real(kind=R8), intent(in) :: solar_declination + shr_orb_cosz = latitude + longitude + solar_declination + end function shr_orb_cosz +end module shr_orb_mod +module cam_control_mod + use shr_kind_mod, only: R8 => SHR_KIND_R8 + implicit none + real(kind=R8) :: eccen = 2.0_R8 + real(kind=R8) :: mvelpp = 3.0_R8 + real(kind=R8) :: lambm0 = 4.0_R8 + real(kind=R8) :: obliqr = 5.0_R8 +end module cam_control_mod \ No newline at end of file diff --git a/test/valgrind.supp b/test/valgrind.supp new file mode 100644 index 00000000..ee1ba85e --- /dev/null +++ b/test/valgrind.supp @@ -0,0 +1,81 @@ +############################################################## +# +# MUSICA TUV-x suppressions +# +# TODO(jiwon) We are experiencing memory leak issues in certain +# functions of TUV-x. It appears that these leaks occur only +# occasionally during initialization. We believe it’s acceptable +# to add a Valgrind suppression for now, and we will investigate +# further if it becomes a significant concern. +# +############################################################## +{ + Suppress_MUSICA_TUV-x_Leak1 + Memcheck:Leak + fun:malloc + fun:__musica_config_MOD_get_string + fun:__tuvx_radiator_aerosol_MOD_constructor + fun:__tuvx_radiator_factory_MOD_radiator_builder + fun:__tuvx_radiator_warehouse_MOD_constructor + fun:__tuvx_radiative_transfer_MOD_constructor + fun:__tuvx_core_MOD_constructor + fun:InternalCreateTuvx + ... +} +{ + Suppress_MUSICA_TUV-x_Leak2 + Memcheck:Leak + fun:malloc + fun:__musica_config_MOD_get_string + fun:__tuvx_radiator_MOD_base_constructor + fun:__tuvx_radiator_MOD_constructor + fun:__tuvx_radiator_factory_MOD_radiator_builder + fun:__tuvx_radiator_warehouse_MOD_constructor + fun:__tuvx_radiative_transfer_MOD_constructor + fun:__tuvx_core_MOD_constructor + fun:InternalCreateTuvx + ... +} +{ + Suppress_MUSICA_TUV-x_CreateRadiator + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:__musica_string_MOD_string_assign_char + fun:__tuvx_radiator_from_host_MOD_constructor_char + fun:__tuvx_radiator_from_host_MOD_constructor_string + fun:InternalCreateRadiator + ... +} +{ + Suppress_MUSICA_TUV-x_AddRadiator + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:__tuvx_radiator_from_host_MOD___copy_tuvx_radiator_from_host_Radiator_from_host_t + fun:__tuvx_radiator_warehouse_MOD_add_radiator + fun:InternalAddRadiator + ... +} +{ + Suppress_MUSICA_TUV-x_GetRadiator + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:__tuvx_radiator_from_host_MOD___copy_tuvx_radiator_from_host_Radiator_from_host_t + fun:InternalGetRadiator + ... +} +{ + Suppress_MUSICA_TUV-x_CreateTuvx-RadiatorFromHost + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:__tuvx_radiator_from_host_MOD___copy_tuvx_radiator_from_host_Radiator_from_host_t + fun:__tuvx_radiator_warehouse_MOD_add_radiator + fun:__tuvx_radiator_warehouse_MOD_add_radiators + fun:__tuvx_radiative_transfer_MOD_constructor + fun:__tuvx_core_MOD_constructor + fun:InternalCreateTuvx + ... +} \ No newline at end of file