diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000000..cdd8e8017e --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,488 @@ +cmake_minimum_required(VERSION 3.18) + +#[==============================================================================[ +# Basic project definition # +#]==============================================================================] +project(MOM6 + DESCRIPTION "Modular Ocean Model6" + HOMEPAGE_URL https://github.com/ACCESS-NRI/MOM6 + LANGUAGES C Fortran) + +#[==============================================================================[ +# Project configuration # +#]==============================================================================] +# Configurations to build +option(ACCESS3_MOM6 "Building MOM6 library with Access3share" ON) + +message(STATUS "Configuration") +message(STATUS " Building MOM6 library with Access3share " ${ACCESS3_MOM6}) + +# build options +option(ENABLE_MOM_SYMMETRIC "Use MOM symmetric memory" ON ) +option(CESMCOUPLED "Use CESMCOUPLED" OFF) + +if (CESMCOUPLED AND NOT ACCESS3_MOM6) + message(FATAL_ERROR "ENABLE_CESMCOUPLED can only be set if ACCESS3_MOM6 is ON.") +endif() + +message(STATUS "Build options") +message(STATUS "MOM_SYMMETRIC ${ENABLE_MOM_SYMMETRIC}" ) +message(STATUS "CESMCOUPLED ${CESMCOUPLED}" ) + +#[==============================================================================[ +# Project configuration # +#]==============================================================================] +# include libraries +list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}) +include(GNUInstallDirs) +include(FortranLib) +include(CompilerFlags) +include(CMakePackageConfigHelpers) + +# Precessor (macro) definitions +add_compile_definitions(_USE_GENERIC_TRACER _USE_MOM6_DIAG) + +if (CESMCOUPLED) + add_compile_definitions(CESMCOUPLED) +endif() + +## Fortran modules path; currently this is simply set to be the include dir +set(CMAKE_INSTALL_MODULEDIR ${CMAKE_INSTALL_INCLUDEDIR} + CACHE STRING + "Fortran module installation path (Not a cmake native variable)" +) + +#[==============================================================================[ +# External packages # +#]==============================================================================] +find_package(NetCDF REQUIRED Fortran) + +if (NOT NetCDF_PARALLEL) + message(FATAL_ERROR "NetCDF does not have parallel I/O support!") +endif() + +find_package(fms COMPONENTS R8 REQUIRED) + +if (ACCESS3_MOM6) + find_package(ESMF 8.3.0 MODULE REQUIRED) + find_package(Access3Share REQUIRED cdeps timing share nuopc_cap_share) +endif() + + +### Targets + +## MOM6 library + +# set paths for the source code and config source +set(SRC "${CMAKE_SOURCE_DIR}/../src") +set(CONFIG_SRC "${CMAKE_SOURCE_DIR}/../config_src") + +add_fortran_library(mom6lib mod STATIC) + +target_include_directories(mom6lib PRIVATE $) + +if(ENABLE_MOM_SYMMETRIC) + set(MOM_MEMORY_DIR $) +else() + set(MOM_MEMORY_DIR $) +endif() +target_include_directories(mom6lib PRIVATE ${MOM_MEMORY_DIR}) + +target_compile_options(mom6lib PRIVATE "$<$:${fortran_compile_flags}>") + +# link libraries +target_link_libraries(mom6lib PRIVATE FMS::fms_r8) + +if(ACCESS3_MOM6) +target_link_libraries(mom6lib + PUBLIC esmf + PRIVATE Access3::nuopc_cap_share Access3::share Access3::timing Access3::cdeps-common +) +endif() + +target_sources(mom6lib PRIVATE + ${SRC}/ALE/coord_adapt.F90 + ${SRC}/ALE/coord_hycom.F90 + ${SRC}/ALE/coord_rho.F90 + ${SRC}/ALE/coord_sigma.F90 + ${SRC}/ALE/coord_zlike.F90 + ${SRC}/ALE/MOM_ALE.F90 + ${SRC}/ALE/MOM_hybgen_regrid.F90 + ${SRC}/ALE/MOM_hybgen_remap.F90 + ${SRC}/ALE/MOM_hybgen_unmix.F90 + ${SRC}/ALE/MOM_regridding.F90 + ${SRC}/ALE/MOM_remapping.F90 + ${SRC}/ALE/P1M_functions.F90 + ${SRC}/ALE/P3M_functions.F90 + ${SRC}/ALE/PCM_functions.F90 + ${SRC}/ALE/PLM_functions.F90 + ${SRC}/ALE/polynomial_functions.F90 + ${SRC}/ALE/PPM_functions.F90 + ${SRC}/ALE/PQM_functions.F90 + ${SRC}/ALE/regrid_consts.F90 + ${SRC}/ALE/regrid_edge_values.F90 + ${SRC}/ALE/regrid_interp.F90 + ${SRC}/ALE/regrid_solvers.F90 + ${SRC}/ALE/remapping_attic.F90 + + ${SRC}/core/MOM_barotropic.F90 + ${SRC}/core/MOM_boundary_update.F90 + ${SRC}/core/MOM_check_scaling.F90 + ${SRC}/core/MOM_checksum_packages.F90 + ${SRC}/core/MOM_continuity.F90 + ${SRC}/core/MOM_continuity_PPM.F90 + ${SRC}/core/MOM_CoriolisAdv.F90 + ${SRC}/core/MOM_density_integrals.F90 + ${SRC}/core/MOM_dynamics_split_RK2.F90 + ${SRC}/core/MOM_dynamics_split_RK2b.F90 + ${SRC}/core/MOM_dynamics_unsplit.F90 + ${SRC}/core/MOM_dynamics_unsplit_RK2.F90 + ${SRC}/core/MOM.F90 + ${SRC}/core/MOM_grid.F90 + ${SRC}/core/MOM_interface_heights.F90 + ${SRC}/core/MOM_isopycnal_slopes.F90 + ${SRC}/core/MOM_open_boundary.F90 + ${SRC}/core/MOM_porous_barriers.F90 + ${SRC}/core/MOM_PressureForce.F90 + ${SRC}/core/MOM_PressureForce_FV.F90 + ${SRC}/core/MOM_PressureForce_Montgomery.F90 + ${SRC}/core/MOM_stoch_eos.F90 + ${SRC}/core/MOM_transcribe_grid.F90 + ${SRC}/core/MOM_unit_tests.F90 + ${SRC}/core/MOM_variables.F90 + ${SRC}/core/MOM_verticalGrid.F90 + ${SRC}/core/MOM_forcing_type.F90 + + ${SRC}/diagnostics/MOM_debugging.F90 + ${SRC}/diagnostics/MOM_diagnose_MLD.F90 + ${SRC}/diagnostics/MOM_diagnostics.F90 + ${SRC}/diagnostics/MOM_harmonic_analysis.F90 + ${SRC}/diagnostics/MOM_obsolete_diagnostics.F90 + ${SRC}/diagnostics/MOM_obsolete_params.F90 + ${SRC}/diagnostics/MOM_PointAccel.F90 + ${SRC}/diagnostics/MOM_spatial_means.F90 + ${SRC}/diagnostics/MOM_sum_output.F90 + ${SRC}/diagnostics/MOM_wave_speed.F90 + + ${SRC}/equation_of_state/MOM_EOS.F90 + ${SRC}/equation_of_state/MOM_EOS_base_type.F90 + ${SRC}/equation_of_state/MOM_EOS_Jackett06.F90 + ${SRC}/equation_of_state/MOM_EOS_linear.F90 + ${SRC}/equation_of_state/MOM_EOS_Roquet_rho.F90 + ${SRC}/equation_of_state/MOM_EOS_Roquet_SpV.F90 + ${SRC}/equation_of_state/MOM_EOS_TEOS10.F90 + ${SRC}/equation_of_state/MOM_EOS_UNESCO.F90 + ${SRC}/equation_of_state/MOM_EOS_Wright.F90 + ${SRC}/equation_of_state/MOM_EOS_Wright_full.F90 + ${SRC}/equation_of_state/MOM_EOS_Wright_red.F90 + ${SRC}/equation_of_state/MOM_temperature_convert.F90 + ${SRC}/equation_of_state/MOM_TFreeze.F90 + + ${SRC}/equation_of_state/TEOS10/gsw_chem_potential_water_t_exact.f90 + ${SRC}/equation_of_state/TEOS10/gsw_ct_freezing_exact.f90 + ${SRC}/equation_of_state/TEOS10/gsw_ct_freezing_poly.f90 + ${SRC}/equation_of_state/TEOS10/gsw_ct_from_pt.f90 + ${SRC}/equation_of_state/TEOS10/gsw_ct_from_t.f90 + ${SRC}/equation_of_state/TEOS10/gsw_entropy_part.f90 + ${SRC}/equation_of_state/TEOS10/gsw_entropy_part_zerop.f90 + ${SRC}/equation_of_state/TEOS10/gsw_gibbs.f90 + ${SRC}/equation_of_state/TEOS10/gsw_gibbs_ice.f90 + ${SRC}/equation_of_state/TEOS10/gsw_gibbs_pt0_pt0.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_error_functions.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_freezing_poly_coefficients.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_gibbs_ice_coefficients.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_kinds.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_specvol_coefficients.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_teos10_constants.f90 + ${SRC}/equation_of_state/TEOS10/gsw_mod_toolbox.f90 + ${SRC}/equation_of_state/TEOS10/gsw_pt0_from_t.f90 + ${SRC}/equation_of_state/TEOS10/gsw_pt_from_ct.f90 + ${SRC}/equation_of_state/TEOS10/gsw_pt_from_t.f90 + ${SRC}/equation_of_state/TEOS10/gsw_rho.f90 + ${SRC}/equation_of_state/TEOS10/gsw_rho_first_derivatives.f90 + ${SRC}/equation_of_state/TEOS10/gsw_rho_second_derivatives.f90 + ${SRC}/equation_of_state/TEOS10/gsw_sp_from_sr.f90 + ${SRC}/equation_of_state/TEOS10/gsw_sr_from_sp.f90 + ${SRC}/equation_of_state/TEOS10/gsw_specvol.f90 + ${SRC}/equation_of_state/TEOS10/gsw_specvol_first_derivatives.f90 + ${SRC}/equation_of_state/TEOS10/gsw_specvol_second_derivatives.f90 + ${SRC}/equation_of_state/TEOS10/gsw_t_deriv_chem_potential_water_t_exact.f90 + ${SRC}/equation_of_state/TEOS10/gsw_t_freezing_exact.f90 + ${SRC}/equation_of_state/TEOS10/gsw_t_freezing_poly.f90 + ${SRC}/equation_of_state/TEOS10/gsw_t_from_ct.f90 + + ${SRC}/framework/MOM_array_transform.F90 + ${SRC}/framework/MOM_checksums.F90 + ${SRC}/framework/MOM_coms.F90 + ${SRC}/framework/MOM_cpu_clock.F90 + ${SRC}/framework/MOM_data_override.F90 + ${SRC}/framework/MOM_diag_mediator.F90 + ${SRC}/framework/MOM_diag_remap.F90 + ${SRC}/framework/MOM_document.F90 + ${SRC}/framework/MOM_domains.F90 + ${SRC}/framework/MOM_dyn_horgrid.F90 + ${SRC}/framework/MOM_ensemble_manager.F90 + ${SRC}/framework/MOM_error_handler.F90 + ${SRC}/framework/MOM_file_parser.F90 + ${SRC}/framework/MOM_get_input.F90 + ${SRC}/framework/MOM_hor_index.F90 + ${SRC}/framework/MOM_horizontal_regridding.F90 + ${SRC}/framework/MOM_interpolate.F90 + ${SRC}/framework/MOM_intrinsic_functions.F90 + ${SRC}/framework/MOM_io.F90 + ${SRC}/framework/MOM_io_file.F90 + ${SRC}/framework/MOM_memory_macros.h + ${SRC}/framework/MOM_netcdf.F90 + ${SRC}/framework/MOM_random.F90 + ${SRC}/framework/MOM_restart.F90 + ${SRC}/framework/MOM_safe_alloc.F90 + ${SRC}/framework/MOM_string_functions.F90 + ${SRC}/framework/MOM_unique_scales.F90 + ${SRC}/framework/MOM_unit_scaling.F90 + ${SRC}/framework/MOM_write_cputime.F90 + ${SRC}/framework/posix.F90 + ${SRC}/framework/MOM_coupler_types.F90 + + ${SRC}/ice_shelf/MOM_ice_shelf_diag_mediator.F90 + ${SRC}/ice_shelf/MOM_ice_shelf_dynamics.F90 + ${SRC}/ice_shelf/MOM_ice_shelf.F90 + ${SRC}/ice_shelf/MOM_ice_shelf_initialize.F90 + ${SRC}/ice_shelf/MOM_ice_shelf_state.F90 + ${SRC}/ice_shelf/MOM_marine_ice.F90 + ${SRC}/ice_shelf/user_shelf_init.F90 + + ${SRC}/initialization/MOM_coord_initialization.F90 + ${SRC}/initialization/MOM_fixed_initialization.F90 + ${SRC}/initialization/MOM_grid_initialize.F90 + ${SRC}/initialization/MOM_shared_initialization.F90 + ${SRC}/initialization/MOM_state_initialization.F90 + ${SRC}/initialization/MOM_tracer_initialization_from_Z.F90 + + ${SRC}/ocean_data_assim/MOM_oda_driver.F90 + ${SRC}/ocean_data_assim/MOM_oda_incupd.F90 + + ${SRC}/parameterizations/CVmix/cvmix_background.F90 + ${SRC}/parameterizations/CVmix/cvmix_convection.F90 + ${SRC}/parameterizations/CVmix/cvmix_ddiff.F90 + ${SRC}/parameterizations/CVmix/cvmix_kinds_and_types.F90 + ${SRC}/parameterizations/CVmix/cvmix_kpp.F90 + ${SRC}/parameterizations/CVmix/cvmix_math.F90 + ${SRC}/parameterizations/CVmix/cvmix_put_get.F90 + ${SRC}/parameterizations/CVmix/cvmix_shear.F90 + ${SRC}/parameterizations/CVmix/cvmix_tidal.F90 + ${SRC}/parameterizations/CVmix/cvmix_utils.F90 + + ${SRC}/parameterizations/lateral/MOM_hor_visc.F90 + ${SRC}/parameterizations/lateral/MOM_interface_filter.F90 + ${SRC}/parameterizations/lateral/MOM_internal_tides.F90 + ${SRC}/parameterizations/lateral/MOM_lateral_mixing_coeffs.F90 + ${SRC}/parameterizations/lateral/MOM_load_love_numbers.F90 + ${SRC}/parameterizations/lateral/MOM_MEKE.F90 + ${SRC}/parameterizations/lateral/MOM_MEKE_types.F90 + ${SRC}/parameterizations/lateral/MOM_mixed_layer_restrat.F90 + ${SRC}/parameterizations/lateral/MOM_spherical_harmonics.F90 + ${SRC}/parameterizations/lateral/MOM_streaming_filter.F90 + ${SRC}/parameterizations/lateral/MOM_self_attr_load.F90 + ${SRC}/parameterizations/lateral/MOM_thickness_diffuse.F90 + ${SRC}/parameterizations/lateral/MOM_tidal_forcing.F90 + ${SRC}/parameterizations/lateral/MOM_Zanna_Bolton.F90 + + ${SRC}/parameterizations/stochastic/MOM_stochastics.F90 + + ${SRC}/parameterizations/vertical/MOM_ALE_sponge.F90 + ${SRC}/parameterizations/vertical/MOM_bkgnd_mixing.F90 + ${SRC}/parameterizations/vertical/MOM_bulk_mixed_layer.F90 + ${SRC}/parameterizations/vertical/MOM_CVMix_conv.F90 + ${SRC}/parameterizations/vertical/MOM_CVMix_ddiff.F90 + ${SRC}/parameterizations/vertical/MOM_CVMix_KPP.F90 + ${SRC}/parameterizations/vertical/MOM_CVMix_shear.F90 + ${SRC}/parameterizations/vertical/MOM_diabatic_aux.F90 + ${SRC}/parameterizations/vertical/MOM_diabatic_driver.F90 + ${SRC}/parameterizations/vertical/MOM_diapyc_energy_req.F90 + ${SRC}/parameterizations/vertical/MOM_energetic_PBL.F90 + ${SRC}/parameterizations/vertical/MOM_entrain_diffusive.F90 + ${SRC}/parameterizations/vertical/MOM_full_convection.F90 + ${SRC}/parameterizations/vertical/MOM_geothermal.F90 + ${SRC}/parameterizations/vertical/MOM_internal_tide_input.F90 + ${SRC}/parameterizations/vertical/MOM_kappa_shear.F90 + ${SRC}/parameterizations/vertical/MOM_opacity.F90 + ${SRC}/parameterizations/vertical/MOM_regularize_layers.F90 + ${SRC}/parameterizations/vertical/MOM_set_diffusivity.F90 + ${SRC}/parameterizations/vertical/MOM_set_viscosity.F90 + ${SRC}/parameterizations/vertical/MOM_sponge.F90 + ${SRC}/parameterizations/vertical/MOM_tidal_mixing.F90 + ${SRC}/parameterizations/vertical/MOM_vert_friction.F90 + + ${SRC}/tracer/advection_test_tracer.F90 + ${SRC}/tracer/boundary_impulse_tracer.F90 + ${SRC}/tracer/DOME_tracer.F90 + ${SRC}/tracer/dyed_obc_tracer.F90 + ${SRC}/tracer/dye_example.F90 + ${SRC}/tracer/ideal_age_example.F90 + ${SRC}/tracer/ISOMIP_tracer.F90 + ${SRC}/tracer/MOM_CFC_cap.F90 + ${SRC}/tracer/MOM_hor_bnd_diffusion.F90 + ${SRC}/tracer/MOM_neutral_diffusion.F90 + ${SRC}/tracer/MOM_OCMIP2_CFC.F90 + ${SRC}/tracer/MOM_offline_aux.F90 + ${SRC}/tracer/MOM_offline_main.F90 + ${SRC}/tracer/MOM_tracer_advect.F90 + ${SRC}/tracer/MOM_tracer_diabatic.F90 + ${SRC}/tracer/MOM_tracer_flow_control.F90 + ${SRC}/tracer/MOM_tracer_hor_diff.F90 + ${SRC}/tracer/MOM_tracer_registry.F90 + ${SRC}/tracer/MOM_tracer_types.F90 + ${SRC}/tracer/MOM_tracer_Z_init.F90 + ${SRC}/tracer/nw2_tracers.F90 + ${SRC}/tracer/oil_tracer.F90 + ${SRC}/tracer/pseudo_salt_tracer.F90 + ${SRC}/tracer/RGC_tracer.F90 + ${SRC}/tracer/tracer_example.F90 + + ${SRC}/user/adjustment_initialization.F90 + ${SRC}/user/baroclinic_zone_initialization.F90 + ${SRC}/user/basin_builder.F90 + ${SRC}/user/benchmark_initialization.F90 + ${SRC}/user/BFB_initialization.F90 + ${SRC}/user/BFB_surface_forcing.F90 + ${SRC}/user/circle_obcs_initialization.F90 + ${SRC}/user/dense_water_initialization.F90 + ${SRC}/user/DOME2d_initialization.F90 + ${SRC}/user/DOME_initialization.F90 + ${SRC}/user/dumbbell_initialization.F90 + ${SRC}/user/dumbbell_surface_forcing.F90 + ${SRC}/user/dyed_channel_initialization.F90 + ${SRC}/user/dyed_obcs_initialization.F90 + ${SRC}/user/external_gwave_initialization.F90 + ${SRC}/user/Idealized_Hurricane.F90 + ${SRC}/user/ISOMIP_initialization.F90 + ${SRC}/user/Kelvin_initialization.F90 + ${SRC}/user/lock_exchange_initialization.F90 + ${SRC}/user/MOM_controlled_forcing.F90 + ${SRC}/user/MOM_wave_interface.F90 + ${SRC}/user/Neverworld_initialization.F90 + ${SRC}/user/Phillips_initialization.F90 + ${SRC}/user/RGC_initialization.F90 + ${SRC}/user/Rossby_front_2d_initialization.F90 + ${SRC}/user/SCM_CVMix_tests.F90 + ${SRC}/user/seamount_initialization.F90 + ${SRC}/user/shelfwave_initialization.F90 + ${SRC}/user/sloshing_initialization.F90 + ${SRC}/user/soliton_initialization.F90 + ${SRC}/user/supercritical_initialization.F90 + ${SRC}/user/tidal_bay_initialization.F90 + ${SRC}/user/user_change_diffusivity.F90 + ${SRC}/user/user_initialization.F90 + ${SRC}/user/user_revise_forcing.F90 + + ${CONFIG_SRC}/external/database_comms/MOM_database_comms.F90 + ${CONFIG_SRC}/external/database_comms/database_client_interface.F90 + + ${CONFIG_SRC}/external/drifters/MOM_particles.F90 + ${CONFIG_SRC}/external/drifters/MOM_particles_types.F90 + + ${CONFIG_SRC}/external/ODA_hooks/kdtree.f90 + ${CONFIG_SRC}/external/ODA_hooks/ocean_da_core.F90 + ${CONFIG_SRC}/external/ODA_hooks/ocean_da_types.F90 + ${CONFIG_SRC}/external/ODA_hooks/write_ocean_obs.F90 + + ${CONFIG_SRC}/external/stochastic_physics/get_stochy_pattern.F90 + ${CONFIG_SRC}/external/stochastic_physics/stochastic_physics.F90 + + ${CONFIG_SRC}/external/GFDL_ocean_BGC/generic_tracer_utils.F90 + ${CONFIG_SRC}/external/GFDL_ocean_BGC/generic_tracer.F90 + + ${CONFIG_SRC}/infra/FMS2/MOM_coms_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_constants.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_cpu_clock_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_data_override_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_diag_manager_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_domain_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_ensemble_manager_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_error_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_interp_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_io_infra.F90 + ${CONFIG_SRC}/infra/FMS2/MOM_time_manager.F90 + + ${CONFIG_SRC}/infra/FMS2/MOM_couplertype_infra.F90 + + ${SRC}/tracer/MOM_generic_tracer.F90 +) + +if (ACCESS3_MOM6) + target_sources(mom6lib PRIVATE + ${CONFIG_SRC}/drivers/nuopc_cap/mom_cap_time.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/mom_cap_gtracer_flux.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/mom_surface_forcing_nuopc.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/ocn_comp_NUOPC.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/time_utils.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/mom_cap.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/mom_cap_methods.F90 + ${CONFIG_SRC}/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 + ) +else() + target_sources(mom6lib PRIVATE + ${CONFIG_SRC}/drivers/solo_driver/MOM_surface_forcing.F90 + ${CONFIG_SRC}/drivers/solo_driver/MESO_surface_forcing.F90 + ${CONFIG_SRC}/drivers/solo_driver/user_surface_forcing.F90 + ) +endif() +### Install and Export + +## Library +if(ACCESS3_MOM6) + set_target_properties(mom6lib PROPERTIES + OUTPUT_NAME access-mom6lib + EXPORT_NAME mom6lib + ) + install(TARGETS mom6lib + EXPORT MOM6libTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT AccessMOM6Cmeps_runtime NAMELINK_COMPONENT AccessMOM6Cmeps_Development + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT AccessMOM6Cmeps_Development + ) + # Fortran module files are a special case, as currently there is no standard + # way of handling them in CMake + target_include_directories(mom6lib PUBLIC "$") + get_target_property(mom_moddir mom6lib Fortran_MODULE_DIRECTORY) + install(FILES ${mom_moddir}/ocn_comp_nuopc.mod ${mom_moddir}/mom_cap_mod.mod + DESTINATION ${CMAKE_INSTALL_MODULEDIR}/MOM6lib + COMPONENT AccessMOM6Cmeps_Development + ) + install(EXPORT MOM6libTargets + FILE MOM6libTargets.cmake + NAMESPACE Access3:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MOM6lib + ) + + configure_package_config_file( + MOM6libConfig.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/MOM6libConfig.cmake" + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MOM6lib + ) + + install(FILES ${CMAKE_SOURCE_DIR}/FindNetCDF.cmake ${CMAKE_CURRENT_BINARY_DIR}/MOM6libConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MOM6lib + COMPONENT AccessMOM6Cmeps_Development + ) + +else() + +# executable + add_executable(MOM6 ${CONFIG_SRC}/drivers/solo_driver/MOM_driver.F90) + target_link_libraries(MOM6 PRIVATE mom6lib) + + target_include_directories(MOM6 PRIVATE + ${SRC}/framework + ${MOM_MEMORY_DIR} + ${CONFIG_SRC}/drivers/solo_driver + ) + + set_target_properties(MOM6 PROPERTIES + LINKER_LANGUAGE Fortran + OUTPUT_NAME mom6 + ) + + install(TARGETS MOM6 + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) +endif() diff --git a/cmake/CompilerFlags.cmake b/cmake/CompilerFlags.cmake new file mode 100644 index 0000000000..112340a560 --- /dev/null +++ b/cmake/CompilerFlags.cmake @@ -0,0 +1,30 @@ +# Common compiler flags and definitions + +# Fortran compiler flags +if(CMAKE_Fortran_COMPILER_ID MATCHES "GNU") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fbacktrace -fconvert=big-endian -ffree-line-length-none -ffixed-line-length-none -fdefault-real-8 -fdefault-double-8") + if(${CMAKE_Fortran_COMPILER_VERSION} VERSION_GREATER_EQUAL 10) + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -fallow-argument-mismatch") + endif() + set(CMAKE_Fortran_FLAGS_RELEASE "-O") + set(CMAKE_Fortran_FLAGS_DEBUG "-g -Wall -Og -ffpe-trap=zero,overflow -fcheck=bounds") +elseif(CMAKE_Fortran_COMPILER_ID MATCHES "Intel") + set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -qno-opt-dynamic-align -convert big_endian -assume byterecl -ftz -traceback -assume realloc_lhs -fp-model source -r8") + set(CMAKE_Fortran_FLAGS_RELEASE "-O2 -debug minimal") + set(CMAKE_Fortran_FLAGS_DEBUG "-O0 -g -check uninit -check bounds -check pointers -fpe0 -check noarg_temp_created") +else() + message(WARNING "Fortran compiler with ID ${CMAKE_Fortran_COMPILER_ID} will be used with CMake default options") +endif() + +# C compiler flags +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") + set(CMAKE_C_FLAGS_RELEASE "-O") + set(CMAKE_C_FLAGS_DEBUG "-g -Wall -Og -fbacktrace -ffpe-trap=invalid,zero,overflow -fcheck=bounds") +elseif(CMAKE_C_COMPILER_ID MATCHES "Intel") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -traceback -qno-opt-dynamic-align -fp-model precise -std=gnu99") + set(CMAKE_C_FLAGS_RELEASE "-O2 -debug minimal") + set(CMAKE_C_FLAGS_DEBUG "-O0 -g") +else() + message(WARNING "C compiler with ID ${CMAKE_C_COMPILER_ID} will be used with CMake default options") +endif() diff --git a/cmake/FindESMF.cmake b/cmake/FindESMF.cmake new file mode 100644 index 0000000000..c79c5d3a1e --- /dev/null +++ b/cmake/FindESMF.cmake @@ -0,0 +1,135 @@ +# - Try to find ESMF +# +# Requires setting ESMFMKFILE to the filepath of esmf.mk. If this is NOT set, +# then ESMF_FOUND will always be FALSE. If ESMFMKFILE exists, then ESMF_FOUND=TRUE +# and all ESMF makefile variables will be set in the global scope. Optionally, +# set ESMF_MKGLOBALS to a string list to filter makefile variables. For example, +# to globally scope only ESMF_LIBSDIR and ESMF_APPSDIR variables, use this CMake +# command in CMakeLists.txt: +# +# set(ESMF_MKGLOBALS "LIBSDIR" "APPSDIR") + + +# Add the ESMFMKFILE path to the cache if defined as system env variable +if(DEFINED ENV{ESMFMKFILE} AND NOT DEFINED ESMFMKFILE) + set(ESMFMKFILE $ENV{ESMFMKFILE} CACHE FILEPATH "Path to ESMF mk file") +endif() + +# If it's not explicitly set try to find esmf.mk file in default locations (ESMF_ROOT, CMAKE_PREFIX_PATH, etc) +if(NOT DEFINED ESMFMKFILE) + find_path(ESMFMKFILE_PATH esmf.mk PATH_SUFFIXES lib lib64) + if(ESMFMKFILE_PATH) + set(ESMFMKFILE ${ESMFMKFILE_PATH}/esmf.mk) + message(STATUS "Found esmf.mk file ${ESMFMKFILE}") + else() + message(STATUS "ESMFMKFILE not defined. This is the path to esmf.mk file. \ +Without this filepath, ESMF_FOUND will always be FALSE.") + endif() +endif() + +# Only parse the mk file if it is found +if(EXISTS ${ESMFMKFILE}) + # Read the mk file + file(STRINGS "${ESMFMKFILE}" esmfmkfile_contents) + # Parse each line in the mk file + foreach(str ${esmfmkfile_contents}) + # Only consider uncommented lines + string(REGEX MATCH "^[^#]" def ${str}) + # Line is not commented + if(def) + # Extract the variable name + string(REGEX MATCH "^[^=]+" esmf_varname ${str}) + # Extract the variable's value + string(REGEX MATCH "=.+$" esmf_vardef ${str}) + # Only for variables with a defined value + if(esmf_vardef) + # Get rid of the assignment string + string(SUBSTRING ${esmf_vardef} 1 -1 esmf_vardef) + # Remove whitespace + string(STRIP ${esmf_vardef} esmf_vardef) + # A string or single-valued list + if(NOT DEFINED ESMF_MKGLOBALS) + # Set in global scope + set(${esmf_varname} ${esmf_vardef}) + # Don't display by default in GUI + mark_as_advanced(esmf_varname) + else() # Need to filter global promotion + foreach(m ${ESMF_MKGLOBALS}) + string(FIND ${esmf_varname} ${m} match) + # Found the string + if(NOT ${match} EQUAL -1) + # Promote to global scope + set(${esmf_varname} ${esmf_vardef}) + # Don't display by default in the GUI + mark_as_advanced(esmf_varname) + # No need to search for the current string filter + break() + endif() + endforeach() + endif() + endif() + endif() + endforeach() + + # Construct ESMF_VERSION from ESMF_VERSION_STRING_GIT + # ESMF_VERSION_MAJOR and ESMF_VERSION_MINOR are defined in ESMFMKFILE + set(ESMF_VERSION 0) + set(ESMF_VERSION_PATCH ${ESMF_VERSION_REVISION}) + set(ESMF_BETA_RELEASE FALSE) + if(ESMF_VERSION_BETASNAPSHOT MATCHES "^('T')$") + set(ESMF_BETA_RELEASE TRUE) + string(REGEX REPLACE ".*beta_snapshot_*\([0-9]*\).*" "\\1" ESMF_BETA_SNAPSHOT "${ESMF_VERSION_STRING_GIT}") + message(STATUS "Detected ESMF Beta snapshot ${ESMF_BETA_SNAPSHOT}") + endif() + set(ESMF_VERSION "${ESMF_VERSION_MAJOR}.${ESMF_VERSION_MINOR}.${ESMF_VERSION_PATCH}") + + separate_arguments(ESMF_F90COMPILEPATHS NATIVE_COMMAND ${ESMF_F90COMPILEPATHS}) + foreach(ITEM ${ESMF_F90COMPILEPATHS}) + string(REGEX REPLACE "^-I" "" ITEM "${ITEM}") + list(APPEND tmp ${ITEM}) + endforeach() + set(ESMF_F90COMPILEPATHS ${tmp}) + + # Look for static library, if not found try dynamic library + find_library(esmf_lib NAMES libesmf.a PATHS ${ESMF_LIBSDIR}) + if(esmf_lib MATCHES "esmf_lib-NOTFOUND") + unset(esmf_lib) + message(STATUS "Static ESMF library not found, searching for dynamic library instead") + find_library(esmf_lib NAMES esmf_fullylinked libesmf.so PATHS ${ESMF_LIBSDIR}) + if(esmf_lib MATCHES "esmf_lib-NOTFOUND") + unset(esmf_lib) + message(STATUS "Neither the dynamic nor the static ESMF library was found") + else() + set(_library_type SHARED) + endif() + else() + set(_library_type STATIC) + endif() + + string(STRIP "${ESMF_F90ESMFLINKRPATHS} ${ESMF_F90ESMFLINKPATHS} ${ESMF_F90LINKPATHS} ${ESMF_F90LINKLIBS} ${ESMF_F90LINKOPTS}" ESMF_INTERFACE_LINK_LIBRARIES) + set(ESMF_LIBRARY_LOCATION ${esmf_lib}) + +else() + + message(WARNING "ESMFMKFILE ${ESMFMKFILE} does not exist") + +endif() + +## Finalize find_package +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + ${CMAKE_FIND_PACKAGE_NAME} + REQUIRED_VARS ESMF_LIBRARY_LOCATION + ESMF_INTERFACE_LINK_LIBRARIES + ESMF_F90COMPILEPATHS + VERSION_VAR ESMF_VERSION) + +## If ESMF is found create imported library target +if(ESMF_FOUND) + add_library(esmf ${_library_type} IMPORTED) + set_target_properties(esmf PROPERTIES + IMPORTED_LOCATION "${ESMF_LIBRARY_LOCATION}" + INTERFACE_INCLUDE_DIRECTORIES "${ESMF_F90COMPILEPATHS}" + INTERFACE_LINK_LIBRARIES "${ESMF_INTERFACE_LINK_LIBRARIES}") +endif() \ No newline at end of file diff --git a/cmake/FindNetCDF.cmake b/cmake/FindNetCDF.cmake new file mode 100644 index 0000000000..1439ae8486 --- /dev/null +++ b/cmake/FindNetCDF.cmake @@ -0,0 +1,337 @@ +# (C) Copyright 2011- ECMWF. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation nor +# does it submit to any jurisdiction. + +# Try to find NetCDF includes and library. +# Supports static and shared libaries and allows each component to be found in sepearte prefixes. +# +# This module defines +# +# - NetCDF_FOUND - System has NetCDF +# - NetCDF_INCLUDE_DIRS - the NetCDF include directories +# - NetCDF_VERSION - the version of NetCDF +# - NetCDF_CONFIG_EXECUTABLE - the netcdf-config executable if found +# - NetCDF_PARALLEL - Boolean True if NetCDF4 has parallel IO support via hdf5 and/or pnetcdf +# - NetCDF_HAS_PNETCDF - Boolean True if NetCDF4 has pnetcdf support +# +# Deprecated Defines +# - NetCDF_LIBRARIES - [Deprecated] Use NetCDF::NetCDF_ targets instead. +# +# +# Following components are available: +# +# - C - C interface to NetCDF (netcdf) +# - CXX - CXX4 interface to NetCDF (netcdf_c++4) +# - Fortran - Fortran interface to NetCDF (netcdff) +# +# For each component the following are defined: +# +# - NetCDF__FOUND - whether the component is found +# - NetCDF__LIBRARIES - the libraries for the component +# - NetCDF__LIBRARY_SHARED - Boolean is true if libraries for component are shared +# - NetCDF__INCLUDE_DIRS - the include directories for specified component +# - NetCDF::NetCDF_ - target of component to be used with target_link_libraries() +# +# The following paths will be searched in order if set in CMake (first priority) or environment (second priority) +# +# - NetCDF_ROOT - root of NetCDF installation +# - NetCDF_PATH - root of NetCDF installation +# +# The search process begins with locating NetCDF Include headers. If these are in a non-standard location, +# set one of the following CMake or environment variables to point to the location: +# +# - NetCDF_INCLUDE_DIR or NetCDF_${comp}_INCLUDE_DIR +# - NetCDF_INCLUDE_DIRS or NetCDF_${comp}_INCLUDE_DIR +# +# Notes: +# +# - Use "NetCDF::NetCDF_" targets only. NetCDF_LIBRARIES exists for backwards compatibility and should not be used. +# - These targets have all the knowledge of include directories and library search directories, and a single +# call to target_link_libraries will provide all these transitive properties to your target. Normally all that is +# needed to build and link against NetCDF is, e.g.: +# target_link_libraries(my_c_tgt PUBLIC NetCDF::NetCDF_C) +# - "NetCDF" is always the preferred naming for this package, its targets, variables, and environment variables +# - For compatibility, some variables are also set/checked using alternate names NetCDF4, NETCDF, or NETCDF4 +# - Environments relying on these older environment variable names should move to using a "NetCDF_ROOT" environment variable +# - Preferred component capitalization follows the CMake LANGUAGES variables: i.e., C, Fortran, CXX +# - For compatibility, alternate capitalizations are supported but should not be used. +# - If no components are defined, all components will be searched +# + +list( APPEND _possible_components C CXX Fortran ) + +## Include names for each component +set( NetCDF_C_INCLUDE_NAME netcdf.h ) +set( NetCDF_CXX_INCLUDE_NAME netcdf ) +set( NetCDF_Fortran_INCLUDE_NAME netcdf.mod ) + +## Library names for each component +set( NetCDF_C_LIBRARY_NAME netcdf ) +set( NetCDF_CXX_LIBRARY_NAME netcdf_c++4 ) +set( NetCDF_Fortran_LIBRARY_NAME netcdff ) + +## Enumerate search components +foreach( _comp ${_possible_components} ) + string( TOUPPER "${_comp}" _COMP ) + set( _arg_${_COMP} ${_comp} ) + set( _name_${_COMP} ${_comp} ) +endforeach() + +set( _search_components C) +foreach( _comp ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS} ) + string( TOUPPER "${_comp}" _COMP ) + set( _arg_${_COMP} ${_comp} ) + list( APPEND _search_components ${_name_${_COMP}} ) + if( NOT _name_${_COMP} ) + message(SEND_ERROR "Find${CMAKE_FIND_PACKAGE_NAME}: COMPONENT ${_comp} is not a valid component. Valid components: ${_possible_components}" ) + endif() +endforeach() +list( REMOVE_DUPLICATES _search_components ) + +## Search hints for finding include directories and libraries +foreach( _comp IN ITEMS "_" "_C_" "_Fortran_" "_CXX_" ) + foreach( _name IN ITEMS NetCDF4 NetCDF NETCDF4 NETCDF ) + foreach( _var IN ITEMS ROOT PATH ) + list(APPEND _search_hints ${${_name}${_comp}${_var}} $ENV{${_name}${_comp}${_var}} ) + list(APPEND _include_search_hints + ${${_name}${_comp}INCLUDE_DIR} $ENV{${_name}${_comp}INCLUDE_DIR} + ${${_name}${_comp}INCLUDE_DIRS} $ENV{${_name}${_comp}INCLUDE_DIRS} ) + endforeach() + endforeach() +endforeach() +#Old-school HPC module env variable names +foreach( _name IN ITEMS NetCDF4 NetCDF NETCDF4 NETCDF ) + foreach( _comp IN ITEMS "_C" "_Fortran" "_CXX" ) + list(APPEND _search_hints ${${_name}} $ENV{${_name}}) + list(APPEND _search_hints ${${_name}${_comp}} $ENV{${_name}${_comp}}) + endforeach() +endforeach() + +## Find headers for each component +set(NetCDF_INCLUDE_DIRS) +set(_new_search_components) +foreach( _comp IN LISTS _search_components ) + if(NOT ${PROJECT_NAME}_NetCDF_${_comp}_FOUND) + list(APPEND _new_search_components ${_comp}) + endif() + find_file(NetCDF_${_comp}_INCLUDE_FILE + NAMES ${NetCDF_${_comp}_INCLUDE_NAME} + DOC "NetCDF ${_comp} include directory" + HINTS ${_include_search_hints} ${_search_hints} + PATH_SUFFIXES include include/netcdf + ) + mark_as_advanced(NetCDF_${_comp}_INCLUDE_FILE) + message(DEBUG "NetCDF_${_comp}_INCLUDE_FILE: ${NetCDF_${_comp}_INCLUDE_FILE}") + if( NetCDF_${_comp}_INCLUDE_FILE ) + get_filename_component(NetCDF_${_comp}_INCLUDE_FILE ${NetCDF_${_comp}_INCLUDE_FILE} ABSOLUTE) + get_filename_component(NetCDF_${_comp}_INCLUDE_DIR ${NetCDF_${_comp}_INCLUDE_FILE} DIRECTORY) + list(APPEND NetCDF_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIR}) + endif() +endforeach() +if(NetCDF_INCLUDE_DIRS) + list(REMOVE_DUPLICATES NetCDF_INCLUDE_DIRS) +endif() +set(NetCDF_INCLUDE_DIRS "${NetCDF_INCLUDE_DIRS}" CACHE STRING "NetCDF Include directory paths" FORCE) + +## Find n*-config executables for search components +foreach( _comp IN LISTS _search_components ) + if( _comp MATCHES "^(C)$" ) + set(_conf "c") + elseif( _comp MATCHES "^(Fortran)$" ) + set(_conf "f") + elseif( _comp MATCHES "^(CXX)$" ) + set(_conf "cxx4") + endif() + find_program( NetCDF_${_comp}_CONFIG_EXECUTABLE + NAMES n${_conf}-config + HINTS ${NetCDF_INCLUDE_DIRS} ${_include_search_hints} ${_search_hints} + PATH_SUFFIXES bin Bin ../bin ../../bin + DOC "NetCDF n${_conf}-config helper" ) + message(DEBUG "NetCDF_${_comp}_CONFIG_EXECUTABLE: ${NetCDF_${_comp}_CONFIG_EXECUTABLE}") +endforeach() + +set(_C_libs_flag --libs) +set(_Fortran_libs_flag --flibs) +set(_CXX_libs_flag --libs) +set(_C_includes_flag --includedir) +set(_Fortran_includes_flag --includedir) +set(_CXX_includes_flag --includedir) +function(netcdf_config exec flag output_var) + set(${output_var} False PARENT_SCOPE) + if( exec ) + execute_process( COMMAND ${exec} ${flag} RESULT_VARIABLE _ret OUTPUT_VARIABLE _val) + if( _ret EQUAL 0 ) + string( STRIP ${_val} _val ) + set( ${output_var} ${_val} PARENT_SCOPE ) + endif() + endif() +endfunction() + +## Find libraries for each component +set( NetCDF_LIBRARIES ) +foreach( _comp IN LISTS _search_components ) + string( TOUPPER "${_comp}" _COMP ) + + find_library( NetCDF_${_comp}_LIBRARY + NAMES ${NetCDF_${_comp}_LIBRARY_NAME} + DOC "NetCDF ${_comp} library" + HINTS ${NetCDF_${_comp}_INCLUDE_DIRS} ${_search_hints} + PATH_SUFFIXES lib64 lib ../lib64 ../lib ../../lib64 ../../lib ) + mark_as_advanced( NetCDF_${_comp}_LIBRARY ) + get_filename_component(NetCDF_${_comp}_LIBRARY ${NetCDF_${_comp}_LIBRARY} ABSOLUTE) + set(NetCDF_${_comp}_LIBRARY ${NetCDF_${_comp}_LIBRARY} CACHE STRING "NetCDF ${_comp} library" FORCE) + message(DEBUG "NetCDF_${_comp}_LIBRARY: ${NetCDF_${_comp}_LIBRARY}") + + if( NetCDF_${_comp}_LIBRARY ) + if( NetCDF_${_comp}_LIBRARY MATCHES ".a$" ) + set( NetCDF_${_comp}_LIBRARY_SHARED FALSE ) + set( _library_type STATIC) + else() + list( APPEND NetCDF_LIBRARIES ${NetCDF_${_comp}_LIBRARY} ) + set( NetCDF_${_comp}_LIBRARY_SHARED TRUE ) + set( _library_type SHARED) + endif() + endif() + + #Use nc-config to set per-component LIBRARIES variable if possible + netcdf_config( ${NetCDF_${_comp}_CONFIG_EXECUTABLE} ${_${_comp}_libs_flag} _val ) + if( _val ) + set( NetCDF_${_comp}_LIBRARIES ${_val} ) + if(NOT NetCDF_${_comp}_LIBRARY_SHARED AND NOT NetCDF_${_comp}_FOUND) #Static targets should use nc_config to get a proper link line with all necessary static targets. + list( APPEND NetCDF_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) + endif() + else() + set( NetCDF_${_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARY} ) + if(NOT NetCDF_${_comp}_LIBRARY_SHARED) + message(SEND_ERROR "Unable to properly find NetCDF. Found static libraries at: ${NetCDF_${_comp}_LIBRARY} but could not run nc-config: ${NetCDF_CONFIG_EXECUTABLE}") + endif() + endif() + + #Use nc-config to set per-component INCLUDE_DIRS variable if possible + netcdf_config( ${NetCDF_${_comp}_CONFIG_EXECUTABLE} ${_${_comp}_includes_flag} _val ) + if( _val ) + string( REPLACE " " ";" _val ${_val} ) + set( NetCDF_${_comp}_INCLUDE_DIRS ${_val} ) + else() + set( NetCDF_${_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIR} ) + endif() + + if( NetCDF_${_comp}_LIBRARIES AND NetCDF_${_comp}_INCLUDE_DIRS ) + set( ${CMAKE_FIND_PACKAGE_NAME}_${_arg_${_COMP}}_FOUND TRUE ) + if (NOT TARGET NetCDF::NetCDF_${_comp}) + add_library(NetCDF::NetCDF_${_comp} ${_library_type} IMPORTED) + set_target_properties(NetCDF::NetCDF_${_comp} PROPERTIES + IMPORTED_LOCATION ${NetCDF_${_comp}_LIBRARY} + INTERFACE_INCLUDE_DIRECTORIES "${NetCDF_${_comp}_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) + endif() + endif() +endforeach() +if(NetCDF_LIBRARIES AND NetCDF_${_comp}_LIBRARY_SHARED) + list(REMOVE_DUPLICATES NetCDF_LIBRARIES) +endif() +set(NetCDF_LIBRARIES "${NetCDF_LIBRARIES}" CACHE STRING "NetCDF library targets" FORCE) + +## Find version via netcdf-config if possible +if (NetCDF_INCLUDE_DIRS) + if( NetCDF_C_CONFIG_EXECUTABLE ) + netcdf_config( ${NetCDF_C_CONFIG_EXECUTABLE} --version _vers ) + if( _vers ) + string(REGEX REPLACE ".* ((([0-9]+)\\.)+([0-9]+)).*" "\\1" NetCDF_VERSION "${_vers}" ) + endif() + else() + foreach( _dir IN LISTS NetCDF_INCLUDE_DIRS) + if( EXISTS "${_dir}/netcdf_meta.h" ) + file(STRINGS "${_dir}/netcdf_meta.h" _netcdf_version_lines + REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)") + string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" _netcdf_version_major "${_netcdf_version_lines}") + string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" _netcdf_version_minor "${_netcdf_version_lines}") + string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" _netcdf_version_patch "${_netcdf_version_lines}") + string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" _netcdf_version_note "${_netcdf_version_lines}") + set(NetCDF_VERSION "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}") + unset(_netcdf_version_major) + unset(_netcdf_version_minor) + unset(_netcdf_version_patch) + unset(_netcdf_version_note) + unset(_netcdf_version_lines) + endif() + endforeach() + endif() +endif () + +## Detect additional package properties +netcdf_config(${NetCDF_C_CONFIG_EXECUTABLE} --has-parallel4 _val) +if( NOT _val MATCHES "^(yes|no)$" ) + netcdf_config(${NetCDF_C_CONFIG_EXECUTABLE} --has-parallel _val) +endif() +if( _val MATCHES "^(yes)$" ) + set(NetCDF_PARALLEL TRUE CACHE STRING "NetCDF has parallel IO capability via pnetcdf or hdf5." FORCE) +else() + set(NetCDF_PARALLEL FALSE CACHE STRING "NetCDF has no parallel IO capability." FORCE) +endif() + +## Finalize find_package +include(FindPackageHandleStandardArgs) + +if(NOT NetCDF_FOUND OR _new_search_components) + find_package_handle_standard_args( ${CMAKE_FIND_PACKAGE_NAME} + REQUIRED_VARS NetCDF_INCLUDE_DIRS NetCDF_LIBRARIES + VERSION_VAR NetCDF_VERSION + HANDLE_COMPONENTS ) +endif() + +foreach( _comp IN LISTS _search_components ) + if( NetCDF_${_comp}_FOUND ) + #Record found components to avoid duplication in NetCDF_LIBRARIES for static libraries + set(NetCDF_${_comp}_FOUND ${NetCDF_${_comp}_FOUND} CACHE BOOL "NetCDF ${_comp} Found" FORCE) + #Set a per-package, per-component found variable to communicate between multiple calls to find_package() + set(${PROJECT_NAME}_NetCDF_${_comp}_FOUND True) + endif() +endforeach() + +if( ${CMAKE_FIND_PACKAGE_NAME}_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY AND _new_search_components) + message( STATUS "Find${CMAKE_FIND_PACKAGE_NAME} defines targets:" ) + message( STATUS " - NetCDF_VERSION [${NetCDF_VERSION}]") + message( STATUS " - NetCDF_PARALLEL [${NetCDF_PARALLEL}]") + foreach( _comp IN LISTS _new_search_components ) + string( TOUPPER "${_comp}" _COMP ) + message( STATUS " - NetCDF_${_comp}_CONFIG_EXECUTABLE [${NetCDF_${_comp}_CONFIG_EXECUTABLE}]") + if( ${CMAKE_FIND_PACKAGE_NAME}_${_arg_${_COMP}}_FOUND ) + get_filename_component(_root ${NetCDF_${_comp}_INCLUDE_DIR}/.. ABSOLUTE) + if( NetCDF_${_comp}_LIBRARY_SHARED ) + message( STATUS " - NetCDF::NetCDF_${_comp} [SHARED] [Root: ${_root}] Lib: ${NetCDF_${_comp}_LIBRARY} ") + else() + message( STATUS " - NetCDF::NetCDF_${_comp} [STATIC] [Root: ${_root}] Lib: ${NetCDF_${_comp}_LIBRARY} ") + endif() + endif() + endforeach() +endif() + +foreach( _prefix NetCDF NetCDF4 NETCDF NETCDF4 ${CMAKE_FIND_PACKAGE_NAME} ) + set( ${_prefix}_INCLUDE_DIRS ${NetCDF_INCLUDE_DIRS} ) + set( ${_prefix}_LIBRARIES ${NetCDF_LIBRARIES}) + set( ${_prefix}_VERSION ${NetCDF_VERSION} ) + set( ${_prefix}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_FOUND} ) + set( ${_prefix}_CONFIG_EXECUTABLE ${NetCDF_CONFIG_EXECUTABLE} ) + set( ${_prefix}_PARALLEL ${NetCDF_PARALLEL} ) + + foreach( _comp ${_search_components} ) + string( TOUPPER "${_comp}" _COMP ) + set( _arg_comp ${_arg_${_COMP}} ) + set( ${_prefix}_${_comp}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) + set( ${_prefix}_${_COMP}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) + set( ${_prefix}_${_arg_comp}_FOUND ${${CMAKE_FIND_PACKAGE_NAME}_${_arg_comp}_FOUND} ) + + set( ${_prefix}_${_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) + set( ${_prefix}_${_COMP}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) + set( ${_prefix}_${_arg_comp}_LIBRARIES ${NetCDF_${_comp}_LIBRARIES} ) + + set( ${_prefix}_${_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) + set( ${_prefix}_${_COMP}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) + set( ${_prefix}_${_arg_comp}_INCLUDE_DIRS ${NetCDF_${_comp}_INCLUDE_DIRS} ) + endforeach() +endforeach() diff --git a/cmake/FindPIO.cmake b/cmake/FindPIO.cmake new file mode 100644 index 0000000000..cbbd89f793 --- /dev/null +++ b/cmake/FindPIO.cmake @@ -0,0 +1,210 @@ + +# FindPIO.cmake +# +# Copyright UCAR 2020 +# Copyright NOAA/NWS/NCEP/EMC 2020 +# +# Find PIO: A high-level Parallel I/O Library for structured grid applications +# https://github.com/NCAR/ParallelIO +# +# Components available for query: +# C - Has C support +# Fortran - Has Fortran support +# SHARED - Has shared targets +# STATIC - Has static targets +# +# Variables provided: +# PIO_FOUND - True if PIO was found +# PIO_C_FOUND - True if PIO C support was found +# PIO_Fortran_FOUND - True if PIO Fortran support was found +# PIO_VERSION - Version of installed PIO +# +# Targets provided: +# PIO::PIO_C - C interface target aliased to SHARED|STATIC as requested or to shared libraries if available else static libraries +# PIO::PIO_Fortran - Fortran interface target aliases to SHARED|STATIC as requested or to shared libraries if available else static libraries +# +# To control finding of this package, set PIO_ROOT environment variable to the full path to the prefix +# under which PIO was installed (e.g., /usr/local) + +set( _search_components ) +set( _search_library_type ) +foreach( _comp ${${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS} ) + if( _comp MATCHES "^(STATIC|SHARED)$" ) + list( APPEND _search_library_type ${_comp} ) + else() + list( APPEND _search_components ${_comp} ) + endif() +endforeach() +set( ${CMAKE_FIND_PACKAGE_NAME}_FIND_COMPONENTS ${_search_components} ) + +# If no COMPONENTS are requested, seach both C and Fortran +if( NOT _search_components ) + list( APPEND _search_components C Fortran ) +endif() + +# Ensure there is only one type of library being requested +if( _search_library_type ) + list( LENGTH _search_library_type _len) + if( _len GREATER 1 ) + message(FATAL_ERROR "User requesting both STATIC and SHARED is not permissible") + endif() + unset(_len) +endif() + +## Find libraries and paths, and determine found components +find_path(PIO_INCLUDE_DIR NAMES pio.h HINTS "${PIO_PREFIX}" PATH_SUFFIXES include include/pio) +if(PIO_INCLUDE_DIR) + string(REGEX REPLACE "/include(/.+)?" "" PIO_PREFIX ${PIO_INCLUDE_DIR}) + set(PIO_PREFIX ${PIO_PREFIX} CACHE STRING "") + find_path(PIO_MODULE_DIR NAMES pio.mod PATHS "${PIO_PREFIX}" + PATH_SUFFIXES include include/pio lib/pio/module module module/pio NO_DEFAULT_PATH) + if(APPLE) + set(_SHARED_LIB_EXT dylib) + else() + set(_SHARED_LIB_EXT so) + endif() + find_library(PIO_C_STATIC_LIB libpioc.a PATHS "${PIO_PREFIX}" PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH) + find_library(PIO_C_SHARED_LIB libpioc.${_SHARED_LIB_EXT} PATHS "${PIO_PREFIX}" PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH) + find_library(PIO_Fortran_STATIC_LIB libpiof.a PATHS "${PIO_PREFIX}" PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH) + find_library(PIO_Fortran_SHARED_LIB libpiof.${_SHARED_LIB_EXT} PATHS "${PIO_PREFIX}" PATH_SUFFIXES lib lib64 NO_DEFAULT_PATH) + unset(_SHARED_LIB_EXT) + #Check for Fortran components + if(PIO_MODULE_DIR) + if(PIO_Fortran_STATIC_LIB) + set(PIO_Fortran_STATIC_FOUND 1) + endif() + if(PIO_Fortran_SHARED_LIB) + set(PIO_Fortran_SHARED_FOUND 1) + endif() + endif() + #Check for C components + if(PIO_C_STATIC_LIB) + set(PIO_C_STATIC_FOUND 1) + endif() + if(PIO_C_SHARED_LIB) + set(PIO_C_SHARED_FOUND 1) + endif() +endif() +## Debugging output +message(DEBUG "[FindPIO] PIO_INCLUDE_DIR: ${PIO_INCLUDE_DIR}") +message(DEBUG "[FindPIO] PIO_PREFIX: ${PIO_PREFIX}") +message(DEBUG "[FindPIO] PIO_MODULE_DIR: ${PIO_MODULE_DIR}") +message(DEBUG "[FindPIO] PIO_C_STATIC_LIB: ${PIO_C_STATIC_LIB}") +message(DEBUG "[FindPIO] PIO_C_SHARED_LIB: ${PIO_C_SHARED_LIB}") +message(DEBUG "[FindPIO] PIO_C_SHARED_FOUND: ${PIO_C_SHARED_FOUND}") +message(DEBUG "[FindPIO] PIO_C_STATIC_FOUND: ${PIO_C_STATIC_FOUND}") +message(DEBUG "[FindPIO] PIO_Fortran_STATIC_LIB: ${PIO_Fortran_STATIC_LIB}") +message(DEBUG "[FindPIO] PIO_Fortran_SHARED_LIB: ${PIO_Fortran_SHARED_LIB}") +message(DEBUG "[FindPIO] PIO_Fortran_SHARED_FOUND: ${PIO_Fortran_SHARED_FOUND}") +message(DEBUG "[FindPIO] PIO_Fortran_STATIC_FOUND: ${PIO_Fortran_STATIC_FOUND}") + +## Create targets +set(_new_components) +# PIO_C_STATIC imported interface target +if(PIO_C_STATIC_FOUND AND NOT TARGET PIO_C_STATIC) + add_library(PIO_C_STATIC INTERFACE IMPORTED) + set_target_properties(PIO_C_STATIC PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${PIO_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${PIO_C_STATIC_LIB} + IMPORTED_GLOBAL True ) + set(_new_components 1) +endif() +# PIO_C_SHARED imported interface target +if(PIO_C_SHARED_FOUND AND NOT TARGET PIO_C_SHARED) + add_library(PIO_C_SHARED INTERFACE IMPORTED) + set_target_properties(PIO_C_SHARED PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${PIO_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${PIO_C_SHARED_LIB} + IMPORTED_GLOBAL True ) + set(_new_components 1) +endif() +# PIO_Fortran_STATIC imported interface target +if(PIO_Fortran_STATIC_FOUND AND NOT TARGET PIO_Fortran_STATIC) + add_library(PIO_Fortran_STATIC INTERFACE IMPORTED) + set_target_properties(PIO_Fortran_STATIC PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${PIO_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${PIO_Fortran_STATIC_LIB} + IMPORTED_GLOBAL True ) + if(PIO_MODULE_DIR AND NOT PIO_MODULE_DIR STREQUAL PIO_INCLUDE_DIR ) + set_property(TARGET PIO_Fortran_STATIC APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PIO_MODULE_DIR}) + endif() + set(_new_components 1) + target_link_libraries(PIO_Fortran_STATIC INTERFACE PIO_C_STATIC) +endif() +# PIO_Fortran_SHARED imported interface target +if(PIO_Fortran_SHARED_FOUND AND NOT TARGET PIO_Fortran_SHARED) + add_library(PIO_Fortran_SHARED INTERFACE IMPORTED) + set_target_properties(PIO_Fortran_SHARED PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${PIO_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES ${PIO_Fortran_SHARED_LIB} + IMPORTED_GLOBAL True ) + if(PIO_MODULE_DIR AND NOT PIO_MODULE_DIR STREQUAL PIO_INCLUDE_DIR ) + set_property(TARGET PIO_Fortran_SHARED APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PIO_MODULE_DIR}) + endif() + target_link_libraries(PIO_Fortran_SHARED INTERFACE PIO_C_SHARED) + set(_new_components 1) +endif() + +if( _search_library_type MATCHES "^(SHARED)$" ) + if( TARGET PIO_C_SHARED ) + add_library(PIO::PIO_C ALIAS PIO_C_SHARED) + set(PIO_C_FOUND 1) + endif() + if( TARGET PIO_Fortran_SHARED ) + add_library(PIO::PIO_Fortran ALIAS PIO_Fortran_SHARED) + set(PIO_Fortran_FOUND 1) + endif() +elseif( _search_library_type MATCHES "^(STATIC)$" ) + if( TARGET PIO_C_STATIC ) + add_library(PIO::PIO_C ALIAS PIO_C_STATIC) + set(PIO_C_FOUND 1) + endif() + if( TARGET PIO_Fortran_STATIC ) + add_library(PIO::PIO_Fortran ALIAS PIO_Fortran_STATIC) + set(PIO_Fortran_FOUND 1) + endif() +else() + if( TARGET PIO_C_SHARED ) + add_library(PIO::PIO_C ALIAS PIO_C_SHARED) + set(PIO_C_FOUND 1) + elseif( TARGET PIO_C_STATIC ) + add_library(PIO::PIO_C ALIAS PIO_C_STATIC) + set(PIO_C_FOUND 1) + endif() + if( TARGET PIO_Fortran_SHARED ) + add_library(PIO::PIO_Fortran ALIAS PIO_Fortran_SHARED) + set(PIO_Fortran_FOUND 1) + elseif( TARGET PIO_Fortran_STATIC ) + add_library(PIO::PIO_Fortran ALIAS PIO_Fortran_STATIC) + set(PIO_Fortran_FOUND 1) + endif() +endif() + +## Check package has been found correctly +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + PIO + REQUIRED_VARS + PIO_PREFIX + PIO_INCLUDE_DIR + HANDLE_COMPONENTS +) +message(DEBUG "[FindPIO] PIO_FOUND: ${PIO_FOUND}") + +## Print status +if(${CMAKE_FIND_PACKAGE_NAME}_FOUND AND NOT ${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY AND _new_components) + message( STATUS "Find${CMAKE_FIND_PACKAGE_NAME}:" ) + message( STATUS " - ${CMAKE_FIND_PACKAGE_NAME}_PREFIX [${${CMAKE_FIND_PACKAGE_NAME}_PREFIX}]") + set(_found_comps) + foreach( _comp ${_search_components} ) + if( ${CMAKE_FIND_PACKAGE_NAME}_${_comp}_FOUND ) + list(APPEND _found_comps ${_comp}) + endif() + endforeach() + message( STATUS " - ${CMAKE_FIND_PACKAGE_NAME} Components Found: ${_found_comps}") + unset(_found_comps) +endif() +unset(_new_components) +unset(_search_components) +unset(_search_library_type) +unset(_library_type) diff --git a/cmake/FortranLib.cmake b/cmake/FortranLib.cmake new file mode 100644 index 0000000000..08bcccbf8c --- /dev/null +++ b/cmake/FortranLib.cmake @@ -0,0 +1,8 @@ +function(add_fortran_library LIB MOD_DIR) + add_library(${LIB} ${ARGN}) + + get_target_property(LIB_DIR ${LIB} BINARY_DIR) + set_target_properties(${LIB} PROPERTIES Fortran_MODULE_DIRECTORY ${LIB_DIR}/${MOD_DIR}) + + target_include_directories(${LIB} INTERFACE "$") + endfunction(add_fortran_library) diff --git a/cmake/MOM6libConfig.cmake.in b/cmake/MOM6libConfig.cmake.in new file mode 100644 index 0000000000..531a90cb4c --- /dev/null +++ b/cmake/MOM6libConfig.cmake.in @@ -0,0 +1,24 @@ +@PACKAGE_INIT@ + +if(NOT MOM6lib_FIND_QUIETLY) + message(STATUS "Found MOM6lib: ${PACKAGE_PREFIX_DIR}") +endif() + +include(CMakeFindDependencyMacro) + +# Request components +set(_required_components ${MOM6lib_FIND_COMPONENTS}) + +find_dependency(fms COMPONENTS R8 REQUIRED) +find_dependency(NetCDF REQUIRED Fortran) +if (NOT NetCDF_PARALLEL) + message(FATAL_ERROR "NetCDF does not have parallel I/O support!") +endif() + +# Run the normal Targets.cmake +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) +include("${CMAKE_CURRENT_LIST_DIR}/MOM6libTargets.cmake") +list(REMOVE_ITEM CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +# Check the requested components are valid +check_required_components(_required_components) \ No newline at end of file diff --git a/config_src/drivers/nuopc_cap/mom_cap.F90 b/config_src/drivers/nuopc_cap/mom_cap.F90 index fab6fe1f55..541d7f12cc 100644 --- a/config_src/drivers/nuopc_cap/mom_cap.F90 +++ b/config_src/drivers/nuopc_cap/mom_cap.F90 @@ -2,8 +2,9 @@ module MOM_cap_mod +use field_manager_mod, only: field_manager_init, field_manager_end use MOM_domains, only: get_domain_extent -use MOM_io, only: stdout, io_infra_end +use MOM_io, only: stdout, io_infra_end, slasher use mpp_domains_mod, only: mpp_get_compute_domains use mpp_domains_mod, only: mpp_get_ntile_count, mpp_get_pelist, mpp_get_global_domain use mpp_domains_mod, only: mpp_get_domain_npes @@ -30,6 +31,7 @@ module MOM_cap_mod use MOM_cap_methods, only: ChkErr use MOM_ensemble_manager, only: ensemble_manager_init use MOM_coms, only: sum_across_PEs +use MOM_coupler_types, only: coupler_1d_bc_type, coupler_2d_bc_type #ifdef CESMCOUPLED use shr_log_mod, only: shr_log_setLogUnit @@ -37,6 +39,15 @@ module MOM_cap_mod #endif use time_utils_mod, only: esmf2fms_time +#ifdef _USE_GENERIC_TRACER +use MOM_coupler_types, only: coupler_type_spawn, coupler_type_destructor +use MOM_coupler_types, only: coupler_type_set_diags, coupler_type_send_data, coupler_type_data_override +use MOM_data_override, only: data_override_init, data_override +use MOM_cap_gtracer_flux, only: gas_exchange_init, gas_fields_restore, gas_fields_restart +use MOM_cap_gtracer_flux, only: get_coupled_field_name, add_gas_fluxes_param, UNKNOWN_CMEPS_FIELD +use MOM_cap_gtracer_flux, only: atmos_ocean_fluxes_calc +#endif + use, intrinsic :: iso_fortran_env, only: output_unit use ESMF, only: ESMF_ClockAdvance, ESMF_ClockGet, ESMF_ClockPrint, ESMF_VMget @@ -96,11 +107,14 @@ module MOM_cap_mod public SetServices public SetVM -!> Internal state type with pointers to three types defined by MOM. +!> Internal state type with pointers to types defined by MOM. type ocean_internalstate_type type(ocean_public_type), pointer :: ocean_public_type_ptr type(ocean_state_type), pointer :: ocean_state_type_ptr type(ice_ocean_boundary_type), pointer :: ice_ocean_boundary_type_ptr +#ifdef _USE_GENERIC_TRACER + type(coupler_2d_bc_type), pointer :: coupler_2d_bc_type_ptr +#endif end type !> Wrapper-derived type required to associate an internal state instance @@ -416,6 +430,10 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) type (ocean_public_type), pointer :: ocean_public => NULL() type (ocean_state_type), pointer :: ocean_state => NULL() type(ice_ocean_boundary_type), pointer :: Ice_ocean_boundary => NULL() + type(coupler_1d_bc_type), pointer :: gas_fields_atm => NULL() + type(coupler_1d_bc_type), pointer :: gas_fields_ocn => NULL() + type(coupler_1d_bc_type), pointer :: gas_fluxes => NULL() + type(coupler_2d_bc_type), pointer :: atm_fields => NULL() type(ocean_internalstate_wrapper) :: ocean_internalstate type(ocean_grid_type), pointer :: ocean_grid => NULL() type(directories) :: dirs @@ -447,6 +465,7 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) character(len=512) :: restartfile ! Path/Name of restart file character(len=2048) :: restartfiles ! Path/Name of restart files ! (same as restartfile if single restart file) + character(240) :: additional_restart_dir character(len=*), parameter :: subname='(MOM_cap:InitializeAdvertise)' character(len=32) :: calendar character(len=:), allocatable :: rpointer_filename @@ -525,6 +544,8 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) if (chkerr(rc,__LINE__,u_FILE_u)) return call MOM_infra_init(mpi_comm_mom) + call field_manager_init + ! determine the calendar if (cesm_coupled) then call NUOPC_CompAttributeGet(gcomp, name="calendar", value=cvalue, & @@ -656,13 +677,44 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) endif + ! Set NUOPC attribute additional_restart_dir to RESTART/ if not defined + additional_restart_dir = "RESTART/" + call NUOPC_CompAttributeGet(gcomp, name="additional_restart_dir", value=cvalue, & + isPresent=isPresent, isSet=isSet, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + if (isPresent .and. isSet) then + additional_restart_dir = slasher(cvalue) + else + call ESMF_LogWrite('MOM_cap:additional_restart_dir unset. Defaulting to '//trim(additional_restart_dir), & + ESMF_LOGMSG_INFO) + endif + call NUOPC_CompAttributeSet(gcomp, name="additional_restart_dir", value=additional_restart_dir, rc=rc) + if (chkerr(rc,__LINE__,u_FILE_u)) return + ocean_public%is_ocean_pe = .true. +#ifdef _USE_GENERIC_TRACER + ! Initialise structures for extra tracer fluxes + call gas_exchange_init(gas_fields_atm=gas_fields_atm, gas_fields_ocn=gas_fields_ocn, gas_fluxes=gas_fluxes) + + if (cesm_coupled .and. len_trim(inst_suffix)>0) then + call ocean_model_init(ocean_public, ocean_state, time0, time_start, gas_fields_ocn=gas_fields_ocn, & + input_restart_file=trim(adjustl(restartfiles)), inst_index=inst_index) + else + call ocean_model_init(ocean_public, ocean_state, time0, time_start, gas_fields_ocn=gas_fields_ocn, & + input_restart_file=trim(adjustl(restartfiles))) + endif + + ! Enable data override via the data_table using the component name 'OCN' + call get_ocean_grid(ocean_state, ocean_grid) + call data_override_init(ocean_grid%Domain) +#else if (cesm_coupled .and. len_trim(inst_suffix)>0) then call ocean_model_init(ocean_public, ocean_state, time0, time_start, & input_restart_file=trim(adjustl(restartfiles)), inst_index=inst_index) else call ocean_model_init(ocean_public, ocean_state, time0, time_start, input_restart_file=trim(adjustl(restartfiles))) endif +#endif ! GMM, this call is not needed in CESM. Check with EMC if it can be deleted. call ocean_model_flux_init(ocean_state) @@ -729,6 +781,31 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) Ice_ocean_boundary%hcond = 0.0 endif +#ifdef _USE_GENERIC_TRACER + ! Allocate fields for extra tracer fluxes in Ice_ocean_boundary + ! Annoyingly, spawning doesn't copy param array, so add manually + call coupler_type_spawn(gas_fluxes, Ice_ocean_boundary%fluxes, (/isc,isc,iec,iec/), & + (/jsc,jsc,jec,jec/), suffix='_ice_ocn') + call add_gas_fluxes_param(Ice_ocean_boundary%fluxes) + + ! Initialise structure for atmos fields related to extra tracer fluxes + ! This is set in the ESMF Internal State to be accessed elsewhere + ! TODO: should we deallocate atm_fields in a finalise step? Ice_ocean_boundary is handled + ! in a similar way and does not appear to be deallocated. + allocate(atm_fields) + ocean_internalstate%ptr%coupler_2d_bc_type_ptr => atm_fields + call coupler_type_spawn(gas_fields_atm, atm_fields, (/isc,isc,iec,iec/), & + (/jsc,jsc,jec,jec/), suffix='_atm') + + ! Register diagnosics for extra tracer flux structures + call coupler_type_set_diags(Ice_ocean_boundary%fluxes, "ocean_flux", ocean_public%axes(1:2), time_start) + call coupler_type_set_diags(atm_fields, "atmos_sfc", ocean_public%axes(1:2), time_start) + + ! Restore ocean fields related to extra tracer fluxes from restart files + call get_MOM_input(dirs=dirs) + call gas_fields_restore(ocean_public%fields, ocean_public%domain, dirs%restart_input_dir) +#endif + call query_ocean_state(ocean_state, use_waves=use_waves, wave_method=wave_method) if (use_waves) then if (wave_method == "EFACTOR") then @@ -797,6 +874,15 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) endif endif +#ifdef _USE_GENERIC_TRACER + ! Add import fields required for extra tracer fluxes + do n = 1, gas_fluxes%num_bcs + stdname = get_coupled_field_name(gas_fluxes%bc(n)%name) + if (stdname /= UNKNOWN_CMEPS_FIELD) & + call fld_list_add(fldsToOcn_num, fldsToOcn, stdname, "will provide") + enddo +#endif + !--------- export fields ------------- call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_omask" , "will provide") call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_t" , "will provide") @@ -808,6 +894,8 @@ subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) call fld_list_add(fldsFrOcn_num, fldsFrOcn, "Fioo_q" , "will provide") call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_bldepth" , "will provide") + ! TODO: dts: How to handle export fields from generic tracers? + do n = 1,fldsToOcn_num call NUOPC_Advertise(importState, standardName=fldsToOcn(n)%stdname, name=fldsToOcn(n)%shortname, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return @@ -1619,11 +1707,14 @@ subroutine ModelAdvance(gcomp, rc) type (ocean_public_type), pointer :: ocean_public => NULL() type (ocean_state_type), pointer :: ocean_state => NULL() type(ice_ocean_boundary_type), pointer :: Ice_ocean_boundary => NULL() + type(coupler_2d_bc_type), pointer :: atm_fields => NULL() type(ocean_internalstate_wrapper) :: ocean_internalstate type(ocean_grid_type) , pointer :: ocean_grid type(time_type) :: Time + type(time_type) :: Time_import type(time_type) :: Time_step_coupled type(time_type) :: Time_restart_current + integer :: isc,iec,jsc,jec integer :: dth, dtm, dts integer :: nc type(ESMF_Time) :: MyTime @@ -1635,12 +1726,13 @@ subroutine ModelAdvance(gcomp, rc) integer :: writeunit integer :: localPet type(ESMF_VM) :: vm - integer :: n, i + integer :: m, n, i character(240) :: import_timestr, export_timestr character(len=128) :: fldname character(len=*),parameter :: subname='(MOM_cap:ModelAdvance)' character(len=8) :: suffix character(len=:), allocatable :: rpointer_filename + character(240) :: additional_restart_dir integer :: num_rest_files real(8) :: MPI_Wtime, timers logical :: write_restart @@ -1683,6 +1775,7 @@ subroutine ModelAdvance(gcomp, rc) Time_step_coupled = esmf2fms_time(timeStep) Time = esmf2fms_time(currTime) + Time_import = Time !--------------- ! Apply ocean lag for startup runs: @@ -1758,8 +1851,39 @@ subroutine ModelAdvance(gcomp, rc) ! Import data !--------------- +#ifdef _USE_GENERIC_TRACER + atm_fields => ocean_internalstate%ptr%coupler_2d_bc_type_ptr + + call mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, atm_fields=atm_fields, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + ! Potentially override atm_fields from data_table. + call coupler_type_data_override('OCN', atm_fields, Time_import) + + ! Potentially override ice_ocean_boundary%fluxes from data_table. + ! Doing this before atmos_ocean_fluxes_calc call avoids unnecessary calculation of overridden fluxes. + ! However, we cannot use coupler_type_data_override here since it does not set the override flag on + ! overridden fields + do n = 1, ice_ocean_boundary%fluxes%num_bcs + do m = 1, ice_ocean_boundary%fluxes%bc(n)%num_fields + call data_override('OCN', ice_ocean_boundary%fluxes%bc(n)%field(m)%name, & + ice_ocean_boundary%fluxes%bc(n)%field(m)%values, Time_import, & + override=ice_ocean_boundary%fluxes%bc(n)%field(m)%override) + enddo + enddo + + ! Calculate the extra tracer fluxes + call get_domain_extent(ocean_public%domain, isc, iec, jsc, jec) + call atmos_ocean_fluxes_calc(atm_fields, ocean_public%fields, ice_ocean_boundary%fluxes, & + ice_ocean_boundary%ice_fraction, isc, iec, jsc, jec) + + ! Send diagnostics + call coupler_type_send_data(atm_fields, Time_import) + call coupler_type_send_data(ice_ocean_boundary%fluxes, Time_import) +#else call mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return +#endif !--------------- ! Update MOM6 @@ -1831,7 +1955,7 @@ subroutine ModelAdvance(gcomp, rc) ! determine restart filename call ESMF_ClockGetNextTime(clock, MyTime, rc=rc) if (ChkErr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeGet (MyTime, yy=year, mm=month, dd=day, h=hour, m=minute, s=seconds, rc=rc ) + call ESMF_TimeGet (MyTime, yy=year, mm=month, dd=day, s=seconds, rc=rc ) if (ChkErr(rc,__LINE__,u_FILE_u)) return if (cesm_coupled) then @@ -1889,6 +2013,14 @@ subroutine ModelAdvance(gcomp, rc) endif +#ifdef _USE_GENERIC_TRACER + ! Write fields for extra tracer fluxes to their internally defined ocean restart file + call NUOPC_CompAttributeGet(gcomp, name="additional_restart_dir", value=additional_restart_dir, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call gas_fields_restart(ocean_public%fields, ocean_public%domain, additional_restart_dir) +#endif + if (is_root_pe()) then write(stdout,*) subname//' writing restart file ',trim(restartname) endif @@ -2169,6 +2301,7 @@ subroutine ocean_model_finalize(gcomp, rc) integer :: alarmCount character(len=64) :: timestamp logical :: write_restart + character(240) :: additional_restart_dir character(len=*),parameter :: subname='(MOM_cap:ocean_model_finalize)' real(8) :: MPI_Wtime, timefs @@ -2202,6 +2335,18 @@ subroutine ocean_model_finalize(gcomp, rc) call ocean_model_end(ocean_public, ocean_State, Time, write_restart=write_restart) +#ifdef _USE_GENERIC_TRACER + if (write_restart) then + ! Write fields for extra tracer fluxes to their internally defined ocean restart file + call NUOPC_CompAttributeGet(gcomp, name="additional_restart_dir", value=additional_restart_dir, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + + call gas_fields_restart(ocean_public%fields, ocean_public%domain, additional_restart_dir) + endif +#endif + + call field_manager_end() + call io_infra_end() call MOM_infra_end() diff --git a/config_src/drivers/nuopc_cap/mom_cap_gtracer_flux.F90 b/config_src/drivers/nuopc_cap/mom_cap_gtracer_flux.F90 new file mode 100644 index 0000000000..af1d886b37 --- /dev/null +++ b/config_src/drivers/nuopc_cap/mom_cap_gtracer_flux.F90 @@ -0,0 +1,680 @@ +!> Contains routines for handling FMS coupler_bc_type tracer flux structures +!! when using MOM generic tracers + +module MOM_cap_gtracer_flux + +use MOM_domains, only: domain2d +use MOM_coupler_types, only: coupler_1d_bc_type, coupler_2d_bc_type +use MOM_coupler_types, only: ind_flux, ind_deltap, ind_kw, ind_flux0 +use MOM_coupler_types, only: ind_pcair, ind_u10, ind_psurf +use MOM_coupler_types, only: ind_alpha, ind_csurf, ind_sc_no +use MOM_coupler_types, only: ind_runoff, ind_deposition +use MOM_ocean_model_nuopc, only: ocean_model_flux_init +use coupler_types_mod, only: coupler_type_register_restarts, coupler_type_restore_state +use atmos_ocean_fluxes_mod, only: atmos_ocean_type_fluxes_init, atmos_ocean_fluxes_init +use fms2_io_mod, only: FmsNetcdfDomainFile_t +use fms2_io_mod, only: fms2_check_if_open => check_if_open, fms2_close_file => close_file +use fms2_io_mod, only: fms2_write_data => write_data +use fms2_io_mod, only: fms2_read_restart => read_restart, fms2_write_restart => write_restart +use fms2_io_mod, only: fms2_get_global_io_domain_indices => get_global_io_domain_indices +use field_manager_mod, only: fm_field_name_len, fm_type_name_len, fm_loop_over_list, fm_change_list +use fm_util_mod, only: fm_util_get_real_array +use mpp_mod, only: mpp_error, FATAL +use FMSconstants, only: wtmair, rdgas, vonkarm + +implicit none; private + +! Public member functions +public :: gas_exchange_init +public :: gas_fields_restore +public :: gas_fields_restart +public :: add_gas_fluxes_param +public :: get_coupled_field_name +public :: atmos_ocean_fluxes_calc +public :: UNKNOWN_CMEPS_FIELD + +character(len=*), parameter :: mod_name = 'mom_cap_gtracer_flux' +character(len=*), parameter :: UNKNOWN_CMEPS_FIELD = "UNKNOWN_FIELD" +real, parameter :: epsln=1.0e-30 + +!> FMS coupler_bc_types for additional tracer fields when using generic tracers +logical :: gas_fluxes_initialized = .false. ! This is set to true when the following types are initialized. +type(coupler_1d_bc_type), target :: ex_gas_fields_atm ! tracer fields in atm + !< Structure containing atmospheric surface variables that are used in the + !! calculation of the atmosphere-ocean gas fluxes, as well as parameters + !! regulating these fluxes. The fields in this structure are never actually + !! set, but the structure is used for initialisation of components and to + !! spawn other structure whose fields are set. +type(coupler_1d_bc_type), target :: ex_gas_fields_ocn ! tracer fields atop the ocean + !< Structure containing ocean surface variables that are used in the + !! calculation of the atmosphere-ocean gas fluxes, as well as parameters + !! regulating these fluxes. The fields in this structure are never actually + !! set, but the structure is used for initialisation of components and to + !! spawn other structure whose fields are set. +type(coupler_1d_bc_type), target :: ex_gas_fluxes ! tracer fluxes between the atm and ocean + !< A structure for exchanging gas or tracer fluxes between the atmosphere and + !! ocean, defined by the field table, as well as a place holder of + !! intermediate calculations, such as piston velocities, and parameters that + !! impact the fluxes. The fields in this structure are never actually set, + !! but the structure is used for initialisation of components and to spawn + !! other structure whose fields are set. + +contains + +!> \brief Gas and tracer field initialization routine for running with MOM generic tracers. +!! Copied and adapted slightly from +!! https://github.com/NOAA-GFDL/FMScoupler/blob/7761886/full/flux_exchange.F90#L626. +subroutine gas_exchange_init (gas_fields_atm, gas_fields_ocn, gas_fluxes) + type(coupler_1d_bc_type), optional, pointer :: gas_fields_atm ! tracer fields in atm + !< Pointer to a structure containing atmospheric surface variables that + !! are used in the calculation of the atmosphere-ocean gas fluxes, as well + !! as parameters regulating these fluxes. + type(coupler_1d_bc_type), optional, pointer :: gas_fields_ocn ! tracer fields atop the ocean + !< Pointer to a structure containing ocean surface variables that are + !! used in the calculation of the atmosphere-ocean gas fluxes, as well as + !! parameters regulating these fluxes. + type(coupler_1d_bc_type), optional, pointer :: gas_fluxes ! tracer fluxes between the atm and ocean + !< Pointer to a structure for exchanging gas or tracer fluxes between the + !! atmosphere and ocean, defined by the field table, as well as a place holder + !! of intermediate calculations, such as piston velocities, and parameters + !! that impact the fluxes. + + if (.not.gas_fluxes_initialized) then + call atmos_ocean_type_fluxes_init( ) + call ocean_model_flux_init( ) + call atmos_ocean_fluxes_init(ex_gas_fluxes, ex_gas_fields_atm, ex_gas_fields_ocn) + gas_fluxes_initialized = .true. + endif + + if (present(gas_fields_atm)) gas_fields_atm => ex_gas_fields_atm + if (present(gas_fields_ocn)) gas_fields_ocn => ex_gas_fields_ocn + if (present(gas_fluxes)) gas_fluxes => ex_gas_fluxes + +end subroutine gas_exchange_init + +!> \brief Restore FMS coupler_bc_type state from ocean restart file +! See https://github.com/NOAA-GFDL/FMScoupler/blob/008399d/full/full_coupler_mod.F90#L1096 +subroutine gas_fields_restore(gas_fields, domain, directory) + type(coupler_2d_bc_type), intent(inout) :: gas_fields !< FMS coupler_bc_type to be registered for restarts + type(domain2D), intent(in) :: domain !< The FMS domain to use for this registration call + character(len=*), optional, intent(in) :: directory !< Directory containing the restart file + + ! local variables + type(FmsNetcdfDomainFile_t), dimension(:), pointer :: ocn_bc_restart => NULL() !< Structures describing + !! the restart files + integer :: num_ocn_bc_restart !< The number of restart + !! files to use + integer :: n + + call coupler_type_register_restarts(gas_fields, ocn_bc_restart, num_ocn_bc_restart, & + domain, to_read=.true., ocean_restart=.true., directory=directory) + + ! Restore the fields from the restart files + do n = 1, num_ocn_bc_restart + if (fms2_check_if_open(ocn_bc_restart(n))) then + call fms2_read_restart(ocn_bc_restart(n)) + endif + enddo + + ! Check whether the restarts were read successfully. + call coupler_type_restore_state(gas_fields, use_fms2_io=.true., test_by_field=.true.) + + do n = 1, num_ocn_bc_restart + if(fms2_check_if_open(ocn_bc_restart(n))) call fms2_close_file(ocn_bc_restart(n)) + enddo + +end subroutine gas_fields_restore + +!> \brief Write ocean restart file for FMS coupler_bc_type state +! See https://github.com/NOAA-GFDL/FMScoupler/blob/008399d/full/full_coupler_mod.F90#L1408 +subroutine gas_fields_restart(gas_fields, domain, directory) + type(coupler_2d_bc_type), intent(inout) :: gas_fields !< FMS coupler_bc_type to be registered for restarts + type(domain2D), intent(in) :: domain !< The FMS domain to use for this registration call + character(len=*), optional, intent(in) :: directory !< Directory containing the restart file + + ! local variables + type(FmsNetcdfDomainFile_t), dimension(:), pointer :: ocn_bc_restart => NULL() !< Structures describing + !! the restart files + integer :: num_ocn_bc_restart !< The number of restart + !! files to use + integer :: n + + call coupler_type_register_restarts(gas_fields, ocn_bc_restart, num_ocn_bc_restart, & + domain, to_read=.false., ocean_restart=.true., directory=directory) + + do n = 1, num_ocn_bc_restart + if (fms2_check_if_open(ocn_bc_restart(n))) then + call fms2_write_restart(ocn_bc_restart(n)) + call add_domain_dimension_data(ocn_bc_restart(n)) + call fms2_close_file(ocn_bc_restart(n)) + endif + enddo + +end subroutine gas_fields_restart + +!> Register the axis data as a variable in the netcdf file and add some dummy data. +!! This is needed so the combiner can work correctly when the io_layout is not 1,1. Copied from +!! https://github.com/NOAA-GFDL/FMScoupler/blob/008399d/full/full_coupler_mod.F90#L1328 +subroutine add_domain_dimension_data(fileobj) + type(FmsNetcdfDomainFile_t) :: fileobj !< Fms2io domain decomposed fileobj + + ! local variables + integer, dimension(:), allocatable :: buffer !< Buffer with axis data + integer :: is, ie !< Starting and Ending indices for data + + call fms2_get_global_io_domain_indices(fileobj, "xaxis_1", is, ie, indices=buffer) + call fms2_write_data(fileobj, "xaxis_1", buffer) + deallocate(buffer) + + call fms2_get_global_io_domain_indices(fileobj, "yaxis_1", is, ie, indices=buffer) + call fms2_write_data(fileobj, "yaxis_1", buffer) + deallocate(buffer) + +end subroutine add_domain_dimension_data + +!> Retrieve param array from field_manager and add to FMS coupler_bc_type. This is +!! needed because the coupler_type_spawn routine does not copy the param array into +!! the spawned type. Hopefully we can get rid of this. This routine is based on +!! https://github.com/NOAA-GFDL/FMS/blob/7f58528/coupler/atmos_ocean_fluxes.F90#L448 +subroutine add_gas_fluxes_param(gas_fluxes) + type(coupler_2d_bc_type), intent(inout) :: gas_fluxes !< FMS coupler_bc_type to add param to + + ! local variables + integer :: n + character(len=fm_field_name_len) :: name + character(len=fm_type_name_len) :: typ + integer :: ind + + character(len=*), parameter :: sub_name = 'add_gas_fluxes_param' + character(len=*), parameter :: error_header =& + '==>Error from ' // trim(mod_name) // '(' // trim(sub_name) // '):' + + n = 0 + do while (fm_loop_over_list('/coupler_mod/fluxes', name, typ, ind)) + if (typ .ne. 'list') then + call mpp_error(FATAL, trim(error_header) // ' ' // trim(name) // ' is not a list') + endif + + n = n + 1 + + if (.not. fm_change_list('/coupler_mod/fluxes/' // trim(name))) then + call mpp_error(FATAL, trim(error_header) // ' Problem changing to ' // trim(name)) + endif + + if (gas_fluxes%bc(n)%name .eq. name) then + gas_fluxes%bc(n)%param => fm_util_get_real_array('param') + else + call mpp_error(FATAL, trim(error_header) // ' Problem setting param array pointer') + endif + enddo +end subroutine add_gas_fluxes_param + +!> Return the CMEPS standard_name of the coupled field required for a given coupled +!! generic_tracer flux name. +function get_coupled_field_name(name) + character(len=64) :: get_coupled_field_name !< CMEPS standard_name + character(len=*), intent(in) :: name !< gtracer flux name + + ! Add other coupled field names here + select case(trim(name)) + case( 'co2_flux' ) + get_coupled_field_name = "Sa_co2prog" + case default + get_coupled_field_name = UNKNOWN_CMEPS_FIELD + end select +end function get_coupled_field_name + +!> \brief Calculate the FMS coupler_bc_type ocean tracer fluxes. Units should be mol/m^2/s. +!! Upward flux is positive. +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +!! and subsequently modified in the following ways: +!! - Operate on 2D inputs, rather than 1D +!! - Add calculation for 'air_sea_deposition' taken from +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_dep_fluxes_calc.F90 +!! - Multiply fluxes by ice_fraction input, rather than masking based on seawater input +!! - Use MOM over FMS modules where easy to do so +!! - Make tsurf input optional, as it is only used by a few implementations +!! - Use ind_runoff rather than ind_deposition in runoff flux calculation (note, their +!! values are equal) +!! - Rename gas_fields_ice to gas_fields_ocn +subroutine atmos_ocean_fluxes_calc(gas_fields_atm, gas_fields_ocn, gas_fluxes,& + ice_fraction, isc, iec, jsc, jec, tsurf, ustar, cd_m) + type(coupler_2d_bc_type), intent(in) :: gas_fields_atm ! fields in atm + !< Structure containing atmospheric surface variables that are used in the calculation + !! of the atmosphere-ocean tracer fluxes. + type(coupler_2d_bc_type), intent(in) :: gas_fields_ocn ! fields atop the ocean + !< Structure containing ocean surface variables that are used in the calculation of the + !! atmosphere-ocean tracer fluxes. + type(coupler_2d_bc_type), intent(inout) :: gas_fluxes ! fluxes between the atm and ocean + !< Structure containing the gas fluxes between the atmosphere and the ocean and + !! parameters related to the calculation of these fluxes. + real, intent(in) :: ice_fraction(isc:iec,jsc:jec) !< sea ice fraction + integer, intent(in) :: isc !< The start i-index of cell centers within + !! the computational domain + integer, intent(in) :: iec !< The end i-index of cell centers within the + !! computational domain + integer, intent(in) :: jsc !< The start j-index of cell centers within + !! the computational domain + integer, intent(in) :: jec !< The end j-index of cell centers within the + !! computational domain + real, intent(in), optional :: tsurf(isc:iec,jsc:jec) !< surface temperature + real, intent(in), optional :: ustar(isc:iec,jsc:jec) !< friction velocity, not + !! used + real, intent(in), optional :: cd_m (isc:iec,jsc:jec) !< drag coefficient, not + !! used + + ! local variables + character(len=*), parameter :: sub_name = 'atmos_ocean_fluxes_calc' + character(len=*), parameter :: error_header =& + & '==>Error from ' // trim(mod_name) // '(' // trim(sub_name) // '):' + real, parameter :: permeg=1.0e-6 + + integer :: n + integer :: i + integer :: j + real, dimension(:,:), allocatable :: kw + real, dimension(:,:), allocatable :: cair + character(len=128) :: error_string + + ! Return if no fluxes to be calculated + if (gas_fluxes%num_bcs .le. 0) return + + if (.not. associated(gas_fluxes%bc)) then + if (gas_fluxes%num_bcs .ne. 0) then + call mpp_error(FATAL, trim(error_header) // ' Number of gas fluxes not zero') + else + return + endif + endif + + do n = 1, gas_fluxes%num_bcs + ! only do calculations if the flux has not been overridden + if ( .not. gas_fluxes%bc(n)%field(ind_flux)%override) then + if (gas_fluxes%bc(n)%flux_type .eq. 'air_sea_gas_flux_generic') then + if (.not. allocated(kw)) then + allocate( kw(isc:iec,jsc:jec) ) + allocate ( cair(isc:iec,jsc:jec) ) + elseif ((size(kw(:,:), dim=1) .ne. iec-isc+1) .or. (size(kw(:,:), dim=2) .ne. jec-jsc+1)) then + call mpp_error(FATAL, trim(error_header) // ' Sizes of flux fields do not match') + endif + + if (gas_fluxes%bc(n)%implementation .eq. 'ocmip2') then + do j = jsc,jec + do i = isc,iec + gas_fluxes%bc(n)%field(ind_kw)%values(i,j) =& + & (1 - ice_fraction(i,j)) * gas_fluxes%bc(n)%param(1) * & + & gas_fields_atm%bc(n)%field(ind_u10)%values(i,j)**2 + cair(i,j) = & + gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) * & + gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) * & + gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * gas_fluxes%bc(n)%param(2) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & sqrt(660. / (gas_fields_ocn%bc(n)%field(ind_sc_no)%values(i,j) + epsln)) *& + & (gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) - cair(i,j)) + gas_fluxes%bc(n)%field(ind_flux0)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & sqrt(660. / (gas_fields_ocn%bc(n)%field(ind_sc_no)%values(i,j) + epsln)) *& + & gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) + gas_fluxes%bc(n)%field(ind_deltap)%values(i,j) =& + & (gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) - cair(i,j)) / & + (gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) * permeg + epsln) + enddo + enddo + elseif (gas_fluxes%bc(n)%implementation .eq. 'duce') then + if (.not. present(tsurf)) then + call mpp_error(FATAL, trim(error_header) // ' Implementation ' //& + trim(gas_fluxes%bc(n)%implementation) // ' for ' // trim(gas_fluxes%bc(n)%name) //& + ' requires input tsurf') + endif + do j = jsc,jec + do i = isc,iec + gas_fluxes%bc(n)%field(ind_kw)%values(i,j) = & + & (1 - ice_fraction(i,j)) * gas_fields_atm%bc(n)%field(ind_u10)%values(i,j) /& + & (770.+45.*gas_fluxes%bc(n)%param(1)**(1./3.)) *& + & 101325./(rdgas*wtmair*1e-3*tsurf(i,j) *& + & max(gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j),epsln)) + !alpha: mol/m3/atm + cair(i,j) = & + gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) * & + gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) * & + gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * 9.86923e-6 + cair(i,j) = max(cair(i,j),0.) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & (max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) - cair(i,j)) + gas_fluxes%bc(n)%field(ind_flux0)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) + gas_fluxes%bc(n)%field(ind_deltap)%values(i,j) =& + & (max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) - cair(i,j)) /& + & (gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) * permeg + epsln) + enddo + enddo + elseif (gas_fluxes%bc(n)%implementation .eq. 'johnson') then + if (.not. present(tsurf)) then + call mpp_error(FATAL, trim(error_header) // ' Implementation ' //& + trim(gas_fluxes%bc(n)%implementation) // ' for ' // trim(gas_fluxes%bc(n)%name) //& + ' requires input tsurf') + endif + !f1p: not sure how to pass salinity. For now, just force at 35. + do j = jsc,jec + do i = isc,iec + !calc_kw(tk,p,u10,h,vb,mw,sc_w,ustar,cd_m) + gas_fluxes%bc(n)%field(ind_kw)%values(i,j) =& + & (1 - ice_fraction(i,j)) * calc_kw(tsurf(i,j),& + & gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j),& + & gas_fields_atm%bc(n)%field(ind_u10)%values(i,j),& + & 101325./(rdgas*wtmair*1e-3*tsurf(i,j)*& + & max(gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j),epsln)),& + & gas_fluxes%bc(n)%param(2),& + & gas_fluxes%bc(n)%param(1),& + & gas_fields_ocn%bc(n)%field(ind_sc_no)%values(i,j)) + cair(i,j) =& + & gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * 9.86923e-6 + cair(i,j) = max(cair(i,j),0.) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & (max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) - cair(i,j)) + gas_fluxes%bc(n)%field(ind_flux0)%values(i,j) =& + & gas_fluxes%bc(n)%field(ind_kw)%values(i,j) *& + & max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) + gas_fluxes%bc(n)%field(ind_deltap)%values(i,j) =& + & (max(gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j),0.) - cair(i,j)) /& + & (gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) * permeg + epsln) + enddo + enddo + else + call mpp_error(FATAL, ' Unknown implementation (' //& + & trim(gas_fluxes%bc(n)%implementation) // ') for ' // trim(gas_fluxes%bc(n)%name)) + endif + elseif (gas_fluxes%bc(n)%flux_type .eq. 'air_sea_gas_flux') then + if (.not. allocated(kw)) then + allocate( kw(isc:iec,jsc:jec) ) + allocate ( cair(isc:iec,jsc:jec) ) + elseif ((size(kw(:,:), dim=1) .ne. iec-isc+1) .or. (size(kw(:,:), dim=2) .ne. jec-jsc+1)) then + call mpp_error(FATAL, trim(error_header) // ' Sizes of flux fields do not match') + endif + + if (gas_fluxes%bc(n)%implementation .eq. 'ocmip2_data') then + do j = jsc,jec + do i = isc,iec + kw(i,j) = (1 - ice_fraction(i,j)) * gas_fluxes%bc(n)%param(1) *& + & gas_fields_atm%bc(n)%field(ind_u10)%values(i,j) + cair(i,j) =& + & gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * gas_fluxes%bc(n)%param(2) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = kw(i,j) *& + & (gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) - cair(i,j)) + enddo + enddo + elseif (gas_fluxes%bc(n)%implementation .eq. 'ocmip2') then + do j = jsc,jec + do i = isc,iec + kw(i,j) = (1 - ice_fraction(i,j)) * gas_fluxes%bc(n)%param(1) *& + & gas_fields_atm%bc(n)%field(ind_u10)%values(i,j)**2 + cair(i,j) =& + & gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * gas_fluxes%bc(n)%param(2) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = kw(i,j) *& + & (gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) - cair(i,j)) + enddo + enddo + elseif (gas_fluxes%bc(n)%implementation .eq. 'linear') then + do j = jsc,jec + do i = isc,iec + kw(i,j) = (1 - ice_fraction(i,j)) * gas_fluxes%bc(n)%param(1) *& + & max(0.0, gas_fields_atm%bc(n)%field(ind_u10)%values(i,j) - gas_fluxes%bc(n)%param(2)) + cair(i,j) =& + & gas_fields_ocn%bc(n)%field(ind_alpha)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_pcair)%values(i,j) *& + & gas_fields_atm%bc(n)%field(ind_psurf)%values(i,j) * gas_fluxes%bc(n)%param(3) + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = kw(i,j) *& + & (gas_fields_ocn%bc(n)%field(ind_csurf)%values(i,j) - cair(i,j)) + enddo + enddo + else + call mpp_error(FATAL, ' Unknown implementation (' //& + & trim(gas_fluxes%bc(n)%implementation) // ') for ' // trim(gas_fluxes%bc(n)%name)) + endif + elseif (gas_fluxes%bc(n)%flux_type .eq. 'air_sea_deposition') then + if (gas_fluxes%bc(n)%param(1) .le. 0.0) then + write (error_string, '(1pe10.3)') gas_fluxes%bc(n)%param(1) + call mpp_error(FATAL, 'Bad parameter (' // trim(error_string) //& + & ') for air_sea_deposition for ' // trim(gas_fluxes%bc(n)%name)) + endif + + if (gas_fluxes%bc(n)%implementation .eq. 'dry') then + do j = jsc,jec + do i = isc,iec + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = (1 - ice_fraction(i,j)) *& + gas_fields_atm%bc(n)%field(ind_deposition)%values(i,j) / gas_fluxes%bc(n)%param(1) + enddo + enddo + elseif (gas_fluxes%bc(n)%implementation .eq. 'wet') then + do j = jsc,jec + do i = isc,iec + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = (1 - ice_fraction(i,j)) *& + gas_fields_atm%bc(n)%field(ind_deposition)%values(i,j) / gas_fluxes%bc(n)%param(1) + enddo + enddo + else + call mpp_error(FATAL, 'Unknown implementation (' //& + & trim(gas_fluxes%bc(n)%implementation) // ') for ' // trim(gas_fluxes%bc(n)%name)) + endif + elseif (gas_fluxes%bc(n)%flux_type .eq. 'land_sea_runoff') then + if (gas_fluxes%bc(n)%param(1) .le. 0.0) then + write (error_string, '(1pe10.3)') gas_fluxes%bc(n)%param(1) + call mpp_error(FATAL, ' Bad parameter (' // trim(error_string) //& + & ') for land_sea_runoff for ' // trim(gas_fluxes%bc(n)%name)) + endif + + if (gas_fluxes%bc(n)%implementation .eq. 'river') then + do j = jsc,jec + do i = isc,iec + gas_fluxes%bc(n)%field(ind_flux)%values(i,j) = (1 - ice_fraction(i,j)) *& + & gas_fields_atm%bc(n)%field(ind_runoff)%values(i,j) /& + & gas_fluxes%bc(n)%param(1) + enddo + enddo + else + call mpp_error(FATAL, ' Unknown implementation (' //& + & trim(gas_fluxes%bc(n)%implementation) // ') for ' // trim(gas_fluxes%bc(n)%name)) + endif + else + call mpp_error(FATAL, ' Unknown flux_type (' // trim(gas_fluxes%bc(n)%flux_type) //& + & ') for ' // trim(gas_fluxes%bc(n)%name)) + endif + endif + enddo + + if (allocated(kw)) then + deallocate(kw) + deallocate(cair) + endif +end subroutine atmos_ocean_fluxes_calc + +!> Calculate \f$k_w\f$ +!! +!! Taken from Johnson, Ocean Science, 2010. (http://doi.org/10.5194/os-6-913-2010) +!! +!! Uses equations defined in Liss[1974], +!! \f[ +!! F = K_g(c_g - H C_l) = K_l(c_g/H - C_l) +!! \f] +!! where \f$c_g\f$ and \f$C_l\f$ are the bulk gas and liquid concentrations, \f$H\f$ +!! is the Henry's law constant (\f$H = c_{sg}/C_{sl}\f$, where \f$c_{sg}\f$ is the +!! equilibrium concentration in gas phase (\f$g/cm^3\f$ of air) and \f$C_{sl}\f$ is the +!! equilibrium concentration of unionised dissolved gas in liquid phase (\f$g/cm^3\f$ +!! of water)), +!! \f[ +!! 1/K_g = 1/k_g + H/k_l +!! \f] +!! and +!! \f[ +!! 1/K_l = 1/k_l + 1/{Hk_g} +!! \f] +!! where \f$k_g\f$ and \f$k_l\f$ are the exchange constants for the gas and liquid +!! phases, respectively. +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function calc_kw(tk, p, u10, h, vb, mw, sc_w, ustar, cd_m) + real, intent(in) :: tk !< temperature at surface in kelvin + real, intent(in) :: p !< pressure at surface in pa + real, intent(in) :: u10 !< wind speed at 10m above the surface in m/s + real, intent(in) :: h !< Henry's law constant (\f$H=c_sg/C_sl\f$) (unitless) + real, intent(in) :: vb !< Molar volume + real, intent(in) :: mw !< molecular weight (g/mol) + real, intent(in) :: sc_w + real, intent(in), optional :: ustar !< Friction velocity (m/s). If not provided, + !! ustar = \f$u_{10} \sqrt{C_D}\f$. + real, intent(in), optional :: cd_m !< Drag coefficient (\f$C_D\f$). Used only if + !! ustar is provided. + !! If ustar is not provided, + !! cd_m = \f$6.1 \times 10^{-4} + 0.63 \times 10^{-4} *u_10\f$ + + real :: ra,rl,tc + + tc = tk-273.15 + ra = 1./max(h*calc_ka(tc,p,mw,vb,u10,ustar,cd_m),epsln) + rl = 1./max(calc_kl(tc,u10,sc_w),epsln) + calc_kw = 1./max(ra+rl,epsln) +end function calc_kw + +!> Calculate \f$k_a\f$ +!! +!! See calc_kw +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function calc_ka(t, p, mw, vb, u10, ustar, cd_m) + real, intent(in) :: t !< temperature at surface in C + real, intent(in) :: p !< pressure at surface in pa + real, intent(in) :: mw !< molecular weight (g/mol) + real, intent(in) :: vb !< molar volume + real, intent(in) :: u10 !< wind speed at 10m above the surface in m/s + real, intent(in), optional :: ustar !< Friction velocity (m/s). If not provided, + !! ustar = \f$u_{10} \sqrt{C_D}\f$. + real, intent(in), optional :: cd_m !< Drag coefficient (\f$C_D\f$). Used only if + !! ustar is provided. + !! If ustar is not provided, + !! cd_m = \f$6.1 \times 10^{-4} + 0.63 \times 10^{-4} *u_10\f$ + + real :: sc + real :: ustar_t, cd_m_t + + if (.not. present(ustar)) then + !drag coefficient + cd_m_t = 6.1e-4 +0.63e-4*u10 + !friction velocity + ustar_t = u10*sqrt(cd_m_t) + else + cd_m_t = cd_m + ustar_t = ustar + end if + sc = schmidt_g(t,p,mw,vb) + calc_ka = 1e-3+ustar_t/(13.3*sqrt(sc)+1/sqrt(cd_m_t)-5.+log(sc)/(2.*vonkarm)) +end function calc_ka + +!> Calculate \f$k_l\f$ +!! +!! See calc_kw, and Nightingale, Global Biogeochemical Cycles, 2000 +!! (https://doi.org/10.1029/1999GB900091) +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function calc_kl(t, v, sc) + real, intent(in) :: t !< temperature at surface in C + real, intent(in) :: v !< wind speed at surface in m/s + real, intent(in) :: sc + + calc_kl = (((0.222*v**2)+0.333*v)*(max(sc,epsln)/600.)**(-0.5))/(100.*3600.) +end function calc_kl + +!> Schmidt number of the gas in air +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function schmidt_g(t, p, mw, vb) + real, intent(in) :: t !< temperature at surface in C + real, intent(in) :: p !< pressure at surface in pa + real, intent(in) :: mw !< molecular weight (g/mol) + real, intent(in) :: vb !< molar volume + + real :: d,v + + d = d_air(t,p,mw,vb) + v = v_air(t) + schmidt_g = v / d +end function schmidt_g + +!> From Fuller, Industrial & Engineering Chemistry (https://doi.org/10.1021/ie50677a007) +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function d_air(t, p, mw, vb) + real, intent(in) :: t !< temperature in c + real, intent(in) :: p !< pressure in pa + real, intent(in) :: mw !< molecular weight (g/mol) + real, intent(in) :: vb !< diffusion coefficient (\f$cm3/mol\f$) + + real, parameter :: ma = 28.97d0 !< molecular weight air in g/mol + real, parameter :: va = 20.1d0 !< diffusion volume for air (\f$cm^3/mol\f$) + + real :: pa + + ! convert p to atm + pa = 9.8692d-6*p + d_air = 1d-3 *& + & (t+273.15d0)**(1.75d0)*sqrt(1d0/ma + 1d0/mw)/(pa*(va**(1d0/3d0)+vb**(1d0/3d0))**2d0) + ! d_air is in cm2/s convert to m2/s + d_air = d_air * 1d-4 +end function d_air + +!> kinematic viscosity in air +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function p_air(t) + real, intent(in) :: t + + real, parameter :: sd_0 = 1.293393662d0,& + & sd_1 = -5.538444326d-3,& + & sd_2 = 3.860201577d-5,& + & sd_3 = -5.2536065d-7 + p_air = sd_0+(sd_1*t)+(sd_2*t**2)+(sd_3*t**3) +end function p_air + +!> Kinematic viscosity in air (\f$m^2/s\f$ +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function v_air(t) + real, intent(in) :: t !< temperature in C + v_air = n_air(t)/p_air(t) +end function v_air + +!> dynamic viscosity in air +!! +!! This routine was copied from FMScoupler at +!! https://github.com/NOAA-GFDL/FMScoupler/blob/6442d38/full/atmos_ocean_fluxes_calc.F90 +real function n_air(t) + real, intent(in) :: t !< temperature in C + + real, parameter :: sv_0 = 1.715747771d-5,& + & sv_1 = 4.722402075d-8,& + & sv_2 = -3.663027156d-10,& + & sv_3 = 1.873236686d-12,& + & sv_4 = -8.050218737d-14 + ! in n.s/m^2 (pa.s) + n_air = sv_0+(sv_1*t)+(sv_2*t**2)+(sv_3*t**3)+(sv_4*t**4) +end function n_air + +end module MOM_cap_gtracer_flux \ No newline at end of file diff --git a/config_src/drivers/nuopc_cap/mom_cap_methods.F90 b/config_src/drivers/nuopc_cap/mom_cap_methods.F90 index 125bae5748..3f81f6aa7e 100644 --- a/config_src/drivers/nuopc_cap/mom_cap_methods.F90 +++ b/config_src/drivers/nuopc_cap/mom_cap_methods.F90 @@ -20,8 +20,15 @@ module MOM_cap_methods use MOM_surface_forcing_nuopc, only: ice_ocean_boundary_type use MOM_grid, only: ocean_grid_type use MOM_domains, only: pass_var +use MOM_coupler_types, only: coupler_2d_bc_type use mpp_domains_mod, only: mpp_get_compute_domain +#ifdef _USE_GENERIC_TRACER +use MOM_coupler_types, only: set_coupler_type_data +use MOM_coupler_types, only: ind_pcair, ind_u10, ind_psurf, ind_runoff, ind_deposition +use MOM_cap_gtracer_flux, only: get_coupled_field_name, UNKNOWN_CMEPS_FIELD +#endif + ! By default make data private implicit none; private @@ -72,11 +79,17 @@ end subroutine mom_set_geomtype !> This function has a few purposes: !! (1) it imports surface fluxes using data from the mediator; and !! (2) it can apply restoring in SST and SSS. -subroutine mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, rc) +!! (3) optional: if atm_fields is provided, it imports and sets the fields in atm_fields required +!! for the calculation of coupled generic tracer fluxes +subroutine mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, atm_fields, rc) type(ocean_public_type) , intent(in) :: ocean_public !< Ocean surface state type(ocean_grid_type) , intent(in) :: ocean_grid !< Ocean model grid type(ESMF_State) , intent(inout) :: importState !< incoming data from mediator type(ice_ocean_boundary_type) , intent(inout) :: ice_ocean_boundary !< Ocean boundary forcing + type(coupler_2d_bc_type), optional, intent(inout) :: atm_fields !< If present, this type + !! describes the atmospheric tracer fields to + !! be imported for the calculation of generic + !! tracer fluxes. integer , intent(inout) :: rc !< Return code ! Local Variables @@ -88,7 +101,10 @@ subroutine mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, real(ESMF_KIND_R8), allocatable :: tauy(:,:) real(ESMF_KIND_R8), allocatable :: stkx(:,:,:) real(ESMF_KIND_R8), allocatable :: stky(:,:,:) + real(ESMF_KIND_R8), allocatable :: work(:,:) character(len=*) , parameter :: subname = '(mom_import)' + character(len=256) :: stdname + integer :: field_index rc = ESMF_SUCCESS @@ -364,6 +380,48 @@ subroutine mom_import(ocean_public, ocean_grid, importState, ice_ocean_boundary, deallocate(stkx,stky) endif + !--- + ! Tracer flux fields for generic tracers + !--- +#ifdef _USE_GENERIC_TRACER + if (present(atm_fields)) then + ! Set fields in atm_fields from coupler + allocate (work(isc:iec,jsc:jec)) + do n = 1, atm_fields%num_bcs + if (atm_fields%bc(n)%flux_type .eq. 'air_sea_deposition') then + field_index = ind_deposition + elseif (atm_fields%bc(n)%flux_type .eq. 'land_sea_runoff') then + field_index = ind_runoff + else + ! This is a gas flux - set ind_u10 and ind_psurf + ! Note, we set these fields even though the pcair field may not be set below. This + ! is to allow flux calculation with overridden pcair fields + field_index = ind_pcair + call set_coupler_type_data(sqrt(ice_ocean_boundary%u10_sqr), n, atm_fields, & + idim=(/isc,isc,iec,iec/), jdim=(/jsc,jsc,jec,jec/), field_index=ind_u10) + call set_coupler_type_data(ice_ocean_boundary%p, n, atm_fields, & + idim=(/isc,isc,iec,iec/), jdim=(/jsc,jsc,jec,jec/), field_index=ind_psurf) + endif + + stdname = get_coupled_field_name(atm_fields%bc(n)%name) + if (stdname /= UNKNOWN_CMEPS_FIELD) then + call ESMF_LogWrite(trim(subname)//': generic_tracer flux, '//trim(atm_fields%bc(n)%name)//& + ': setting field index '//CHAR(48+field_index)//' to '//trim(stdname)//' if provided '//& + 'by coupler, otherwise defaulting to zero', ESMF_LOGMSG_INFO) + work(:,:) = 0._ESMF_KIND_R8 + call state_getimport(importState, trim(stdname), isc, iec, jsc, jec, work, rc=rc) + if (ChkErr(rc,__LINE__,u_FILE_u)) return + call set_coupler_type_data(work, n, atm_fields, & + idim=(/isc,isc,iec,iec/), jdim=(/jsc,jsc,jec,jec/), field_index=field_index) + else + call ESMF_LogWrite(trim(subname)//': generic_tracer flux, '//trim(atm_fields%bc(n)%name)//& + ': no fields set from coupler', ESMF_LOGMSG_INFO) + endif + enddo + deallocate(work) + endif +#endif + end subroutine mom_import !> Maps outgoing ocean data to ESMF State diff --git a/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 b/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 index 9ac40daaa4..b6cc4de0be 100644 --- a/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 +++ b/config_src/drivers/nuopc_cap/mom_ocean_model_nuopc.F90 @@ -20,6 +20,7 @@ module MOM_ocean_model_nuopc use MOM_constants, only : CELSIUS_KELVIN_OFFSET, hlf use MOM_diag_mediator, only : diag_ctrl, enable_averages, disable_averaging use MOM_diag_mediator, only : diag_mediator_close_registration, diag_mediator_end +use MOM_domains, only : MOM_domain_type, domain2d, clone_MOM_domain, get_domain_extent use MOM_domains, only : pass_var, pass_vector, AGRID, BGRID_NE, CGRID_NE use MOM_domains, only : TO_ALL, Omit_Corners use MOM_error_handler, only : MOM_error, FATAL, WARNING, is_root_pe @@ -51,8 +52,6 @@ module MOM_ocean_model_nuopc use MOM_coupler_types, only : coupler_type_spawn, coupler_type_write_chksums use MOM_coupler_types, only : coupler_type_initialized, coupler_type_copy_data use MOM_coupler_types, only : coupler_type_set_diags, coupler_type_send_data -use mpp_domains_mod, only : domain2d, mpp_get_layout, mpp_get_global_domain -use mpp_domains_mod, only : mpp_define_domains, mpp_get_compute_domain, mpp_get_data_domain use MOM_io, only : stdout use MOM_EOS, only : gsw_sp_from_sr, gsw_pt_from_ct use MOM_wave_interface, only : wave_parameters_CS, MOM_wave_interface_init @@ -102,7 +101,7 @@ module MOM_ocean_model_nuopc !! points of the two velocity components. Valid entries !! include AGRID, BGRID_NE, CGRID_NE, BGRID_SW, and CGRID_SW, !! corresponding to the community-standard Arakawa notation. - !! (These are named integers taken from mpp_parameter_mod.) + !! (These are named integers taken from the MOM_domains module.) !! Following MOM5, stagger is BGRID_NE by default when the !! ocean is initialized, but here it is set to -999 so that !! a global max across ocean and non-ocean processors can be @@ -407,14 +406,7 @@ subroutine ocean_model_init(Ocean_sfc, OS, Time_init, Time_in, gas_fields_ocn, i ! it also initializes statistical waves. call MOM_wave_interface_init(OS%Time, OS%grid, OS%GV, OS%US, param_file, OS%Waves, OS%diag) - if (associated(OS%grid%Domain%maskmap)) then - call initialize_ocean_public_type(OS%grid%Domain%mpp_domain, Ocean_sfc, & - OS%diag, maskmap=OS%grid%Domain%maskmap, & - gas_fields_ocn=gas_fields_ocn) - else - call initialize_ocean_public_type(OS%grid%Domain%mpp_domain, Ocean_sfc, & - OS%diag, gas_fields_ocn=gas_fields_ocn) - endif + call initialize_ocean_public_type(OS%grid%Domain, Ocean_sfc, OS%diag, gas_fields_ocn=gas_fields_ocn) ! This call can only occur here if the coupler_bc_type variables have been ! initialized already using the information from gas_fields_ocn. @@ -529,8 +521,7 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, & (/is,is,ie,ie/), (/js,js,je,je/), as_needed=.true.) ! Translate Ice_ocean_boundary into fluxes. - call mpp_get_compute_domain(Ocean_sfc%Domain, index_bnds(1), index_bnds(2), & - index_bnds(3), index_bnds(4)) + call get_domain_extent(Ocean_sfc%Domain, index_bnds(1), index_bnds(2), index_bnds(3), index_bnds(4)) weight = 1.0 @@ -792,7 +783,7 @@ end subroutine ocean_model_end subroutine ocean_model_save_restart(OS, Time, directory, filename_suffix) type(ocean_state_type), pointer :: OS !< A pointer to the structure containing the !! internal ocean state (in). - type(time_type), intent(in) :: Time !< The model time at this call, needed for mpp_write calls. + type(time_type), intent(in) :: Time !< The model time at this call, needed for writing files. character(len=*), optional, intent(in) :: directory !< An optional directory into which to !! write these restart files. character(len=*), optional, intent(in) :: filename_suffix !< An optional suffix (e.g., a time-stamp) @@ -823,16 +814,12 @@ subroutine ocean_model_save_restart(OS, Time, directory, filename_suffix) end subroutine ocean_model_save_restart !> Initialize the public ocean type -subroutine initialize_ocean_public_type(input_domain, Ocean_sfc, diag, maskmap, & - gas_fields_ocn) - type(domain2D), intent(in) :: input_domain !< The ocean model domain description +subroutine initialize_ocean_public_type(input_domain, Ocean_sfc, diag, gas_fields_ocn) + type(MOM_domain_type), intent(in) :: input_domain !< The ocean model domain description type(ocean_public_type), intent(inout) :: Ocean_sfc !< A structure containing various publicly - !! visible ocean surface properties after initialization, whose - !! elements are allocated here. - type(diag_ctrl), intent(in) :: diag !< A structure that regulates diagnsotic output - logical, dimension(:,:), & - optional, intent(in) :: maskmap !< A mask indicating which virtual processors - !! are actually in use. If missing, all are used. + !! visible ocean surface properties after + !! initialization, whose elements are allocated here. + type(diag_ctrl), intent(in) :: diag !< A structure that regulates diagnostic output type(coupler_1d_bc_type), & optional, intent(in) :: gas_fields_ocn !< If present, this type describes the !! ocean and surface-ice fields that will participate @@ -844,14 +831,9 @@ subroutine initialize_ocean_public_type(input_domain, Ocean_sfc, diag, maskmap, ! and have no halos. integer :: isc, iec, jsc, jec - call mpp_get_layout(input_domain,layout) - call mpp_get_global_domain(input_domain, xsize=xsz, ysize=ysz) - if (PRESENT(maskmap)) then - call mpp_define_domains((/1,xsz,1,ysz/),layout,Ocean_sfc%Domain, maskmap=maskmap) - else - call mpp_define_domains((/1,xsz,1,ysz/),layout,Ocean_sfc%Domain) - endif - call mpp_get_compute_domain(Ocean_sfc%Domain, isc, iec, jsc, jec) + call clone_MOM_domain(input_domain, Ocean_sfc%Domain, halo_size=0, symmetric=.false.) + + call get_domain_extent(Ocean_sfc%Domain, isc, iec, jsc, jec) allocate ( Ocean_sfc%t_surf (isc:iec,jsc:jec), & Ocean_sfc%s_surf (isc:iec,jsc:jec), & @@ -907,8 +889,7 @@ subroutine convert_state_to_ocean_type(sfc_state, Ocean_sfc, G, US, patm, press_ is = G%isc ; ie = G%iec ; js = G%jsc ; je = G%jec call pass_vector(sfc_state%u, sfc_state%v, G%Domain) - call mpp_get_compute_domain(Ocean_sfc%Domain, isc_bnd, iec_bnd, & - jsc_bnd, jec_bnd) + call get_domain_extent(Ocean_sfc%Domain, isc_bnd, iec_bnd, jsc_bnd, jec_bnd) if (present(patm)) then ! Check that the inidicies in patm are (isc_bnd:iec_bnd,jsc_bnd:jec_bnd). if (.not.present(press_to_z)) call MOM_error(FATAL, & diff --git a/config_src/external/GFDL_ocean_BGC/generic_tracer.F90 b/config_src/external/GFDL_ocean_BGC/generic_tracer.F90 index 42c386497a..90e7d795ff 100644 --- a/config_src/external/GFDL_ocean_BGC/generic_tracer.F90 +++ b/config_src/external/GFDL_ocean_BGC/generic_tracer.F90 @@ -21,6 +21,7 @@ module generic_tracer public generic_tracer_vertdiff_G public generic_tracer_get_diag_list public generic_tracer_coupler_accumulate + public generic_tracer_update_from_coupler !> Turn on generic tracers (note dangerous use of module data) logical :: do_generic_tracer = .true. @@ -65,6 +66,14 @@ subroutine generic_tracer_coupler_accumulate(IOB_struc, weight, model_time) type(time_type), optional,intent(in) :: model_time !< Time end subroutine generic_tracer_coupler_accumulate + !> Modify the values obtained from the coupler + subroutine generic_tracer_update_from_coupler(ilb, jlb, salt_flux_added) + integer, intent(in) :: ilb !< Lower bounds of x extent of input arrays on data domain + integer, intent(in) :: jlb !< Lower bounds of y extent of input arrays on data domain + real, dimension(ilb:,jlb:), intent(in) :: salt_flux_added !< Surface salt flux into ocean from restoring + !! or flux adjustment [g/m^2/sec] + end subroutine generic_tracer_update_from_coupler + !> Calls the corresponding generic_X_update_from_source routine for each package X subroutine generic_tracer_source(Temp,Salt,rho_dzt,dzt,hblt_depth,ilb,jlb,tau,dtts,& grid_dat,model_time,nbands,max_wavelength_band,sw_pen_band,opacity_band,internal_heat,& diff --git a/config_src/infra/FMS1/MOM_couplertype_infra.F90 b/config_src/infra/FMS1/MOM_couplertype_infra.F90 index 637f2b5ebf..0fb57c991a 100644 --- a/config_src/infra/FMS1/MOM_couplertype_infra.F90 +++ b/config_src/infra/FMS1/MOM_couplertype_infra.F90 @@ -9,7 +9,10 @@ module MOM_couplertype_infra use coupler_types_mod, only : coupler_type_increment_data, coupler_type_rescale_data use coupler_types_mod, only : coupler_type_extract_data, coupler_type_set_data use coupler_types_mod, only : coupler_type_data_override -use coupler_types_mod, only : ind_flux, ind_alpha, ind_csurf +use coupler_types_mod, only : ind_flux, ind_deltap, ind_kw, ind_flux0 +use coupler_types_mod, only : ind_pcair, ind_u10, ind_psurf +use coupler_types_mod, only : ind_alpha, ind_csurf, ind_sc_no +use coupler_types_mod, only : ind_runoff, ind_deposition use coupler_types_mod, only : coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type use atmos_ocean_fluxes_mod, only : aof_set_coupler_flux use MOM_domain_infra, only : domain2D @@ -22,7 +25,10 @@ module MOM_couplertype_infra public :: CT_set_data, CT_increment_data, CT_rescale_data public :: CT_copy_data, CT_extract_data, CT_redistribute_data public :: atmos_ocn_coupler_flux -public :: ind_flux, ind_alpha, ind_csurf +public :: ind_flux, ind_deltap, ind_kw, ind_flux0 +public :: ind_pcair, ind_u10, ind_psurf +public :: ind_alpha, ind_csurf, ind_sc_no +public :: ind_runoff, ind_deposition public :: coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type !> This is the interface to spawn one coupler_bc_type into another. diff --git a/config_src/infra/FMS2/MOM_couplertype_infra.F90 b/config_src/infra/FMS2/MOM_couplertype_infra.F90 index 3bcccc1dc7..50fd5b09cd 100644 --- a/config_src/infra/FMS2/MOM_couplertype_infra.F90 +++ b/config_src/infra/FMS2/MOM_couplertype_infra.F90 @@ -9,7 +9,10 @@ module MOM_couplertype_infra use coupler_types_mod, only : coupler_type_increment_data, coupler_type_rescale_data use coupler_types_mod, only : coupler_type_extract_data, coupler_type_set_data use coupler_types_mod, only : coupler_type_data_override -use coupler_types_mod, only : ind_flux, ind_alpha, ind_csurf +use coupler_types_mod, only : ind_flux, ind_deltap, ind_kw, ind_flux0 +use coupler_types_mod, only : ind_pcair, ind_u10, ind_psurf +use coupler_types_mod, only : ind_alpha, ind_csurf, ind_sc_no +use coupler_types_mod, only : ind_runoff, ind_deposition use coupler_types_mod, only : coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type use atmos_ocean_fluxes_mod, only : aof_set_coupler_flux use MOM_domain_infra, only : domain2D @@ -22,7 +25,10 @@ module MOM_couplertype_infra public :: CT_set_data, CT_increment_data, CT_rescale_data public :: CT_copy_data, CT_extract_data, CT_redistribute_data public :: atmos_ocn_coupler_flux -public :: ind_flux, ind_alpha, ind_csurf +public :: ind_flux, ind_deltap, ind_kw, ind_flux0 +public :: ind_pcair, ind_u10, ind_psurf +public :: ind_alpha, ind_csurf, ind_sc_no +public :: ind_runoff, ind_deposition public :: coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type !> This is the interface to spawn one coupler_bc_type into another. diff --git a/src/core/MOM_forcing_type.F90 b/src/core/MOM_forcing_type.F90 index 998713d1f1..a2efcaac7d 100644 --- a/src/core/MOM_forcing_type.F90 +++ b/src/core/MOM_forcing_type.F90 @@ -2280,6 +2280,11 @@ subroutine fluxes_accumulate(flux_tmp, fluxes, G, wt2, forces) fluxes%salt_flux(i,j) = wt1*fluxes%salt_flux(i,j) + wt2*flux_tmp%salt_flux(i,j) enddo ; enddo + if (associated(fluxes%salt_flux_added) .and. associated(flux_tmp%salt_flux_added)) then + do j=js,je ; do i=is,ie + fluxes%salt_flux_added(i,j) = wt1*fluxes%salt_flux_added(i,j) + wt2*flux_tmp%salt_flux_added(i,j) + enddo ; enddo + endif if (associated(fluxes%heat_added) .and. associated(flux_tmp%heat_added)) then do j=js,je ; do i=is,ie fluxes%heat_added(i,j) = wt1*fluxes%heat_added(i,j) + wt2*flux_tmp%heat_added(i,j) diff --git a/src/framework/MOM_coupler_types.F90 b/src/framework/MOM_coupler_types.F90 index b931a2ddd2..cac9309b5e 100644 --- a/src/framework/MOM_coupler_types.F90 +++ b/src/framework/MOM_coupler_types.F90 @@ -9,7 +9,10 @@ module MOM_coupler_types use MOM_couplertype_infra, only : CT_copy_data, CT_increment_data, CT_rescale_data use MOM_couplertype_infra, only : CT_set_data, CT_extract_data, CT_redistribute_data use MOM_couplertype_infra, only : coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type -use MOM_couplertype_infra, only : ind_flux, ind_alpha, ind_csurf +use MOM_couplertype_infra, only : ind_flux, ind_deltap, ind_kw, ind_flux0 +use MOM_couplertype_infra, only : ind_pcair, ind_u10, ind_psurf +use MOM_couplertype_infra, only : ind_alpha, ind_csurf, ind_sc_no +use MOM_couplertype_infra, only : ind_runoff, ind_deposition use MOM_domain_infra, only : domain2D use MOM_time_manager, only : time_type @@ -23,7 +26,10 @@ module MOM_coupler_types public :: coupler_1d_bc_type, coupler_2d_bc_type, coupler_3d_bc_type ! These are encoding constant parameters that indicate whether a flux, solubility or ! surface ocean concentration are being set or accessed with an inquiry. -public :: ind_flux, ind_alpha, ind_csurf +public :: ind_flux, ind_deltap, ind_kw, ind_flux0 +public :: ind_pcair, ind_u10, ind_psurf +public :: ind_alpha, ind_csurf, ind_sc_no +public :: ind_runoff, ind_deposition !> This is the interface to spawn one coupler_bc_type into another. interface coupler_type_spawn diff --git a/src/tracer/MOM_generic_tracer.F90 b/src/tracer/MOM_generic_tracer.F90 index 6b10d15e2f..e092aa7d6d 100644 --- a/src/tracer/MOM_generic_tracer.F90 +++ b/src/tracer/MOM_generic_tracer.F90 @@ -21,7 +21,7 @@ module MOM_generic_tracer use generic_tracer, only: generic_tracer_coupler_get, generic_tracer_coupler_set use generic_tracer, only: generic_tracer_end, generic_tracer_get_list, do_generic_tracer use generic_tracer, only: generic_tracer_update_from_bottom,generic_tracer_vertdiff_G - use generic_tracer, only: generic_tracer_coupler_accumulate + use generic_tracer, only: generic_tracer_coupler_accumulate, generic_tracer_update_from_coupler use g_tracer_utils, only: g_tracer_get_name,g_tracer_set_values,g_tracer_set_common,g_tracer_get_common use g_tracer_utils, only: g_tracer_get_next,g_tracer_type,g_tracer_is_prog,g_tracer_flux_init @@ -527,6 +527,11 @@ subroutine MOM_generic_tracer_column_physics(h_old, h_new, ea, eb, fluxes, Hml, ! the fluxes without coming into this subroutine. ! MOM5 has to modified to conform. + ! + !Call the generic_tracer's update_from_coupler routine (convert salt_flux_added to g/m^2/sec) + ! + call generic_tracer_update_from_coupler(G%isd, G%jsd, 1000*(US%RZ_T_to_kg_m2s*fluxes%salt_flux_added)) + ! !Add contribution of river to surface flux !