diff --git a/components/eam/bld/configure b/components/eam/bld/configure index 30ad7e6fced1..a14eda84d646 100755 --- a/components/eam/bld/configure +++ b/components/eam/bld/configure @@ -2617,6 +2617,7 @@ sub write_filepath } elsif ($rad eq 'rrtmgp') { print $fh "$camsrcdir/eam/src/physics/rrtmgp\n"; if (not defined $opts{'rrtmgpxx'} ) { + print $fh "$camsrcdir/eam/src/physics/rrtmgp/f90\n"; print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/rte\n"; print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/rte/kernels\n"; print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/rrtmgp\n"; @@ -2624,7 +2625,6 @@ sub write_filepath print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/extensions\n"; print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/extensions/rng\n"; print $fh "$camsrcdir/eam/src/physics/rrtmgp/external/examples\n"; - print $fh "$camsrcdir/eam/src/physics/rrtmgp/f90\n"; } } diff --git a/components/eam/bld/namelist_files/namelist_defaults_eam.xml b/components/eam/bld/namelist_files/namelist_defaults_eam.xml index 371a9d7857b5..787e8f3a7bec 100755 --- a/components/eam/bld/namelist_files/namelist_defaults_eam.xml +++ b/components/eam/bld/namelist_files/namelist_defaults_eam.xml @@ -359,8 +359,8 @@ atm/cam/physprops/F_nwvl200_mu20_lam50_res64_t298_c080428.nc -atm/cam/rad/rrtmgp_coefficients_lw_20181204.nc -atm/cam/rad/rrtmgp_coefficients_sw_20181204.nc +atm/cam/rad/rrtmgp-data-lw-g128-210809.nc +atm/cam/rad/rrtmgp-data-sw-g112-210809.nc .true. .false. diff --git a/components/eam/src/physics/rrtmgp/cpp/CMakeLists.txt b/components/eam/src/physics/rrtmgp/cpp/CMakeLists.txt index 5415e410c64f..91ec61acf625 100644 --- a/components/eam/src/physics/rrtmgp/cpp/CMakeLists.txt +++ b/components/eam/src/physics/rrtmgp/cpp/CMakeLists.txt @@ -1,8 +1,8 @@ set (F90_SRC rrtmgp_interface.F90) set (CXX_SRC rrtmgp_interface.cpp + mo_load_coefficients.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/cpp/extensions/fluxes_byband/mo_fluxes_byband_kernels.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../external/cpp/examples/mo_load_coefficients.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../external/cpp/examples/all-sky/mo_load_cloud_coefficients.cpp ) diff --git a/components/eam/src/physics/rrtmgp/cpp/mo_load_coefficients.cpp b/components/eam/src/physics/rrtmgp/cpp/mo_load_coefficients.cpp new file mode 100644 index 000000000000..e1d9c0a526c0 --- /dev/null +++ b/components/eam/src/physics/rrtmgp/cpp/mo_load_coefficients.cpp @@ -0,0 +1,123 @@ + +#include "mo_load_coefficients.h" +#include "YAKL_netcdf.h" + +// This code is part of RRTM for GCM Applications - Parallel (RRTMGP) +// +// Contacts: Robert Pincus and Eli Mlawer +// email: rrtmgp@aer.com +// +// Copyright 2015-2018, Atmospheric and Environmental Research and +// Regents of the University of Colorado. All right reserved. +// +// Use and duplication is permitted under the terms of the +// BSD 3-clause license, see http://opensource.org/licenses/BSD-3-Clause +// ------------------------------------------------------------------------------------------------- + +void load_and_init(GasOpticsRRTMGP &kdist, std::string filename, GasConcs const &available_gases) { + yakl::SimpleNetCDF io; + io.open(filename , yakl::NETCDF_MODE_READ); + + // Read the many arrays + string1d gas_names; + string1d gas_minor; + string1d identifier_minor; + string1d minor_gases_lower; + string1d minor_gases_upper; + string1d scaling_gas_lower; + string1d scaling_gas_upper; + intHost3d key_species; + realHost2d band_lims; + intHost2d band2gpt; + real press_ref_trop; + real temp_ref_p; + real temp_ref_t; + realHost1d press_ref; + realHost1d temp_ref; + realHost3d vmr_ref; + realHost4d kmajor; + intHost2d minor_limits_gpt_lower; + intHost2d minor_limits_gpt_upper; + boolHost1d minor_scales_with_density_lower; + boolHost1d minor_scales_with_density_upper; + boolHost1d scale_by_complement_lower; + boolHost1d scale_by_complement_upper; + intHost1d kminor_start_lower; + intHost1d kminor_start_upper; + realHost3d kminor_lower; + realHost3d kminor_upper; + realHost3d rayl_lower; + realHost3d rayl_upper; + + // Read in strings + charHost2d tmp; + tmp = charHost2d(); io.read( tmp , "gas_names" ); gas_names = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "gas_minor" ); gas_minor = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "identifier_minor" ); identifier_minor = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "minor_gases_lower" ); minor_gases_lower = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "minor_gases_upper" ); minor_gases_upper = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "scaling_gas_lower" ); scaling_gas_lower = char2d_to_string1d(tmp); + tmp = charHost2d(); io.read( tmp , "scaling_gas_upper" ); scaling_gas_upper = char2d_to_string1d(tmp); + + io.read( key_species , "key_species" ); + io.read( band_lims , "bnd_limits_wavenumber" ); + io.read( band2gpt , "bnd_limits_gpt" ); + io.read( press_ref , "press_ref" ); + io.read( temp_ref , "temp_ref" ); + io.read( temp_ref_p , "absorption_coefficient_ref_P" ); + io.read( temp_ref_t , "absorption_coefficient_ref_T" ); + io.read( press_ref_trop , "press_ref_trop" ); + io.read( kminor_lower , "kminor_lower" ); + io.read( kminor_upper , "kminor_upper" ); + io.read( minor_limits_gpt_lower , "minor_limits_gpt_lower" ); + io.read( minor_limits_gpt_upper , "minor_limits_gpt_upper" ); + io.read( minor_scales_with_density_lower , "minor_scales_with_density_lower" ); + io.read( minor_scales_with_density_upper , "minor_scales_with_density_upper" ); + io.read( scale_by_complement_lower , "scale_by_complement_lower" ); + io.read( scale_by_complement_upper , "scale_by_complement_upper" ); + io.read( kminor_start_lower , "kminor_start_lower" ); + io.read( kminor_start_upper , "kminor_start_upper" ); + io.read( vmr_ref , "vmr_ref" ); + io.read( kmajor , "kmajor" ); + + if (io.varExists("rayl_lower")) { + io.read( rayl_lower , "rayl_lower" ); + io.read( rayl_upper , "rayl_upper" ); + } + + // Initialize the gas optics class with data. The calls look slightly different depending + // on whether the radiation sources are internal to the atmosphere (longwave) or external (shortwave) + // gas_optics%load() returns a string; a non-empty string indicates an error. + if (io.varExists("totplnk")) { + // If there's a totplnk variable in the file, then it's a longwave (internal sources) type + realHost2d totplnk; + realHost4d planck_frac; + io.read( totplnk , "totplnk" ); + io.read( planck_frac , "plank_fraction" ); + kdist.load(available_gases, gas_names, key_species, band2gpt, band_lims, press_ref, press_ref_trop, + temp_ref, temp_ref_p, temp_ref_t, vmr_ref, kmajor, kminor_lower, kminor_upper, + gas_minor, identifier_minor, minor_gases_lower, minor_gases_upper, + minor_limits_gpt_lower, minor_limits_gpt_upper, minor_scales_with_density_lower, + minor_scales_with_density_upper, scaling_gas_lower, scaling_gas_upper, + scale_by_complement_lower, scale_by_complement_upper, kminor_start_lower, + kminor_start_upper, totplnk, planck_frac, rayl_lower, rayl_upper); + } else { + // Otherwise, it's a shortwave type + realHost1d solar_src; + if (io.varExists("solar_source")) { + io.read( solar_src , "solar_source" ); + } else { + io.read( solar_src , "solar_source_quiet" ); + } + kdist.load(available_gases, gas_names, key_species, band2gpt, band_lims, press_ref, press_ref_trop, + temp_ref, temp_ref_p, temp_ref_t, vmr_ref, kmajor, kminor_lower, kminor_upper, + gas_minor, identifier_minor, minor_gases_lower, minor_gases_upper, + minor_limits_gpt_lower, minor_limits_gpt_upper, minor_scales_with_density_lower, + minor_scales_with_density_upper, scaling_gas_lower, scaling_gas_upper, + scale_by_complement_lower, scale_by_complement_upper, kminor_start_lower, + kminor_start_upper, solar_src, rayl_lower, rayl_upper); + } + io.close(); +} + + diff --git a/components/eam/src/physics/rrtmgp/f90/mo_load_coefficients.F90 b/components/eam/src/physics/rrtmgp/f90/mo_load_coefficients.F90 new file mode 100644 index 000000000000..2858c9931864 --- /dev/null +++ b/components/eam/src/physics/rrtmgp/f90/mo_load_coefficients.F90 @@ -0,0 +1,241 @@ +! This code is part of RRTM for GCM Applications - Parallel (RRTMGP) +! +! Contacts: Robert Pincus and Eli Mlawer +! email: rrtmgp@aer.com +! +! Copyright 2015-2018, Atmospheric and Environmental Research and +! Regents of the University of Colorado. All right reserved. +! +! Use and duplication is permitted under the terms of the +! BSD 3-clause license, see http://opensource.org/licenses/BSD-3-Clause +! ------------------------------------------------------------------------------------------------- +! +! The gas optics class used by RRMTGP needs to be initialized with data stored in a netCDF file. +! RRTMGP itself doesn't include methods for reading the data so we don't conflict with users' +! local environment. This module provides a straight-forward implementation of reading the data +! and calling gas_optics%load(). +! +! ------------------------------------------------------------------------------------------------- +module mo_load_coefficients + ! + ! Modules for working with rte and rrtmgp + ! + use mo_rte_kind, only: wp, wl + use mo_gas_concentrations, only: ty_gas_concs + use mo_gas_optics_rrtmgp, only: ty_gas_optics_rrtmgp + ! -------------------------------------------------- + use mo_simple_netcdf, only: read_field, read_char_vec, read_logical_vec, var_exists, get_dim_size + use netcdf + implicit none + private + public :: load_and_init + +contains + subroutine stop_on_err(msg) + use iso_fortran_env, only : error_unit + character(len=*), intent(in) :: msg + + if(msg /= "") then + write(error_unit, *) msg + stop + end if + end subroutine + !-------------------------------------------------------------------------------------------------------------------- + ! read optical coefficients from NetCDF file + subroutine load_and_init(kdist, filename, available_gases) + class(ty_gas_optics_rrtmgp), intent(inout) :: kdist + character(len=*), intent(in ) :: filename + class(ty_gas_concs), intent(in ) :: available_gases ! Which gases does the host model have available? + ! -------------------------------------------------- + ! + ! Variables that will be passed to gas_optics%load() + ! + character(len=32), dimension(:), allocatable :: gas_names + integer, dimension(:,:,:), allocatable :: key_species + integer, dimension(:,: ), allocatable :: band2gpt + real(wp), dimension(:,: ), allocatable :: band_lims + real(wp) :: press_ref_trop, temp_ref_p, temp_ref_t + real(wp), dimension(: ), allocatable :: press_ref + real(wp), dimension(: ), allocatable :: temp_ref + real(wp), dimension(:,:,: ), allocatable :: vmr_ref + real(wp), dimension(:,:,:,:), allocatable :: kmajor + + character(len=32), dimension(:), allocatable :: gas_minor, identifier_minor + character(len=32), dimension(:), allocatable :: minor_gases_lower, minor_gases_upper + integer, dimension(:,:), allocatable :: minor_limits_gpt_lower, minor_limits_gpt_upper + logical(wl), dimension(:), allocatable :: minor_scales_with_density_lower, minor_scales_with_density_upper + character(len=32), dimension(:), allocatable :: scaling_gas_lower, scaling_gas_upper + logical(wl), dimension(:), allocatable :: scale_by_complement_lower, scale_by_complement_upper + integer, dimension(:), allocatable :: kminor_start_lower, kminor_start_upper + real(wp), dimension(:,:,:), allocatable :: kminor_lower, kminor_upper + + real(wp), dimension(:,:,: ), allocatable :: rayl_lower, rayl_upper + real(wp), dimension(: ), allocatable :: solar_src + real(wp), dimension(:,: ), allocatable :: totplnk + real(wp), dimension(:,:,:,:), allocatable :: planck_frac + ! ----------------- + ! + ! Book-keeping variables + ! + integer :: ncid + integer :: ntemps, & + npress, & + nabsorbers, & + nextabsorbers, & + nminorabsorbers, & + nmixingfracs, & + nlayers, & + nbnds, & + ngpts, & + npairs, & + nminor_absorber_intervals_lower, & + nminor_absorber_intervals_upper, & + ncontributors_lower, & + ncontributors_upper, & + ninternalSourcetemps + ! -------------------------------------------------- + ! + ! How big are the various arrays? + ! + if(nf90_open(trim(fileName), NF90_NOWRITE, ncid) /= NF90_NOERR) & + call stop_on_err("load_and_init(): can't open file " // trim(fileName)) + ntemps = get_dim_size(ncid,'temperature') + npress = get_dim_size(ncid,'pressure') + nabsorbers = get_dim_size(ncid,'absorber') + nminorabsorbers = get_dim_size(ncid,'minor_absorber') + nextabsorbers = get_dim_size(ncid,'absorber_ext') + nmixingfracs = get_dim_size(ncid,'mixing_fraction') + nlayers = get_dim_size(ncid,'atmos_layer') + nbnds = get_dim_size(ncid,'bnd') + ngpts = get_dim_size(ncid,'gpt') + npairs = get_dim_size(ncid,'pair') + nminor_absorber_intervals_lower & + = get_dim_size(ncid,'minor_absorber_intervals_lower') + nminor_absorber_intervals_upper & + = get_dim_size(ncid,'minor_absorber_intervals_upper') + ninternalSourcetemps & + = get_dim_size(ncid,'temperature_Planck') + ncontributors_lower = get_dim_size(ncid,'contributors_lower') + ncontributors_upper = get_dim_size(ncid,'contributors_upper') + ! ----------------- + ! + ! Read the many arrays + ! + gas_names = read_char_vec(ncid, 'gas_names', nabsorbers) + key_species = read_field(ncid, 'key_species', 2, nlayers, nbnds) + band_lims = read_field(ncid, 'bnd_limits_wavenumber', 2, nbnds) + band2gpt = int(read_field(ncid, 'bnd_limits_gpt', 2, nbnds)) + press_ref = read_field(ncid, 'press_ref', npress) + temp_ref = read_field(ncid, 'temp_ref', ntemps) + temp_ref_p = read_field(ncid, 'absorption_coefficient_ref_P') + temp_ref_t = read_field(ncid, 'absorption_coefficient_ref_T') + press_ref_trop = read_field(ncid, 'press_ref_trop') + kminor_lower = read_field(ncid, 'kminor_lower', & + ncontributors_lower, nmixingfracs, ntemps) + kminor_upper = read_field(ncid, 'kminor_upper', & + ncontributors_upper, nmixingfracs, ntemps) + gas_minor = read_char_vec(ncid, 'gas_minor', nminorabsorbers) + identifier_minor = read_char_vec(ncid, 'identifier_minor', nminorabsorbers) + minor_gases_lower = read_char_vec(ncid, 'minor_gases_lower', nminor_absorber_intervals_lower) + minor_gases_upper = read_char_vec(ncid, 'minor_gases_upper', nminor_absorber_intervals_upper) + minor_limits_gpt_lower & + = int(read_field(ncid, 'minor_limits_gpt_lower', npairs,nminor_absorber_intervals_lower)) + minor_limits_gpt_upper & + = int(read_field(ncid, 'minor_limits_gpt_upper', npairs,nminor_absorber_intervals_upper)) + minor_scales_with_density_lower & + = read_logical_vec(ncid, 'minor_scales_with_density_lower', nminor_absorber_intervals_lower) + minor_scales_with_density_upper & + = read_logical_vec(ncid, 'minor_scales_with_density_upper', nminor_absorber_intervals_upper) + scale_by_complement_lower & + = read_logical_vec(ncid, 'scale_by_complement_lower', nminor_absorber_intervals_lower) + scale_by_complement_upper & + = read_logical_vec(ncid, 'scale_by_complement_upper', nminor_absorber_intervals_upper) + scaling_gas_lower & + = read_char_vec(ncid, 'scaling_gas_lower', nminor_absorber_intervals_lower) + scaling_gas_upper & + = read_char_vec(ncid, 'scaling_gas_upper', nminor_absorber_intervals_upper) + kminor_start_lower & + = read_field(ncid, 'kminor_start_lower', nminor_absorber_intervals_lower) + kminor_start_upper & + = read_field(ncid, 'kminor_start_upper', nminor_absorber_intervals_upper) + vmr_ref = read_field(ncid, 'vmr_ref', nlayers, nextabsorbers, ntemps) + + kmajor = read_field(ncid, 'kmajor', ngpts, nmixingfracs, npress+1, ntemps) + if(var_exists(ncid, 'rayl_lower')) then + rayl_lower = read_field(ncid, 'rayl_lower', ngpts, nmixingfracs, ntemps) + rayl_upper = read_field(ncid, 'rayl_upper', ngpts, nmixingfracs, ntemps) + end if + ! -------------------------------------------------- + ! + ! Initialize the gas optics class with data. The calls look slightly different depending + ! on whether the radiation sources are internal to the atmosphere (longwave) or external (shortwave) + ! gas_optics%load() returns a string; a non-empty string indicates an error. + ! + if(var_exists(ncid, 'totplnk')) then + ! + ! If there's a totplnk variable in the file it's a longwave (internal sources) type + ! + totplnk = read_field(ncid, 'totplnk', ninternalSourcetemps, nbnds) + planck_frac = read_field(ncid, 'plank_fraction', ngpts, nmixingfracs, npress+1, ntemps) + call stop_on_err(kdist%load(available_gases, & + gas_names, & + key_species, & + band2gpt, & + band_lims, & + press_ref, & + press_ref_trop, & + temp_ref, & + temp_ref_p, temp_ref_t, & + vmr_ref, kmajor, & + kminor_lower, kminor_upper, & + gas_minor,identifier_minor, & + minor_gases_lower, minor_gases_upper, & + minor_limits_gpt_lower, & + minor_limits_gpt_upper, & + minor_scales_with_density_lower, & + minor_scales_with_density_upper, & + scaling_gas_lower, scaling_gas_upper, & + scale_by_complement_lower, & + scale_by_complement_upper, & + kminor_start_lower, & + kminor_start_upper, & + totplnk, planck_frac, & + rayl_lower, rayl_upper)) + else + ! + ! Solar source doesn't have an dependencies yet + ! + if (var_exists(ncid, 'solar_source')) then + solar_src = read_field(ncid, 'solar_source', ngpts) + else + solar_src = read_field(ncid, 'solar_source_quiet', ngpts) + end if + call stop_on_err(kdist%load(available_gases, & + gas_names, & + key_species, & + band2gpt, & + band_lims, & + press_ref, & + press_ref_trop, & + temp_ref, & + temp_ref_p, temp_ref_t, & + vmr_ref, kmajor, & + kminor_lower, kminor_upper, & + gas_minor,identifier_minor,& + minor_gases_lower, minor_gases_upper, & + minor_limits_gpt_lower, & + minor_limits_gpt_upper, & + minor_scales_with_density_lower, & + minor_scales_with_density_upper, & + scaling_gas_lower, scaling_gas_upper, & + scale_by_complement_lower, & + scale_by_complement_upper, & + kminor_start_lower, & + kminor_start_upper, & + solar_src, & + rayl_lower, rayl_upper)) + end if + ! -------------------------------------------------- + ncid = nf90_close(ncid) + end subroutine load_and_init +end module