diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index b26d266..5d7bf4a 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -3,7 +3,7 @@ name: build on: [pull_request,workflow_dispatch] jobs: - test_micm_api: + test_musica_api: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/doc/ChangeLog b/doc/ChangeLog index 426c8f9..42af014 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,52 @@ =============================================================== +Tag name: +Originator(s): mattldawson +Date: 29 May 2024 +One-line Summary: Add dynamic species from MICM +Github PR URL: https://github.com/ESCOMP/atmospheric_physics/pull/93 + +This PR fixes the following NCAR/atmospheric_physics Github issues: 74 + +Code reviewed by: cacraigucar, k20shores, boulderdaze, peverwhee, mwaxmonsky, nusbaume + +List all existing files that have been added (A), modified (M), or deleted (D), +and describe the changes: + +M .github/workflows/test.yaml +M doc/ChangeLog +M musica/micm/micm.F90 +A musica/musica.F90 +M musica/micm/micm.meta -> musica/musica.meta +M musica/micm/micm_namelist.xml -> musica/musica_namelist.xml +A musica/util.F90 +M suite_musica.xml +M test/CMakeLists.txt +M test/cmake/SetDefaults.cmake +A test/cmake/TestUtils.cmake +M test/docker/Dockerfile.musica +M test/musica/micm/CMakeLists.txt -> test/musica/CMakeLists.txt +M test/musica/micm/configs/chapman/reactions.json +M test/musica/micm/configs/chapman/species.json +D test/musica/micm/test_micm_api.F90 +A test/musica/musica_namelist.F90 +A test/musica/test_musica_api.F90 + +Registers constituents and sets needed constituent properties from MICM configuration. + +Additionally: + +- returns error codes and messages from MUSICA functions using new error handling in MICM +- creates a musica_ccpp module to act as a the CCPP interface and moves existing functionality to a musica_ccpp_micm module in preparation for adding TUV-x (we will use musica_ccpp_ as a prefix for all the MUSICA modules in this repo to prevent naming conflicts with MUSICA Fortran library modules) +- updates build scripts for changes to MUSICA and CCPP framework +- adds memory checking to MUSICA tests + +List and Describe any test failures: none + +Summarize any changes to answers: none + +=============================================================== + Tag name:atmos_phys0_02_005 Originator(s): kshores Date: 26 Mar 2024 diff --git a/musica/micm/micm.F90 b/musica/micm/micm.F90 index 988df9d..875c4d6 100644 --- a/musica/micm/micm.F90 +++ b/musica/micm/micm.F90 @@ -1,48 +1,91 @@ -module micm +module musica_ccpp_micm use iso_c_binding ! Note: "micm_core" is included in an external pre-built MICM library that the host ! model is responsible for linking to during compilation - use micm_core, only: micm_t + use musica_micm, only: micm_t + use musica_ccpp_util, only: has_error_occurred use ccpp_kinds, only: kind_phys + use musica_namelist, only: filename_of_micm_configuration implicit none + private - public :: micm_init, micm_run, micm_final - private :: convert_to_mol_per_cubic_meter, convert_to_mass_mixing_ratio + public :: micm_register, micm_init, micm_run, micm_final - type(micm_t), pointer :: micm_obj + type(micm_t), pointer :: micm => null( ) contains - !> \section arg_table_micm_init Argument Table - !! \htmlinclude micm_init.html - subroutine micm_init(config_path, iulog, errcode, errmsg) - character(len=*), intent(in) :: config_path - integer, intent(in) :: iulog + !> Register MICM constituents with the CCPP + subroutine micm_register(constituents, errcode, errmsg) + use ccpp_constituent_prop_mod, only: ccpp_constituent_properties_t + use musica_util, only: error_t, mapping_t + type(ccpp_constituent_properties_t), allocatable, intent(out) :: constituents(:) integer, intent(out) :: errcode character(len=512), intent(out) :: errmsg + type(error_t) :: error + type(mapping_t) :: mapping + real(kind=kind_phys) :: molar_mass + logical :: is_advected + integer :: i + errcode = 0 errmsg = '' - micm_obj => micm_t(config_path, errcode) + micm => micm_t(filename_of_micm_configuration, error) + if (has_error_occurred(error, errcode, errmsg)) return + allocate(constituents(size(micm%species_ordering)), stat=errcode) if (errcode /= 0) then - errmsg = "[fatal] [micm] Failed to create MICM solver. Parsing configuration failed. & - Please look over at MICM log file for further information." + errmsg = "[MUSICA Error] Failed to allocate memory for constituents." return - endif + end if + + do i = 1, size(micm%species_ordering) + associate( map => micm%species_ordering(i) ) + molar_mass = micm%get_species_property_double(map%name(), & + "molecular weight [kg mol-1]", & + error) + if (has_error_occurred(error, errcode, errmsg)) return + is_advected = micm%get_species_property_bool(map%name(), & + "__is advected", & + error) + if (has_error_occurred(error, errcode, errmsg)) return + + call constituents(map%index())%instantiate( & + std_name = map%name(), & + long_name = map%name(), & + units = 'kg kg-1', & + vertical_dim = 'vertical_layer_dimension', & + default_value = 0.0_kind_phys, & + min_value = 0.0_kind_phys, & + molar_mass = molar_mass, & + advected = is_advected, & + errcode = errcode, & + errmsg = errmsg) + if (errcode /= 0) return + end associate ! map + end do - write(iulog,*) "[info] [micm] Created MICM solver." + end subroutine micm_register + + !> Intitialize MICM + subroutine micm_init(errcode, errmsg) + integer, intent(out) :: errcode + character(len=512), intent(out) :: errmsg + + errcode = 0 + errmsg = '' end subroutine micm_init - !> \section arg_table_micm_run Argument Table - !! \htmlinclude micm_run.html + !> Solve chemistry at the current time step subroutine micm_run(time_step, temperature, pressure, dry_air_density, constituent_props, & - constituents, iulog, errcode, errmsg) + constituents, errcode, errmsg) use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use musica_util, only: error_t real(kind_phys), intent(in) :: time_step ! s real(kind_phys), intent(in) :: temperature(:,:) ! K @@ -50,7 +93,6 @@ subroutine micm_run(time_step, temperature, pressure, dry_air_density, constitue real(kind_phys), intent(in) :: dry_air_density(:,:) ! kg m-3 type(ccpp_constituent_prop_ptr_t), intent(in) :: constituent_props(:) real(kind_phys), intent(inout) :: constituents(:,:,:) ! kg kg-1 - integer, intent(in) :: iulog integer, intent(out) :: errcode character(len=512), intent(out) :: errmsg @@ -63,8 +105,12 @@ subroutine micm_run(time_step, temperature, pressure, dry_air_density, constitue real(c_double), dimension(size(constituents, dim=1), & size(constituents, dim=2), & size(constituents, dim=3)) :: c_constituents + real(c_double), dimension(size(constituents, dim=1), & + size(constituents, dim=2), & + 0) :: c_rate_params real(kind_phys), dimension(size(constituents, dim=3)) :: molar_mass_arr ! kg mol-1 + type(error_t) :: error integer :: num_columns, num_layers, num_constituents integer :: i_column, i_layer, i_elem @@ -104,17 +150,15 @@ subroutine micm_run(time_step, temperature, pressure, dry_air_density, constitue c_pressure = real(pressure, c_double) c_constituents = real(constituents, c_double) - write(iulog,*) "[info] [micm] Running MICM solver..." - do i_column = 1, num_columns do i_layer = 1, num_layers - call micm_obj%solve(c_temperature(i_column, i_layer), c_pressure(i_column, i_layer), & - c_time_step, num_constituents, c_constituents(i_column, i_layer, :)) + call micm%solve(c_temperature(i_column, i_layer), c_pressure(i_column, i_layer), & + c_time_step, num_constituents, c_constituents(i_column, i_layer, :), & + 0, c_rate_params(i_column, i_layer, :), error) + if (has_error_occurred(error, errcode, errmsg)) return end do end do - write(iulog,*) "[info] [micm] MICM solver has finished." - constituents = real(c_constituents, kind_phys) ! Convert MICM unit back to CAM-SIMA unit (mol m-3 -> kg kg-1) @@ -122,18 +166,14 @@ subroutine micm_run(time_step, temperature, pressure, dry_air_density, constitue end subroutine micm_run - !> \section arg_table_micm_final Argument Table - !! \htmlinclude micm_final.html - subroutine micm_final(iulog, errcode, errmsg) - integer, intent(in) :: iulog + !> Finalize MICM + subroutine micm_final(errcode, errmsg) integer, intent(out) :: errcode character(len=512), intent(out) :: errmsg errcode = 0 errmsg = '' - write(iulog,*) "[debug] [micm] Deallocating MICM object..." - end subroutine micm_final ! Convert CAM-SIMA unit to MICM unit (kg kg-1 -> mol m-3) @@ -190,4 +230,4 @@ subroutine convert_to_mass_mixing_ratio(dry_air_density, molar_mass_arr, constit end subroutine convert_to_mass_mixing_ratio -end module micm \ No newline at end of file +end module musica_ccpp_micm diff --git a/musica/musica.F90 b/musica/musica.F90 new file mode 100644 index 0000000..de87dfa --- /dev/null +++ b/musica/musica.F90 @@ -0,0 +1,60 @@ +!> Top-level wrapper for MUSICA chemistry components +module musica_ccpp + use musica_ccpp_micm, only : micm_register, micm_init, micm_run, micm_final + + implicit none + private + + public :: musica_register, musica_init, musica_run, musica_final + +contains + + subroutine musica_register(constituents, errcode, errmsg) + use ccpp_constituent_prop_mod, only : ccpp_constituent_properties_t + type(ccpp_constituent_properties_t), allocatable, intent(out) :: constituents(:) + integer, intent(out) :: errcode + character(len=512), intent(out) :: errmsg + + call micm_register(constituents, errcode, errmsg) + end subroutine musica_register + + !> \section arg_table_musica_init Argument Table + !! \htmlinclude musica_init.html + subroutine musica_init(errcode, errmsg) + integer, intent(out) :: errcode + character(len=512), intent(out) :: errmsg + + call micm_init(errcode, errmsg) + end subroutine musica_init + + !> \section arg_table_musica_run Argument Table + !! \htmlinclude musica_run.html + subroutine musica_run(time_step, temperature, pressure, dry_air_density, constituent_props, & + constituents, errcode, errmsg) + use ccpp_kinds, only: kind_phys + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + + real(kind_phys), intent(in) :: time_step ! s + real(kind_phys), intent(in) :: temperature(:,:) ! K + real(kind_phys), intent(in) :: pressure(:,:) ! Pa + real(kind_phys), intent(in) :: dry_air_density(:,:) ! kg m-3 + type(ccpp_constituent_prop_ptr_t), intent(in) :: constituent_props(:) + real(kind_phys), intent(inout) :: constituents(:,:,:) ! kg kg-1 + integer, intent(out) :: errcode + character(len=512), intent(out) :: errmsg + + call micm_run(time_step, temperature, pressure, dry_air_density, constituent_props, & + constituents, errcode, errmsg) + + end subroutine musica_run + + !> \section arg_table_musica_final Argument Table + !! \htmlinclude musica_final.html + subroutine musica_final(errcode, errmsg) + integer, intent(out) :: errcode + character(len=512), intent(out) :: errmsg + + call micm_final(errcode, errmsg) + end subroutine musica_final + +end module musica_ccpp diff --git a/musica/micm/micm.meta b/musica/musica.meta similarity index 77% rename from musica/micm/micm.meta rename to musica/musica.meta index a54f407..fa6632e 100644 --- a/musica/micm/micm.meta +++ b/musica/musica.meta @@ -1,22 +1,11 @@ [ccpp-table-properties] - name = micm + name = musica type = scheme + dynamic_constituent_routine = musica_register [ccpp-arg-table] - name = micm_init + name = musica_init type = scheme -[ config_path ] - standard_name = filename_of_micm_configuration - units = none - type = character | kind = len=* - dimensions = () - intent = in -[ iulog ] - standard_name = log_output_unit - units = 1 - type = integer - dimensions = () - intent = in [ errcode ] standard_name = ccpp_error_code units = 1 @@ -31,7 +20,7 @@ intent = out [ccpp-arg-table] - name = micm_run + name = musica_run type = scheme [ time_step ] standard_name = timestep_for_physics @@ -59,22 +48,16 @@ intent = in [ constituent_props ] standard_name = ccpp_constituent_properties - units = none + units = None type = ccpp_constituent_prop_ptr_t dimensions = (number_of_ccpp_constituents) intent = in [ constituents ] standard_name = ccpp_constituents - units = kg kg-1 + units = none type = real | kind = kind_phys dimensions = (horizontal_loop_extent,vertical_layer_dimension,number_of_ccpp_constituents) intent = inout -[ iulog ] - standard_name = log_output_unit - units = 1 - type = integer - dimensions = () - intent = in [ errcode ] standard_name = ccpp_error_code units = 1 @@ -89,14 +72,8 @@ intent = out [ccpp-arg-table] - name = micm_final + name = musica_final type = scheme -[ iulog ] - standard_name = log_output_unit - units = 1 - type = integer - dimensions = () - intent = in [ errcode ] standard_name = ccpp_error_code units = 1 diff --git a/musica/micm/micm_namelist.xml b/musica/musica_namelist.xml similarity index 98% rename from musica/micm/micm_namelist.xml rename to musica/musica_namelist.xml index 3eddc98..aaae358 100644 --- a/musica/micm/micm_namelist.xml +++ b/musica/musica_namelist.xml @@ -75,12 +75,12 @@ units This is the CCPP unit specification of the variable (e.g., m s-1). --> - + char*512 musica - micm + musica filename_of_micm_configuration none diff --git a/musica/util.F90 b/musica/util.F90 new file mode 100644 index 0000000..b91a732 --- /dev/null +++ b/musica/util.F90 @@ -0,0 +1,38 @@ +! Copyright (C) 2024 National Center for Atmospheric Research, +! SPDX-License-Identifier: Apache-2.0 +module musica_ccpp_util + + implicit none + + private + public :: has_error_occurred + +contains + + !> @brief Evaluate a MUSICA error for failure and convert to CCPP error data + !> @param[in] error The error code to evaluate and convert. + !> @param[out] error_code The CCPP error code. + !> @param[out] error_message The CCPP error message. + !> @return True for an error, false for success. + logical function has_error_occurred(error, error_code, error_message) + use musica_util, only : error_t + type(error_t), intent(in) :: error + integer, intent(out) :: error_code + character(len=512), intent(out) :: error_message + + character(len=30) :: error_code_str + + if ( error%is_success( ) ) then + error_code = 0 + error_message = '' + has_error_occurred = .false. + return + end if + error_code = error%code( ) + write(error_code_str, '(I30)') error%code( ) + error_message = '[MUSICA Error]: ' // error%category( ) // '[' // & + trim( adjustl( error_code_str ) ) // ']: ' // error%message( ) + has_error_occurred = .true. + end function has_error_occurred + +end module musica_ccpp_util diff --git a/suite_musica.xml b/suite_musica.xml index 0502aac..c1f82b1 100644 --- a/suite_musica.xml +++ b/suite_musica.xml @@ -3,6 +3,6 @@ calc_dry_air_ideal_gas_density - micm + musica diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5b02cc8..369d526 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -12,20 +12,24 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) # -------------------------------------------------------------------------------- # NOTE: If 'CCPP_ENABLE_MUSICA_TESTS' on, this is not a stand-alone cmake project anymore. -# MICM CCPP wrapper needs MUSICA library and ccpp-framework/src. +# MUSICA CCPP wrapper needs MUSICA library and ccpp-framework/src. # To 'CCPP_ENABLE_MUSICA_TESTS', you either build a cmake project through # 'docker/Dockerfile.musica' or follow the build instructions in the file. # The following '$ENV' variables are set by the docker file. # -------------------------------------------------------------------------------- -option(CCPP_ENABLE_MUSICA_TESTS "Build the micm test" OFF) +option(CCPP_ENABLE_MUSICA_TESTS "Build the MUSICA tests" OFF) +option(CCPP_ENABLE_MEMCHECK "Enable memory checks in tests" OFF) + +set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) +set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) if (CCPP_ENABLE_MUSICA_TESTS) set(MUSICA_VERSION $ENV{MUSICA_VERSION}) - set(MICM_SRC_PATH ${CMAKE_SOURCE_DIR}/../musica/micm) + set(MUSICA_SRC_PATH ${CMAKE_SOURCE_DIR}/../musica) set(CCPP_SRC_PATH ${CMAKE_SOURCE_DIR}/$ENV{CCPP_SRC_PATH}) set(CCPP_TEST_SRC_PATH ${CMAKE_SOURCE_DIR}/include) - add_subdirectory(musica/micm) + add_subdirectory(musica) enable_testing() -endif() \ No newline at end of file +endif() diff --git a/test/cmake/SetDefaults.cmake b/test/cmake/SetDefaults.cmake index d39f7b1..39e5d1d 100644 --- a/test/cmake/SetDefaults.cmake +++ b/test/cmake/SetDefaults.cmake @@ -1,4 +1,4 @@ # Overwrite the init values choosen by CMake if (CMAKE_Fortran_COMPILER_ID MATCHES "GNU") set(CMAKE_Fortran_FLAGS_DEBUG_INIT "-g") -endif() \ No newline at end of file +endif() diff --git a/test/cmake/TestUtils.cmake b/test/cmake/TestUtils.cmake new file mode 100644 index 0000000..3f887db --- /dev/null +++ b/test/cmake/TestUtils.cmake @@ -0,0 +1,20 @@ +################################################################################ +# Utility functions for creating tests + +if(CCPP_ENABLE_MEMCHECK) + find_program(MEMORYCHECK_COMMAND "valgrind") +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 + ${test_binary} ${test_args} + WORKING_DIRECTORY ${working_dir}) + endif() +endfunction(add_memory_check_test) + +################################################################################ diff --git a/test/docker/Dockerfile.musica b/test/docker/Dockerfile.musica index 7d14fc1..7b6f49f 100644 --- a/test/docker/Dockerfile.musica +++ b/test/docker/Dockerfile.musica @@ -1,7 +1,16 @@ FROM ubuntu:22.04 RUN apt update \ - && apt -y install \ + && apt install -y sudo \ + && adduser 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 \ curl \ @@ -9,49 +18,62 @@ RUN apt update \ gcc \ gfortran \ git \ + libblas-dev \ + liblapack-dev \ lcov \ libcurl4-openssl-dev \ libhdf5-dev \ libnetcdff-dev \ + libopenmpi-dev \ m4 \ make \ nlohmann-json3-dev \ + openmpi-bin \ + python3 \ tree \ valgrind \ vim \ zlib1g-dev \ - && apt clean + && sudo apt clean + +ENV PATH="${PATH}:/usr/lib/openmpi/bin" -ENV FC=gfortran +ENV FC=mpif90 ENV FFLAGS="-I/usr/include/" # Install MUSICA (MUSICA-C) RUN git clone https://github.com/NCAR/musica.git RUN cd musica \ && git fetch \ - && git checkout v0.6.0 \ + && git checkout 2a5eeaac982a3eb80b96d1e2087b91b301d1e748 \ && cmake \ -S . \ -B build \ + -D CMAKE_BUILD_TYPE=Release \ -D MUSICA_ENABLE_TESTS=OFF \ + -D MUSICA_BUILD_FORTRAN_INTERFACE=OFF \ + -D MUSICA_ENABLE_MICM=ON \ + -D MUSICA_ENABLE_TUVX=ON \ && cd build \ - && make install + && sudo make install COPY . atmospheric_physics +RUN sudo chown -R test_user:test_user atmospheric_physics RUN cd atmospheric_physics/test \ && mkdir lib \ && cd lib \ - && git clone -b CPF_0.2.051 --depth 1 https://github.com/peverwhee/ccpp-framework.git + && git clone -b add_const_interface --depth 1 https://github.com/peverwhee/ccpp-framework.git ENV CCPP_SRC_PATH="lib/ccpp-framework/src" RUN cd atmospheric_physics/test \ - && cmake -S. \ + && cmake -S . \ -B build \ -D CCPP_ENABLE_MUSICA_TESTS=ON \ + -D CCPP_ENABLE_MEMCHECK=ON \ && cmake --build ./build RUN cd atmospheric_physics \ && cp -r test/musica/micm/configs/chapman ./test/build/chapman -WORKDIR atmospheric_physics/test/build \ No newline at end of file +WORKDIR /home/test_user/atmospheric_physics/test/build diff --git a/test/musica/micm/CMakeLists.txt b/test/musica/CMakeLists.txt similarity index 54% rename from test/musica/micm/CMakeLists.txt rename to test/musica/CMakeLists.txt index 487041b..1391a29 100644 --- a/test/musica/micm/CMakeLists.txt +++ b/test/musica/CMakeLists.txt @@ -1,30 +1,30 @@ -find_package(musica ${MUSICA_VERSION} REQUIRED) -message(STATUS " MUSICA version ${MUSICA_VERSION}") - include(FetchContent) +include(TestUtils) FetchContent_Declare(musica GIT_REPOSITORY https://github.com/NCAR/musica.git - GIT_TAG v0.6.1 +# GIT_TAG v0.6.1 + GIT_TAG 2a5eeaac982a3eb80b96d1e2087b91b301d1e748 ) -set(MUSICA_ENABLE_TESTS OFF) set(MUSICA_BUILD_C_CXX_INTERFACE OFF) set(MUSICA_BUILD_FORTRAN_INTERFACE ON) -set(MUSICA_ENABLE_TUVX OFF) +set(MUSICA_ENABLE_TESTS OFF) +set(MUSICA_ENABLE_INSTALL OFF) FetchContent_MakeAvailable(musica) # --------------------------------------------------------- -# Create a test for MICM CCPP wrapper +# Create a test for MUSICA CCPP wrapper # --------------------------------------------------------- -add_executable(test_micm_api test_micm_api.F90) +add_executable(test_musica_api test_musica_api.F90 musica_namelist.F90) -target_sources(test_micm_api +target_sources(test_musica_api PUBLIC - ${MICM_SRC_PATH}/micm.F90 - ${CCPP_SRC_PATH}/ccpp_api.F90 + ${MUSICA_SRC_PATH}/micm/micm.F90 + ${MUSICA_SRC_PATH}/musica.F90 + ${MUSICA_SRC_PATH}/util.F90 ${CCPP_SRC_PATH}/ccpp_constituent_prop_mod.F90 ${CCPP_SRC_PATH}/ccpp_hash_table.F90 ${CCPP_SRC_PATH}/ccpp_hashable.F90 @@ -32,12 +32,12 @@ target_sources(test_micm_api ${CCPP_TEST_SRC_PATH}/ccpp_kinds.F90 ) -target_link_libraries(test_micm_api +target_link_libraries(test_musica_api PRIVATE musica::musica-fortran ) -set_target_properties(test_micm_api +set_target_properties(test_musica_api PROPERTIES LINKER_LANGUAGE Fortran ) @@ -45,7 +45,9 @@ set_target_properties(test_micm_api include(CTest) add_test( - NAME test_micm_api - COMMAND $ + NAME test_musica_api + COMMAND $ WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -) \ No newline at end of file +) + +add_memory_check_test(test_musica_api $ "" ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) diff --git a/test/musica/micm/configs/chapman/reactions.json b/test/musica/micm/configs/chapman/reactions.json index 679593f..f37fd20 100644 --- a/test/musica/micm/configs/chapman/reactions.json +++ b/test/musica/micm/configs/chapman/reactions.json @@ -94,4 +94,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/test/musica/micm/configs/chapman/species.json b/test/musica/micm/configs/chapman/species.json index c962907..07d77d2 100644 --- a/test/musica/micm/configs/chapman/species.json +++ b/test/musica/micm/configs/chapman/species.json @@ -3,27 +3,37 @@ { "name": "M", "type": "CHEM_SPEC", - "tracer type": "CONSTANT" + "tracer type": "CONSTANT", + "molecular weight [kg mol-1]": 0.029, + "__is advected": false }, { "name": "O2", "type": "CHEM_SPEC", - "tracer type": "CONSTANT" + "tracer type": "CONSTANT", + "molecular weight [kg mol-1]": 0.032, + "__is advected": false }, { "name": "O", "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 + "absolute tolerance": 1e-12, + "molecular weight [kg mol-1]": 0.016, + "__is advected": false }, { "name": "O1D", "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 + "absolute tolerance": 1e-12, + "molecular weight [kg mol-1]": 0.016, + "__is advected": false }, { "name": "O3", "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 + "absolute tolerance": 1e-12, + "molecular weight [kg mol-1]": 0.048, + "__is advected": true } ] -} \ No newline at end of file +} diff --git a/test/musica/micm/test_micm_api.F90 b/test/musica/micm/test_micm_api.F90 deleted file mode 100644 index c121a06..0000000 --- a/test/musica/micm/test_micm_api.F90 +++ /dev/null @@ -1,131 +0,0 @@ -subroutine test_micm_ccpp_api() - use iso_c_binding - use micm - use ccpp_kinds, only: kind_phys - use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t - use ccpp_constituent_prop_mod, only: ccpp_constituent_properties_t - - implicit none - - real(kind_phys) :: time_step ! s - real(kind_phys), dimension(2,1) :: temperature ! K - real(kind_phys), dimension(2,1) :: pressure ! Pa - real(kind_phys), dimension(2,1) :: dry_air_density ! kg m-3 - type(ccpp_constituent_prop_ptr_t), dimension(5) :: constituent_props_ptr - real(kind_phys), dimension(5) :: molar_mass_arr ! kg mol-1 - real(kind_phys), dimension(2,1,5) :: constituents ! kg kg-1 - integer :: iulog - integer :: errcode - character(len=512) :: errmsg - - ! local variables - type(ccpp_constituent_properties_t), dimension(5), target :: constituent_props - type(ccpp_constituent_properties_t), pointer :: const_prop - integer :: i - - time_step = 60._kind_phys - - temperature(1,1) = 206.6374207_kind_phys - temperature(2,1) = 206.6374207_kind_phys - - pressure(1,1) = 6152.049805_kind_phys - pressure(2,1) = 6152.049805_kind_phys - - dry_air_density(1,1) = 3.580_kind_phys - dry_air_density(2,1) = 3.580_kind_phys - - molar_mass_arr = (/ 200._kind_phys, 200._kind_phys, 200._kind_phys, 200._kind_phys, 200._kind_phys /) - - constituents(1,1,1:5) = (/ 0.75_kind_phys, 8.1e-6_kind_phys, 2.42e-17_kind_phys, & - 1.15e-5_kind_phys, 6.61e-9_kind_phys /) - constituents(2,1,1:5) = (/ 0.75_kind_phys, 8.1e-6_kind_phys, 2.42e-17_kind_phys, & - 1.15e-5_kind_phys, 6.61e-9_kind_phys /) - - iulog = 6 - - call constituent_props(1)%instantiate( & - std_name="water_vapor_mixing_ratio", & - long_name="water_vapor_mixing_ratio", & - units="kg kg-1", & - default_value=0._kind_phys, & - vertical_dim="vertical_layer_dimension", & - advected=.true., & - molar_mass=molar_mass_arr(1), & - errcode=errcode, errmsg=errmsg) - call constituent_props(2)%instantiate( & - std_name="water_vapor_mixing_ratio_a", & - long_name="water_vapor_mixing_ratio", & - units="kg kg-1", & - default_value=0._kind_phys, & - vertical_dim="vertical_layer_dimension", & - advected=.true., & - molar_mass=molar_mass_arr(2), & - errcode=errcode, errmsg=errmsg) - call constituent_props(3)%instantiate( & - std_name="water_vapor_mixing_ratio_b", & - long_name="water_vapor_mixing_ratio", & - units="kg kg-1", & - default_value=0._kind_phys, & - vertical_dim="vertical_layer_dimension", & - advected=.true., & - molar_mass=molar_mass_arr(3), & - errcode=errcode, errmsg=errmsg) - call constituent_props(4)%instantiate( & - std_name="water_vapor_mixing_ratio_c", & - long_name="water_vapor_mixing_ratio", & - units="kg kg-1", & - default_value=0._kind_phys, & - vertical_dim="vertical_layer_dimension", & - advected=.true., & - molar_mass=molar_mass_arr(4), & - errcode=errcode, errmsg=errmsg) - call constituent_props(5)%instantiate( & - std_name="water_vapor_mixing_ratio_d", & - long_name="water_vapor_mixing_ratio", & - units="kg kg-1", & - default_value=0._kind_phys, & - vertical_dim="vertical_layer_dimension", & - advected=.true., & - molar_mass=molar_mass_arr(5), & - errcode=errcode, errmsg=errmsg) - - do i = 1, 5 - const_prop => constituent_props(i) - call constituent_props_ptr(i)%set(const_prop, errcode, errmsg) - end do - - call micm_init("chapman", iulog, errcode, errmsg) - - if (errcode /= 0) then - write(*,*) trim(errmsg) - stop 3 - endif - - write(*,*) " -- Initial time_step", time_step - write(*,*) " -- Initial temp", temperature - write(*,*) " -- Initial pressure", pressure - write(*,*) " -- Initial concentrations", constituents - - call micm_run(time_step, temperature, pressure, dry_air_density, constituent_props_ptr, & - constituents, iulog, errcode, errmsg) - - if (errcode /= 0) then - write(*,*) trim(errmsg) - stop 3 - endif - - write(*,*) " -- After solving, concentrations", constituents - - call micm_final(iulog, errcode, errmsg) - - if (errcode /= 0) then - write(*,*) trim(errmsg) - stop 3 - endif - -end subroutine test_micm_ccpp_api - -program run_test_micm_ccpp -implicit none - call test_micm_ccpp_api() -end program \ No newline at end of file diff --git a/test/musica/musica_namelist.F90 b/test/musica/musica_namelist.F90 new file mode 100644 index 0000000..2b3da0a --- /dev/null +++ b/test/musica/musica_namelist.F90 @@ -0,0 +1,11 @@ +! Stub for auto-generated MUSICA namelist module +module musica_namelist + + implicit none + + private + public :: filename_of_micm_configuration + + character(len=*), parameter :: filename_of_micm_configuration = 'chapman' + +end module musica_namelist diff --git a/test/musica/test_musica_api.F90 b/test/musica/test_musica_api.F90 new file mode 100644 index 0000000..43f101f --- /dev/null +++ b/test/musica/test_musica_api.F90 @@ -0,0 +1,116 @@ +subroutine test_musica_ccpp_api() + +#define ASSERT(x) if (.not.(x)) then; write(*,*) "Assertion failed[", __FILE__, ":", __LINE__, "]: x"; stop 1; endif + + use iso_c_binding + use musica_ccpp + use ccpp_kinds, only: kind_phys + use ccpp_constituent_prop_mod, only: ccpp_constituent_prop_ptr_t + use ccpp_constituent_prop_mod, only: ccpp_constituent_properties_t + + implicit none + + real(kind_phys) :: time_step ! s + real(kind_phys), dimension(2,1) :: temperature ! K + real(kind_phys), dimension(2,1) :: pressure ! Pa + real(kind_phys), dimension(2,1) :: dry_air_density ! kg m-3 + type(ccpp_constituent_prop_ptr_t), allocatable :: constituent_props_ptr(:) + real(kind_phys), dimension(5) :: molar_mass_arr ! kg mol-1 + real(kind_phys), dimension(2,1,5) :: constituents ! kg kg-1 + integer :: errcode + character(len=512) :: errmsg + + ! local variables + type(ccpp_constituent_properties_t), allocatable, target :: constituent_props(:) + type(ccpp_constituent_properties_t), pointer :: const_prop + integer :: i + character(len=512) :: species_name, units + logical :: tmp_bool, is_advected + real(kind_phys) :: molar_mass + + time_step = 60._kind_phys + + temperature(1,1) = 206.6374207_kind_phys + temperature(2,1) = 206.6374207_kind_phys + + pressure(1,1) = 6152.049805_kind_phys + pressure(2,1) = 6152.049805_kind_phys + + dry_air_density(1,1) = 3.580_kind_phys + dry_air_density(2,1) = 3.580_kind_phys + + molar_mass_arr = (/ 200._kind_phys, 200._kind_phys, 200._kind_phys, 200._kind_phys, 200._kind_phys /) + + constituents(1,1,1:5) = (/ 0.75_kind_phys, 8.1e-6_kind_phys, 2.42e-17_kind_phys, & + 1.15e-5_kind_phys, 6.61e-9_kind_phys /) + constituents(2,1,1:5) = (/ 0.75_kind_phys, 8.1e-6_kind_phys, 2.42e-17_kind_phys, & + 1.15e-5_kind_phys, 6.61e-9_kind_phys /) + + call musica_register(constituent_props, errcode, errmsg) + ASSERT(allocated(constituent_props)) + ASSERT(size(constituent_props) == 5) + do i = 1, size(constituent_props) + ASSERT(constituent_props(i)%is_instantiated(errcode, errmsg)) + ASSERT(errcode == 0) + call constituent_props(i)%standard_name(species_name, errcode, errmsg) + ASSERT(errcode == 0) + call constituent_props(i)%molar_mass(molar_mass, errcode, errmsg) + ASSERT(errcode == 0) + call constituent_props(i)%is_advected(is_advected, errcode, errmsg) + ASSERT(errcode == 0) + tmp_bool = (trim(species_name) == "M" .and. molar_mass == 0.029_kind_phys .and. .not. is_advected) .or. & + (trim(species_name) == "O2" .and. molar_mass == 0.032_kind_phys .and. .not. is_advected) .or. & + (trim(species_name) == "O" .and. molar_mass == 0.016_kind_phys .and. .not. is_advected) .or. & + (trim(species_name) == "O1D" .and. molar_mass == 0.016_kind_phys .and. .not. is_advected) .or. & + (trim(species_name) == "O3" .and. molar_mass == 0.048_kind_phys .and. is_advected) + ASSERT(tmp_bool) + call constituent_props(i)%units(units, errcode, errmsg) + ASSERT(errcode == 0) + ASSERT(trim(units) == 'kg kg-1') + end do + if (errcode /= 0) then + write(*,*) errcode, trim(errmsg) + stop 3 + end if + + allocate(constituent_props_ptr(size(constituent_props))) + do i = 1, size(constituent_props) + const_prop => constituent_props(i) + call constituent_props_ptr(i)%set(const_prop, errcode, errmsg) + end do + + call musica_init(errcode, errmsg) + + if (errcode /= 0) then + write(*,*) trim(errmsg) + stop 3 + endif + + write(*,*) " -- Initial time_step", time_step + write(*,*) " -- Initial temp", temperature + write(*,*) " -- Initial pressure", pressure + write(*,*) " -- Initial concentrations", constituents + + call musica_run(time_step, temperature, pressure, dry_air_density, constituent_props_ptr, & + constituents, errcode, errmsg) + + if (errcode /= 0) then + write(*,*) trim(errmsg) + stop 3 + endif + + write(*,*) " -- After solving, concentrations", constituents + + call musica_final(errcode, errmsg) + + if (errcode /= 0) then + write(*,*) trim(errmsg) + stop 3 + endif + +end subroutine test_musica_ccpp_api + +program run_test_musica_ccpp +implicit none + call test_musica_ccpp_api() +end program