diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 195f46847ef..771928a8122 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -372,17 +372,11 @@ be lost if SCREAM_HACK_XML is not enabled. ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a1_surf_ne4pg2_2010_clim_c20240815.nc ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a2_surf_ne4pg2_2010_clim_c20240815.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/dst_ne30pg2_c20241028.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/dst_ne4pg2_c20241028.nc - - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/DMSflux.2010.ne4pg2_conserv.POPmonthlyClimFromACES4BGC_c20240814.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so2_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_bc_a4_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a1_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a2_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_num_a4_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_pom_a4_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a1_surf_ne4pg2_2010_clim_c20240815.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/cmip6_mam4_so4_a2_surf_ne4pg2_2010_clim_c20240815.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne30pg2_c20241030.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne4pg2_c20241030.nc @@ -639,6 +633,7 @@ be lost if SCREAM_HACK_XML is not enabled. 1.37146e-07 ,3.45899e-08 ,1.00000e-06 ,9.99601e-08 1.37452e-07 ,3.46684e-08 ,1.00900e-06 ,9.99601e-08 5.08262e-12 ,1.54035e-13 ,3.09018e-13 ,9.14710e-22 + 0.0 0.0 0.0 0.0 diff --git a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp index ee3e21e7461..2c3360a3b4f 100644 --- a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp +++ b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp @@ -28,35 +28,41 @@ void SurfaceCouplingImporter::set_grids(const std::shared_ptr("sfc_alb_dir_vis", scalar2d_layout, nondim, grid_name); - add_field("sfc_alb_dir_nir", scalar2d_layout, nondim, grid_name); - add_field("sfc_alb_dif_vis", scalar2d_layout, nondim, grid_name); - add_field("sfc_alb_dif_nir", scalar2d_layout, nondim, grid_name); - add_field("surf_lw_flux_up", scalar2d_layout, W/m2, grid_name); - add_field("surf_sens_flux", scalar2d_layout, W/m2, grid_name); - add_field("surf_evap", scalar2d_layout, kg/m2/s, grid_name); - add_field("surf_mom_flux", vector2d_layout, N/m2, grid_name); - add_field("surf_radiative_T", scalar2d_layout, K, grid_name); - add_field("T_2m", scalar2d_layout, K, grid_name); - add_field("qv_2m", scalar2d_layout, kg/kg, grid_name); - add_field("wind_speed_10m", scalar2d_layout, m/s, grid_name); - add_field("snow_depth_land", scalar2d_layout, m, grid_name); - add_field("ocnfrac", scalar2d_layout, nondim, grid_name); - add_field("landfrac", scalar2d_layout, nondim, grid_name); - add_field("icefrac", scalar2d_layout, nondim, grid_name); + const FieldLayout scalar2d = m_grid->get_2d_scalar_layout(); + const FieldLayout vector2d = m_grid->get_2d_vector_layout(2); + const FieldLayout vector4d = m_grid->get_2d_vector_layout(4); + + add_field("sfc_alb_dir_vis", scalar2d, nondim, grid_name); + add_field("sfc_alb_dir_nir", scalar2d, nondim, grid_name); + add_field("sfc_alb_dif_vis", scalar2d, nondim, grid_name); + add_field("sfc_alb_dif_nir", scalar2d, nondim, grid_name); + add_field("surf_lw_flux_up", scalar2d, W/m2, grid_name); + add_field("surf_sens_flux", scalar2d, W/m2, grid_name); + add_field("surf_evap", scalar2d, kg/m2/s, grid_name); + add_field("surf_mom_flux", vector2d, N/m2, grid_name); + add_field("surf_radiative_T", scalar2d, K, grid_name); + add_field("T_2m", scalar2d, K, grid_name); + add_field("qv_2m", scalar2d, kg/kg, grid_name); + add_field("wind_speed_10m", scalar2d, m/s, grid_name); + add_field("snow_depth_land", scalar2d, m, grid_name); + add_field("ocnfrac", scalar2d, nondim, grid_name); + add_field("landfrac", scalar2d, nondim, grid_name); + add_field("icefrac", scalar2d, nondim, grid_name); // Friction velocity [m/s] - add_field("fv", scalar2d_layout, m/s, grid_name); + add_field("fv", scalar2d, m/s, grid_name); // Aerodynamical resistance - add_field("ram1", scalar2d_layout, s/m, grid_name); + add_field("ram1", scalar2d, s/m, grid_name); + // Sea surface temperature [K] + add_field("sst", scalar2d, K, grid_name); + //dust fluxes [kg/m^2/s]: Four flux values for eacch column + add_field("dstflx", vector4d, kg/m2/s, grid_name); + } // ========================================================================================= void SurfaceCouplingImporter::setup_surface_coupling_data(const SCDataManager &sc_data_manager) diff --git a/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 b/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 index dc5be4de373..9462750297f 100644 --- a/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 +++ b/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 @@ -6,7 +6,7 @@ module scream_cpl_indices private ! Focus only on the ones that scream imports/exports (subsets of x2a and a2x) - integer, parameter, public :: num_scream_imports = 19 + integer, parameter, public :: num_scream_imports = 24 integer, parameter, public :: num_scream_exports = 17 integer, public :: num_cpl_imports, num_cpl_exports, import_field_size, export_field_size @@ -92,6 +92,11 @@ subroutine scream_set_cpl_indices (x2a, a2x) import_field_names(17) = 'icefrac' import_field_names(18) = 'fv' import_field_names(19) = 'ram1' + import_field_names(20) = 'sst' + import_field_names(21) = 'dstflx' + import_field_names(22) = 'dstflx' + import_field_names(23) = 'dstflx' + import_field_names(24) = 'dstflx' ! CPL indices import_cpl_indices(1) = mct_avect_indexra(x2a,'Sx_avsdr') @@ -113,10 +118,23 @@ subroutine scream_set_cpl_indices (x2a, a2x) import_cpl_indices(17) = mct_avect_indexra(x2a,'Sf_ifrac') import_cpl_indices(18) = mct_avect_indexra(x2a,'Sl_fv') import_cpl_indices(19) = mct_avect_indexra(x2a,'Sl_ram1') + !sst + import_cpl_indices(20) = mct_avect_indexra(x2a,'So_t') + !dust fluxes + import_cpl_indices(21) = mct_avect_indexra(x2a,'Fall_flxdst1') + import_cpl_indices(22) = mct_avect_indexra(x2a,'Fall_flxdst2') + import_cpl_indices(23) = mct_avect_indexra(x2a,'Fall_flxdst3') + import_cpl_indices(24) = mct_avect_indexra(x2a,'Fall_flxdst4') ! Vector components + !(Faxx_taux and Faxx_tauy) import_vector_components(11) = 0 import_vector_components(12) = 1 + !(dust fluxes) + import_vector_components(21) = 0 + import_vector_components(22) = 1 + import_vector_components(23) = 2 + import_vector_components(24) = 3 ! Constant multiples import_constant_multiple(10) = -1 diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp new file mode 100644 index 00000000000..9c01daf8223 --- /dev/null +++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp @@ -0,0 +1,73 @@ +#ifndef EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP +#define EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP + +namespace scream { + +namespace { + +using KT = ekat::KokkosTypes; +using view_1d = typename KT::template view_1d; +using view_2d = typename KT::template view_2d; +using const_view_1d = typename KT::template view_1d; +using const_view_2d = typename KT::template view_2d; + +//-------- Inititlize gas and aerosol fluxes ------ +void init_fluxes(const int &ncol, + view_2d &constituent_fluxes) { // input-output + + constexpr int pcnst = mam4::aero_model::pcnst; + const int gas_start_ind = mam4::utils::gasses_start_ind(); + + const auto policy = + ekat::ExeSpaceUtils::get_default_team_policy( + ncol, pcnst - gas_start_ind); + + // Parallel loop over all the columns + Kokkos::parallel_for( + policy, KOKKOS_LAMBDA(const KT::MemberType &team) { + const int icol = team.league_rank(); + view_1d flux_col = ekat::subview(constituent_fluxes, icol); + + // Zero out constituent fluxes only for gasses and aerosols + Kokkos::parallel_for( + Kokkos::TeamVectorRange(team, gas_start_ind, pcnst), + [&](int icnst) { flux_col(icnst) = 0; }); + }); +} // init_fluxes ends + +//-------- compute online emissions for dust, sea salt and marine organics ----- +void compute_online_dust_nacl_emiss( + const int &ncol, const int &nlev, const const_view_1d &ocnfrac, + const const_view_1d &sst, const const_view_2d &u_wind, + const const_view_2d &v_wind, const const_view_2d &dstflx, + const const_view_1d &mpoly, const const_view_1d &mprot, + const const_view_1d &mlip, const const_view_1d &soil_erodibility, + const const_view_2d &z_mid, + // output + view_2d &constituent_fluxes) { + const int surf_lev = nlev - 1; // surface level + + Kokkos::parallel_for( + "online_emis_fluxes", ncol, KOKKOS_LAMBDA(int icol) { + // Input + const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); + + // Output + view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); + + // Compute online emissions + // NOTE: mam4::aero_model_emissions calculates mass and number emission + // fluxes in units of [kg/m2/s or #/m2/s] (MKS), so no need to convert + mam4::aero_model_emissions::aero_model_emissions( + sst(icol), ocnfrac(icol), u_wind(icol, surf_lev), + v_wind(icol, surf_lev), z_mid(icol, surf_lev), dstflx_icol, + soil_erodibility(icol), mpoly(icol), mprot(icol), mlip(icol), + // out + fluxes_col); + }); +} // compute_online_dust_nacl_emiss ends + +} // namespace +} // namespace scream + +#endif // EAMXX_MAM_SRF_AND_ONLINE_EMISSIONS_FUNCTIONS_HPP diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp index 850a82d0896..fd3ebf79700 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.cpp @@ -1,14 +1,24 @@ -//#include #include +// For surface and online emission functions +#include + +// For reading soil erodibility file +#include + namespace scream { +// For reading soil erodibility file +using soilErodibilityFunc = + soil_erodibility::soilErodibilityFunctions; + // ================================================================ // Constructor // ================================================================ MAMSrfOnlineEmiss::MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms) : AtmosphereProcess(comm, params) { + // FIXME: Do we want to read dust emiss factor from the namelist?? /* Anything that can be initialized without grid information can be * initialized here. Like universal constants. */ @@ -26,18 +36,98 @@ void MAMSrfOnlineEmiss::set_grids( nlev_ = grid_->get_num_vertical_levels(); // Number of levels per column using namespace ekat::units; + constexpr auto m2 = pow(m, 2); + constexpr auto s2 = pow(s, 2); + constexpr auto q_unit = kg / kg; // units of mass mixing ratios of tracers + constexpr auto n_unit = 1 / kg; // units of number mixing ratios of tracers + constexpr auto nondim = ekat::units::Units::nondimensional(); - static constexpr int pcnst = mam4::aero_model::pcnst; - const FieldLayout scalar2d_pcnct = - grid_->get_2d_vector_layout(pcnst, "num_phys_constituents"); + const FieldLayout scalar2d = grid_->get_2d_scalar_layout(); + const FieldLayout scalar3d_m = grid_->get_3d_scalar_layout(true); // mid + const FieldLayout scalar3d_i = grid_->get_3d_scalar_layout(false); // int + + // For U and V components of wind + const FieldLayout vector3d = grid_->get_3d_vector_layout(true, 2); + + // For components of dust flux + const FieldLayout vector4d = grid_->get_2d_vector_layout(4); + + // -------------------------------------------------------------------------- + // These variables are "Required" or pure inputs for the process + // -------------------------------------------------------------------------- + + // ----------- Atmospheric quantities ------------- + + // -- Variables required for building DS to compute z_mid -- + // Specific humidity [kg/kg] + // FIXME: Comply with add_tracer calls + add_field("qv", scalar3d_m, q_unit, grid_name, "tracers"); + + // Cloud liquid mass mixing ratio [kg/kg] + add_field("qc", scalar3d_m, q_unit, grid_name, "tracers"); + + // Cloud ice mass mixing ratio [kg/kg] + add_field("qi", scalar3d_m, q_unit, grid_name, "tracers"); + + // Cloud liquid number mixing ratio [1/kg] + add_field("nc", scalar3d_m, n_unit, grid_name, "tracers"); + + // Cloud ice number mixing ratio [1/kg] + add_field("ni", scalar3d_m, n_unit, grid_name, "tracers"); + + // Temperature[K] at midpoints + add_field("T_mid", scalar3d_m, K, grid_name); + + // Vertical pressure velocity [Pa/s] at midpoints + add_field("omega", scalar3d_m, Pa / s, grid_name); + + // Total pressure [Pa] at midpoints + add_field("p_mid", scalar3d_m, Pa, grid_name); + + // Total pressure [Pa] at interfaces + add_field("p_int", scalar3d_i, Pa, grid_name); + + // Layer thickness(pdel) [Pa] at midpoints + add_field("pseudo_density", scalar3d_m, Pa, grid_name); + + // Planetary boundary layer height [m] + add_field("pbl_height", scalar2d, m, grid_name); + + // Surface geopotential [m2/s2] + add_field("phis", scalar2d, m2 / s2, grid_name); + + //----------- Variables from microphysics scheme ------------- + + // Total cloud fraction [fraction] (Require only for building DS) + add_field("cldfrac_tot", scalar3d_m, nondim, grid_name); + + // -- Variables required for online dust and sea salt emissions -- + + // U and V components of the wind[m/s] + add_field("horiz_winds", vector3d, m / s, grid_name); + + //----------- Variables from coupler (ocean component)--------- + // Ocean fraction [unitless] + add_field("ocnfrac", scalar2d, nondim, grid_name); + + // Sea surface temperature [K] + add_field("sst", scalar2d, K, grid_name); + + // dust fluxes [kg/m^2/s]: Four flux values for each column + add_field("dstflx", vector4d, kg / m2 / s, grid_name); // ------------------------------------------------------------- - // These variables are "Computed" or outputs for the process + // These variables are "Updated" or input-outputs for the process // ------------------------------------------------------------- - static constexpr Units m2(m * m, "m2"); + + constexpr int pcnst = mam4::aero_model::pcnst; + const FieldLayout vector2d_pcnst = + grid_->get_2d_vector_layout(pcnst, "num_phys_constituents"); + // Constituent fluxes of species in [kg/m2/s] - add_field("constituent_fluxes", scalar2d_pcnct, kg / m2 / s, - grid_name); + // FIXME: confirm if it is Updated or Computed + add_field("constituent_fluxes", vector2d_pcnst, kg / m2 / s, + grid_name); // Surface emissions remapping file auto srf_map_file = m_params.get("srf_remap_file", ""); @@ -63,6 +153,7 @@ void MAMSrfOnlineEmiss::set_grids( so2.species_name = "so2"; so2.sectors = {"AGR", "RCO", "SHP", "SLV", "TRA", "WST"}; srf_emiss_species_.push_back(so2); // add to the vector + //-------------------------------------------------------------------- // Init bc_a4 srf emiss data structures //-------------------------------------------------------------------- @@ -147,7 +238,48 @@ void MAMSrfOnlineEmiss::set_grids( // output ispec_srf.horizInterp_, ispec_srf.data_start_, ispec_srf.data_end_, ispec_srf.data_out_, ispec_srf.dataReader_); - } + } // srf emissions file read init + + // ------------------------------------------------------------- + // Setup to enable reading soil erodibility file + // ------------------------------------------------------------- + + const std::string soil_erodibility_data_file = + m_params.get("soil_erodibility_file"); + + // Field to be read from file + const std::string soil_erod_fld_name = "mbl_bsn_fct_geo"; + + // Dimensions of the field + const std::string soil_erod_dname = "ncol"; + + // initialize the file read + soilErodibilityFunc::init_soil_erodibility_file_read( + ncol_, soil_erod_fld_name, soil_erod_dname, grid_, + soil_erodibility_data_file, srf_map_file, serod_horizInterp_, + serod_dataReader_); // output + + // ------------------------------------------------------------- + // Setup to enable reading marine organics file + // ------------------------------------------------------------- + const std::string marine_organics_data_file = + m_params.get("marine_organics_file"); + + // Fields to be read from file (order matters as they are read in the same + // order) + const std::vector marine_org_fld_name = { + "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; + + // Dimensions of the field + const std::string marine_org_dname = "ncol"; + + // initialize the file read + marineOrganicsFunc::init_marine_organics_file_read( + ncol_, marine_org_fld_name, marine_org_dname, grid_, + marine_organics_data_file, srf_map_file, + // output + morg_horizInterp_, morg_data_start_, morg_data_end_, morg_data_out_, + morg_dataReader_); } // set_grid ends @@ -182,6 +314,42 @@ void MAMSrfOnlineEmiss::init_buffers(const ATMBufferManager &buffer_manager) { // INITIALIZE_IMPL // ================================================================ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { + // --------------------------------------------------------------- + // Input fields read in from IC file, namelist or other processes + // --------------------------------------------------------------- + + // Populate the wet atmosphere state with views from fields + wet_atm_.qv = get_field_in("qv").get_view(); + + // Following wet_atm vars are required only for building DS + wet_atm_.qc = get_field_in("qc").get_view(); + wet_atm_.nc = get_field_in("nc").get_view(); + wet_atm_.qi = get_field_in("qi").get_view(); + wet_atm_.ni = get_field_in("ni").get_view(); + + // Populate the dry atmosphere state with views from fields + dry_atm_.T_mid = get_field_in("T_mid").get_view(); + dry_atm_.p_mid = get_field_in("p_mid").get_view(); + dry_atm_.p_del = get_field_in("pseudo_density").get_view(); + dry_atm_.p_int = get_field_in("p_int").get_view(); + + // Following dry_atm vars are required only for building DS + dry_atm_.cldfrac = get_field_in("cldfrac_tot").get_view(); + dry_atm_.pblh = get_field_in("pbl_height").get_view(); + dry_atm_.omega = get_field_in("omega").get_view(); + + // store fields converted to dry mmr from wet mmr in dry_atm_ + dry_atm_.z_mid = buffer_.z_mid; + dry_atm_.z_iface = buffer_.z_iface; + dry_atm_.dz = buffer_.dz; + dry_atm_.qv = buffer_.qv_dry; + dry_atm_.qc = buffer_.qc_dry; + dry_atm_.nc = buffer_.nc_dry; + dry_atm_.qi = buffer_.qi_dry; + dry_atm_.ni = buffer_.ni_dry; + dry_atm_.w_updraft = buffer_.w_updraft; + dry_atm_.z_surf = 0.0; // FIXME: for now + // --------------------------------------------------------------- // Output fields // --------------------------------------------------------------- @@ -212,10 +380,27 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { ispec_srf.data_end_); // output } + //----------------------------------------------------------------- + // Read Soil erodibility data + //----------------------------------------------------------------- + // This data is time-independent, we read all data here for the + // entire simulation + soilErodibilityFunc::update_soil_erodibility_data_from_file( + serod_dataReader_, *serod_horizInterp_, + soil_erodibility_); // output + + //-------------------------------------------------------------------- + // Update marine orgaincs from file + //-------------------------------------------------------------------- + // Time dependent data + marineOrganicsFunc::update_marine_organics_data_from_file( + morg_dataReader_, timestamp(), curr_month, *morg_horizInterp_, + morg_data_end_); // output + //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- - preprocess_.initialize(constituent_fluxes_); + preprocess_.initialize(ncol_, nlev_, wet_atm_, dry_atm_); } // end initialize_impl() @@ -223,14 +408,79 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { // RUN_IMPL // ================================================================ void MAMSrfOnlineEmiss::run_impl(const double dt) { - // Zero-out output - Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); + const auto scan_policy = ekat::ExeSpaceUtils< + KT::ExeSpace>::get_thread_range_parallel_scan_team_policy(ncol_, nlev_); + + // preprocess input -- needs a scan for the calculation of atm height + Kokkos::parallel_for("preprocess", scan_policy, preprocess_); + Kokkos::fence(); + + // Constituent fluxes [kg/m^2/s] + auto constituent_fluxes = this->constituent_fluxes_; + // Zero out constituent fluxes only for gasses and aerosols + init_fluxes(ncol_, // in + constituent_fluxes); // in-out + Kokkos::fence(); // Gather time and state information for interpolation - auto ts = timestamp() + dt; + const auto ts = timestamp() + dt; //-------------------------------------------------------------------- - // Interpolate srf emiss data + // Online emissions from dust and sea salt + //-------------------------------------------------------------------- + + // --- Interpolate marine organics data -- + + // Update TimeState, note the addition of dt + morg_timeState_.t_now = ts.frac_of_year_in_days(); + + // Update time state and if the month has changed, update the data. + marineOrganicsFunc::update_marine_organics_timestate( + morg_dataReader_, ts, *morg_horizInterp_, + // output + morg_timeState_, morg_data_start_, morg_data_end_); + + // Call the main marine organics routine to get interpolated forcings. + marineOrganicsFunc::marineOrganics_main(morg_timeState_, morg_data_start_, + morg_data_end_, morg_data_out_); + + // Marine organics emission data read from the file (order is important here) + const const_view_1d mpoly = ekat::subview(morg_data_out_.emiss_sectors, 0); + const const_view_1d mprot = ekat::subview(morg_data_out_.emiss_sectors, 1); + const const_view_1d mlip = ekat::subview(morg_data_out_.emiss_sectors, 2); + + // Ocean fraction [unitless] + const const_view_1d ocnfrac = + get_field_in("ocnfrac").get_view(); + + // Sea surface temperature [K] + const const_view_1d sst = get_field_in("sst").get_view(); + + // U wind component [m/s] + const const_view_2d u_wind = + get_field_in("horiz_winds").get_component(0).get_view(); + + // V wind component [m/s] + const const_view_2d v_wind = + get_field_in("horiz_winds").get_component(1).get_view(); + + // Dust fluxes [kg/m^2/s]: Four flux values for each column + const const_view_2d dstflx = get_field_in("dstflx").get_view(); + + // Soil edodibility [fraction] + const const_view_1d soil_erodibility = this->soil_erodibility_; + + // Vertical layer height at midpoints + const const_view_2d z_mid = dry_atm_.z_mid; + + compute_online_dust_nacl_emiss(ncol_, nlev_, ocnfrac, sst, u_wind, v_wind, + dstflx, mpoly, mprot, mlip, soil_erodibility, + z_mid, + // output + constituent_fluxes); + Kokkos::fence(); + //-------------------------------------------------------------------- + // Interpolate srf emiss data read in from emissions files //-------------------------------------------------------------------- for(srf_emiss_ &ispec_srf : srf_emiss_species_) { @@ -256,20 +506,18 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // modify units from molecules/cm2/s to kg/m2/s auto fluxes_in_mks_units = this->fluxes_in_mks_units_; - auto constituent_fluxes = this->constituent_fluxes_; const Real mfactor = amufac * mam4::gas_chemistry::adv_mass[species_index - offset_]; + const view_1d ispec_outdata0 = + ekat::subview(ispec_srf.data_out_.emiss_sectors, 0); // Parallel loop over all the columns to update units Kokkos::parallel_for( - "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - fluxes_in_mks_units(icol) = - ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor; + "srf_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + fluxes_in_mks_units(icol) = ispec_outdata0(icol) * mfactor; constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); - } // for loop for species Kokkos::fence(); -} // run_imple ends - +} // run_impl ends // ============================================================================= } // namespace scream diff --git a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp index 031fb62d8b7..1a3bb4f36e3 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_process_interface.hpp @@ -8,11 +8,12 @@ #include #include +// For reading marine organics file +#include + // For declaring surface and online emission class derived from atm process // class #include - -// #include #include namespace scream { @@ -20,19 +21,31 @@ namespace scream { // The process responsible for handling MAM4 surface and online emissions. The // AD stores exactly ONE instance of this class in its list of subcomponents. class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { - using KT = ekat::KokkosTypes; - using view_1d = typename KT::template view_1d; - using view_2d = typename KT::template view_2d; + using KT = ekat::KokkosTypes; + using view_1d = typename KT::template view_1d; + using view_2d = typename KT::template view_2d; + using const_view_1d = typename KT::template view_1d; + using const_view_2d = typename KT::template view_2d; // number of horizontal columns and vertical levels int ncol_, nlev_; + // Wet and dry states of atmosphere + mam_coupling::WetAtmosphere wet_atm_; + mam_coupling::DryAtmosphere dry_atm_; + // buffer for sotring temporary variables mam_coupling::Buffer buffer_; // physics grid for column information std::shared_ptr grid_; + // Sea surface temoerature [K] + const_view_1d sst_; + + // Dust fluxes (four values for each col) [kg/m2/s] + const_view_2d dust_fluxes_; + // Constituent fluxes of species in [kg/m2/s] view_2d constituent_fluxes_; @@ -42,8 +55,16 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // Unified atomic mass unit used for unit conversion (BAD constant) static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu + // For reading soil erodibility file + std::shared_ptr serod_horizInterp_; + std::shared_ptr serod_dataReader_; + const_view_1d soil_erodibility_; + public: + // For reading surface emissions and marine organics file using srfEmissFunc = mam_coupling::srfEmissFunctions; + using marineOrganicsFunc = + marine_organics::marineOrganicsFunctions; // Constructor MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms); @@ -80,13 +101,35 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { struct Preprocess { Preprocess() = default; // on host: initializes preprocess functor with necessary state data - void initialize(const view_2d &constituent_fluxes) { - constituent_fluxes_pre_ = constituent_fluxes; + void initialize(const int &ncol, const int &nlev, + const mam_coupling::WetAtmosphere &wet_atm, + const mam_coupling::DryAtmosphere &dry_atm) { + ncol_pre_ = ncol; + nlev_pre_ = nlev; + wet_atm_pre_ = wet_atm; + dry_atm_pre_ = dry_atm; } + KOKKOS_INLINE_FUNCTION + void operator()( + const Kokkos::TeamPolicy::member_type &team) const { + const int icol = team.league_rank(); // column index + + compute_dry_mixing_ratios(team, wet_atm_pre_, dry_atm_pre_, icol); + team.team_barrier(); + // vertical heights has to be computed after computing dry mixing ratios + // for atmosphere + compute_vertical_layer_heights(team, dry_atm_pre_, icol); + compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, icol); + } // Preprocess operator() + // local variables for preprocess struct - view_2d constituent_fluxes_pre_; - }; // MAMSrfOnlineEmiss::Preprocess + // number of horizontal columns and vertical levels + int ncol_pre_, nlev_pre_; + // local atmospheric and aerosol state data + mam_coupling::WetAtmosphere wet_atm_pre_; + mam_coupling::DryAtmosphere dry_atm_pre_; + }; // MAMSrfOnlineEmiss::Preprocess private: // preprocessing scratch pad Preprocess preprocess_; @@ -95,9 +138,11 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // FIXME: Remove the hardwired indices and use a function // to find them from an array. const std::map spcIndex_in_pcnst_ = { - {"so2", 12}, {"dms", 13}, {"so4_a1", 15}, - {"num_a1", 22}, {"so4_a2", 23}, {"num_a2", 27}, - {"pom_a4", 36}, {"bc_a4", 37}, {"num_a4", 39}}; + {"so2", 12}, {"dms", 13}, {"so4_a1", 15}, {"dst_a1", 19}, + {"ncl_a1", 20}, {"mom_a1", 21}, {"num_a1", 22}, {"so4_a2", 23}, + {"ncl_a2", 25}, {"mom_a2", 26}, {"num_a2", 27}, {"dst_a3", 28}, + {"ncl_a3", 29}, {"num_a3", 35}, {"pom_a4", 36}, {"bc_a4", 37}, + {"mom_a4", 38}, {"num_a4", 39}}; // A struct carrying all the fields needed to read // surface emissions of a species @@ -122,6 +167,13 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // A vector for carrying emissions for all the species std::vector srf_emiss_species_; + // For reading marine organics file + std::shared_ptr morg_horizInterp_; + std::shared_ptr morg_dataReader_; + marineOrganicsFunc::marineOrganicsTimeState morg_timeState_; + marineOrganicsFunc::marineOrganicsInput morg_data_start_, morg_data_end_; + marineOrganicsFunc::marineOrganicsOutput morg_data_out_; + // offset for converting pcnst index to gas_pcnst index static constexpr int offset_ = mam4::aero_model::pcnst - mam4::gas_chemistry::gas_pcnst; diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp new file mode 100644 index 00000000000..a04dff129f4 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp @@ -0,0 +1,133 @@ +#ifndef MARINE_ORGANICS_HPP +#define MARINE_ORGANICS_HPP + +// For AtmosphereInput +#include "share/io/scorpio_input.hpp" + +namespace scream { +namespace marine_organics { + +template +struct marineOrganicsFunctions { + using Device = DeviceType; + + using KT = KokkosTypes; + using MemberType = typename KT::MemberType; + using view_2d = typename KT::template view_2d; + + // ------------------------------------------------------------------------------------------- + struct marineOrganicsTimeState { + marineOrganicsTimeState() = default; + // Whether the timestate has been initialized. + // The current month + int current_month = -1; + // Julian Date for the beginning of the month, as defined in + // /src/share/util/scream_time_stamp.hpp + // See this file for definition of Julian Date. + Real t_beg_month; + // Current simulation Julian Date + Real t_now; + // Number of days in the current month, cast as a Real + Real days_this_month; + }; // marineOrganicsTimeState + + struct marineOrganicsData { + marineOrganicsData() = default; + marineOrganicsData(const int &ncol_, const int &nfields_) + : ncols(ncol_), nsectors(nfields_) { + init(ncols, nsectors, true); + } + + void init(const int &ncol, const int &nsector, const bool allocate) { + ncols = ncol; + nsectors = nsector; + if(allocate) emiss_sectors = view_2d("morgAllSectors", nsectors, ncols); + } // marineOrganicsData init + + // Basic spatial dimensions of the data + int ncols, nsectors; + view_2d emiss_sectors; + }; // marineOrganicsData + + // ------------------------------------------------------------------------------------------- + struct marineOrganicsInput { + marineOrganicsInput() = default; + marineOrganicsInput(const int &ncols_, const int &nfields_) { + init(ncols_, nfields_); + } + + void init(const int &ncols_, const int &nfields_) { + data.init(ncols_, nfields_, true); + } + marineOrganicsData data; // All marineOrganics fields + }; // marineOrganicsInput + + // The output is really just marineOrganicsData, but for clarity it might + // help to see a marineOrganicsOutput along a marineOrganicsInput in functions + // signatures + using marineOrganicsOutput = marineOrganicsData; + + // ------------------------------------------------------------------------------------------- + static std::shared_ptr create_horiz_remapper( + const std::shared_ptr &model_grid, + const std::string &marineOrganics_data_file, const std::string &map_file, + const std::vector &field_name, const std::string &dim_name1); + + // ------------------------------------------------------------------------------------------- + static std::shared_ptr create_data_reader( + const std::shared_ptr &horiz_remapper, + const std::string &data_file); + + // ------------------------------------------------------------------------------------------- + static void update_marine_organics_data_from_file( + std::shared_ptr &scorpio_reader, + const util::TimeStamp &ts, + const int &time_index, // zero-based + AbstractRemapper &horiz_interp, + marineOrganicsInput &marineOrganics_input); + + // ------------------------------------------------------------------------------------------- + static void update_marine_organics_timestate( + std::shared_ptr &scorpio_reader, + const util::TimeStamp &ts, AbstractRemapper &horiz_interp, + marineOrganicsTimeState &time_state, marineOrganicsInput &beg, + marineOrganicsInput &end); + + // ------------------------------------------------------------------------------------------- + static void marineOrganics_main(const marineOrganicsTimeState &time_state, + const marineOrganicsInput &data_beg, + const marineOrganicsInput &data_end, + const marineOrganicsOutput &data_out); + + // ------------------------------------------------------------------------------------------- + static void perform_time_interpolation( + const marineOrganicsTimeState &time_state, + const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end, + const marineOrganicsOutput &data_out); + + // ------------------------------------------------------------------------------------------- + // Performs convex interpolation of x0 and x1 at point t + template + KOKKOS_INLINE_FUNCTION static ScalarX linear_interp(const ScalarX &x0, + const ScalarX &x1, + const ScalarT &t); + + // ------------------------------------------------------------------------------------------- + static void init_marine_organics_file_read( + const int &ncol, const std::vector &field_name, + const std::string &dim_name1, + const std::shared_ptr &grid, + const std::string &data_file, const std::string &mapping_file, + // output + std::shared_ptr &marineOrganicsHorizInterp, + marineOrganicsInput &morg_data_start_, + marineOrganicsInput &morg_data_end_, marineOrganicsData &morg_data_out_, + std::shared_ptr &marineOrganicsDataReader); + +}; // struct marineOrganicsFunctions + +} // namespace marine_organics +} // namespace scream +#endif // MARINE_ORGANICS_HPP + +#include "marine_organics_impl.hpp" \ No newline at end of file diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp new file mode 100644 index 00000000000..389445fa024 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -0,0 +1,296 @@ +#ifndef MARINE_ORGANICS_IMPL_HPP +#define MARINE_ORGANICS_IMPL_HPP + +#include "share/grid/remap/identity_remapper.hpp" +#include "share/grid/remap/refining_remapper_p2p.hpp" +#include "share/io/scream_scorpio_interface.hpp" +#include "share/util/scream_timing.hpp" + +namespace scream { +namespace marine_organics { + +template +std::shared_ptr +marineOrganicsFunctions::create_horiz_remapper( + const std::shared_ptr &model_grid, + const std::string &data_file, const std::string &map_file, + const std::vector &field_name, const std::string &dim_name1) { + using namespace ShortFieldTagsNames; + + scorpio::register_file(data_file, scorpio::Read); + const int ncols_data = scorpio::get_dimlen(data_file, dim_name1); + + scorpio::release_file(data_file); + + // Since shallow clones are cheap, we may as well do it (less lines of + // code) + auto horiz_interp_tgt_grid = + model_grid->clone("marine_organics_horiz_interp_tgt_grid", true); + + const int ncols_model = model_grid->get_num_global_dofs(); + std::shared_ptr remapper; + if(ncols_data == ncols_model) { + remapper = std::make_shared( + horiz_interp_tgt_grid, IdentityRemapper::SrcAliasTgt); + } else { + EKAT_REQUIRE_MSG(ncols_data <= ncols_model, + "Error! We do not allow to coarsen marine organics " + "data to fit the model. We only allow\n" + " marine organics data to be at the same or " + "coarser resolution as the model.\n"); + // We must have a valid map file + EKAT_REQUIRE_MSG(map_file != "", + "ERROR: marine organics data is on a different grid " + "than the model one,\n" + " but remap file is missing from marine organics " + "parameter list."); + + remapper = + std::make_shared(horiz_interp_tgt_grid, map_file); + } + + remapper->registration_begins(); + + const auto tgt_grid = remapper->get_tgt_grid(); + + const auto layout_2d = tgt_grid->get_2d_scalar_layout(); + using namespace ekat::units; + using namespace ekat::prefixes; + Units umolC(micro * mol, "umol C"); + + std::vector fields_vector; + + const int field_size = field_name.size(); + for(int icomp = 0; icomp < field_size; ++icomp) { + auto comp_name = field_name[icomp]; + // set and allocate fields + Field f(FieldIdentifier(comp_name, layout_2d, umolC, tgt_grid->name())); + f.allocate_view(); + fields_vector.push_back(f); + remapper->register_field_from_tgt(f); + } + + remapper->registration_ends(); + + return remapper; + +} // create_horiz_remapper + +// ------------------------------------------------------------------------------------------- +template +std::shared_ptr +marineOrganicsFunctions::create_data_reader( + const std::shared_ptr &horiz_remapper, + const std::string &data_file) { + std::vector io_fields; + for(int ifld = 0; ifld < horiz_remapper->get_num_fields(); ++ifld) { + io_fields.push_back(horiz_remapper->get_src_field(ifld)); + } + const auto io_grid = horiz_remapper->get_src_grid(); + return std::make_shared(data_file, io_grid, io_fields, true); +} // create_data_reader + +// ------------------------------------------------------------------------------------------- +template +void marineOrganicsFunctions::update_marine_organics_data_from_file( + std::shared_ptr &scorpio_reader, const util::TimeStamp &ts, + const int &time_index, // zero-based + AbstractRemapper &horiz_interp, marineOrganicsInput &marineOrganics_input) { + start_timer("EAMxx::marineOrganics::update_marine_organics_data_from_file"); + + // 1. Read from file + start_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::read_" + "data"); + scorpio_reader->read_variables(); + stop_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::read_" + "data"); + + // 2. Run the horiz remapper (it is a do-nothing op if marineOrganics data is + // on same grid as model) + start_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::horiz_" + "remap"); + horiz_interp.remap(/*forward = */ true); + stop_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::horiz_" + "remap"); + + // 3. Get the tgt field of the remapper + start_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::get_" + "field"); + // Recall, the fields are registered in the order: + // Read the field from the file + + for(int ifld = 0; ifld < horiz_interp.get_num_fields(); ++ifld) { + auto sector = horiz_interp.get_tgt_field(ifld).get_view(); + const auto emiss = Kokkos::subview(marineOrganics_input.data.emiss_sectors, + ifld, Kokkos::ALL()); + Kokkos::deep_copy(emiss, sector); + } + + Kokkos::fence(); + + stop_timer( + "EAMxx::marineOrganics::update_marine_organics_data_from_file::get_" + "field"); + + stop_timer("EAMxx::marineOrganics::update_marine_organics_data_from_file"); + +} // END update_marine_organics_data_from_file + +// ------------------------------------------------------------------------------------------- +template +void marineOrganicsFunctions::update_marine_organics_timestate( + std::shared_ptr &scorpio_reader, const util::TimeStamp &ts, + AbstractRemapper &horiz_interp, marineOrganicsTimeState &time_state, + marineOrganicsInput &beg, marineOrganicsInput &end) { + // Now we check if we have to update the data that changes monthly + // NOTE: This means that marineOrganics assumes monthly data to update. Not + // any other frequency. + const auto month = ts.get_month() - 1; // Make it 0-based + if(month != time_state.current_month) { + // Update the marineOrganics time state information + time_state.current_month = month; + time_state.t_beg_month = + util::TimeStamp({ts.get_year(), month + 1, 1}, {0, 0, 0}) + .frac_of_year_in_days(); + time_state.days_this_month = util::days_in_month(ts.get_year(), month + 1); + + // Copy end'data into beg'data, and read in the new + // end + std::swap(beg, end); + + // Update the marineOrganics forcing data for this month and next month + // Start by copying next months data to this months data structure. + // NOTE: If the timestep is bigger than monthly this could cause the wrong + // values + // to be assigned. A timestep greater than a month is very unlikely + // so we will proceed. + int next_month = (time_state.current_month + 1) % 12; + update_marine_organics_data_from_file(scorpio_reader, ts, next_month, + horiz_interp, end); + } + +} // END updata_marine_organics_timestate + +// ------------------------------------------------------------------------------------------- +template +template +KOKKOS_INLINE_FUNCTION ScalarX marineOrganicsFunctions::linear_interp( + const ScalarX &x0, const ScalarX &x1, const ScalarT &t) { + return (1 - t) * x0 + t * x1; +} // linear_interp + +// ------------------------------------------------------------------------------------------- +template +void marineOrganicsFunctions::perform_time_interpolation( + const marineOrganicsTimeState &time_state, + const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end, + const marineOrganicsOutput &data_out) { + using ExeSpace = typename KT::ExeSpace; + using ESU = ekat::ExeSpaceUtils; + + // Gather time stamp info + auto &t_now = time_state.t_now; + auto &t_beg = time_state.t_beg_month; + auto &delta_t = time_state.days_this_month; + + // At this stage, begin/end must have the same dimensions + EKAT_REQUIRE(data_end.data.ncols == data_beg.data.ncols); + + auto delta_t_fraction = (t_now - t_beg) / delta_t; + + EKAT_REQUIRE_MSG(delta_t_fraction >= 0 && delta_t_fraction <= 1, + "Error! Convex interpolation with coefficient out of " + "[0,1].\n t_now : " + + std::to_string(t_now) + + "\n" + " t_beg : " + + std::to_string(t_beg) + + "\n delta_t: " + std::to_string(delta_t) + "\n"); + + const int nsectors = data_beg.data.nsectors; + const int ncols = data_beg.data.ncols; + using ExeSpace = typename KT::ExeSpace; + using ESU = ekat::ExeSpaceUtils; + const auto policy = ESU::get_default_team_policy(ncols, nsectors); + + Kokkos::parallel_for( + policy, KOKKOS_LAMBDA(const MemberType &team) { + const int icol = team.league_rank(); // column index + Kokkos::parallel_for( + Kokkos::TeamVectorRange(team, 0u, nsectors), [&](int isec) { + const auto beg = data_beg.data.emiss_sectors(isec, icol); + const auto end = data_end.data.emiss_sectors(isec, icol); + data_out.emiss_sectors(isec, icol) = + linear_interp(beg, end, delta_t_fraction); + }); + }); + Kokkos::fence(); + +} // perform_time_interpolation + +// ------------------------------------------------------------------------------------------- +template +void marineOrganicsFunctions::marineOrganics_main( + const marineOrganicsTimeState &time_state, + const marineOrganicsInput &data_beg, const marineOrganicsInput &data_end, + const marineOrganicsOutput &data_out) { + // Beg/End/Tmp month must have all sizes matching + + EKAT_REQUIRE_MSG( + data_end.data.ncols == data_beg.data.ncols, + "Error! marineOrganicsInput data structs must have the same number of " + "columns.\n"); + + // Horiz interpolation can be expensive, and does not depend on the particular + // time of the month, so it can be done ONCE per month, *outside* + // marineOrganics_main (when updating the beg/end states, reading them from + // file). + EKAT_REQUIRE_MSG(data_end.data.ncols == data_out.ncols, + "Error! Horizontal interpolation is performed *before* " + "calling marineOrganics_main,\n" + " marineOrganicsInput and marineOrganicsOutput data " + "structs must have the " + "same number columns " + << data_end.data.ncols << " " << data_out.ncols + << ".\n"); + + // Step 1. Perform time interpolation + perform_time_interpolation(time_state, data_beg, data_end, data_out); +} // marineOrganics_main + +// ------------------------------------------------------------------------------------------- +template +void marineOrganicsFunctions::init_marine_organics_file_read( + const int &ncol, const std::vector &field_name, + const std::string &dim_name1, + const std::shared_ptr &grid, + const std::string &data_file, const std::string &mapping_file, + // output + std::shared_ptr &marineOrganicsHorizInterp, + marineOrganicsInput &data_start_, marineOrganicsInput &data_end_, + marineOrganicsData &data_out_, + std::shared_ptr &marineOrganicsDataReader) { + // Init horizontal remap + + marineOrganicsHorizInterp = create_horiz_remapper( + grid, data_file, mapping_file, field_name, dim_name1); + + // Initialize the size of start/end/out data structures + data_start_ = marineOrganicsInput(ncol, field_name.size()); + data_end_ = marineOrganicsInput(ncol, field_name.size()); + data_out_.init(ncol, field_name.size(), true); + + // Create reader (an AtmosphereInput object) + marineOrganicsDataReader = + create_data_reader(marineOrganicsHorizInterp, data_file); + +} // init_marine_organics_file_read +} // namespace marine_organics +} // namespace scream + +#endif // MARINE_ORGANICS_IMPL_HPP \ No newline at end of file diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp new file mode 100644 index 00000000000..8b47c81d907 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp @@ -0,0 +1,44 @@ +#ifndef SOIL_ERODIBILITY_HPP +#define SOIL_ERODIBILITY_HPP + +// For AtmosphereInput +#include "share/io/scorpio_input.hpp" + +namespace scream { +namespace soil_erodibility { + +template +struct soilErodibilityFunctions { + using Device = DeviceType; + + using KT = KokkosTypes; + using const_view_1d = typename KT::template view_1d; + + static std::shared_ptr create_horiz_remapper( + const std::shared_ptr &model_grid, + const std::string &soilErodibility_data_file, const std::string &map_file, + const std::string &field_name, const std::string &dim_name1); + + static std::shared_ptr create_data_reader( + const std::shared_ptr &horiz_remapper, + const std::string &data_file); + + static void update_soil_erodibility_data_from_file( + std::shared_ptr &scorpio_reader, + AbstractRemapper &horiz_interp, const_view_1d &input); + + static void init_soil_erodibility_file_read( + const int ncol, const std::string field_name, const std::string dim_name1, + const std::shared_ptr &grid, + const std::string &data_file, const std::string &mapping_file, + // output + std::shared_ptr &SoilErodibilityHorizInterp, + std::shared_ptr &SoilErodibilityDataReader); + +}; // struct soilErodilityFunctions + +} // namespace soil_erodibility +} // namespace scream +#endif // SOIL_ERODIBILITY_HPP + +#include "soil_erodibility_impl.hpp" \ No newline at end of file diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp new file mode 100644 index 00000000000..af0c4d73c17 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp @@ -0,0 +1,147 @@ +#ifndef SOIL_ERODIBILITY_IMPL_HPP +#define SOIL_ERODIBILITY_IMPL_HPP + +#include "share/grid/remap/identity_remapper.hpp" +#include "share/grid/remap/refining_remapper_p2p.hpp" +#include "share/io/scream_scorpio_interface.hpp" +#include "share/util/scream_timing.hpp" + +namespace scream { +namespace soil_erodibility { + +template +std::shared_ptr +soilErodibilityFunctions::create_horiz_remapper( + const std::shared_ptr &model_grid, + const std::string &data_file, const std::string &map_file, + const std::string &field_name, const std::string &dim_name1) { + using namespace ShortFieldTagsNames; + + scorpio::register_file(data_file, scorpio::Read); + const int ncols_data = scorpio::get_dimlen(data_file, dim_name1); + + scorpio::release_file(data_file); + + // We could use model_grid directly if using same num levels, + // but since shallow clones are cheap, we may as well do it (less lines of + // code) + auto horiz_interp_tgt_grid = + model_grid->clone("soil_erodibility_horiz_interp_tgt_grid", true); + + const int ncols_model = model_grid->get_num_global_dofs(); + std::shared_ptr remapper; + if(ncols_data == ncols_model) { + remapper = std::make_shared( + horiz_interp_tgt_grid, IdentityRemapper::SrcAliasTgt); + } else { + EKAT_REQUIRE_MSG(ncols_data <= ncols_model, + "Error! We do not allow to coarsen soil erodibility " + "data to fit the model. We only allow\n" + " soil erodibility data to be at the same or " + "coarser resolution as the model.\n"); + // We must have a valid map file + EKAT_REQUIRE_MSG(map_file != "", + "ERROR: soil erodibility data is on a different grid " + "than the model one,\n" + " but remap file is missing from soil erodibility " + "parameter list."); + + remapper = + std::make_shared(horiz_interp_tgt_grid, map_file); + } + + remapper->registration_begins(); + + const auto tgt_grid = remapper->get_tgt_grid(); + + const auto layout_2d = tgt_grid->get_2d_scalar_layout(); + const auto nondim = ekat::units::Units::nondimensional(); + + Field soil_erodibility( + FieldIdentifier(field_name, layout_2d, nondim, tgt_grid->name())); + soil_erodibility.allocate_view(); + + remapper->register_field_from_tgt(soil_erodibility); + + remapper->registration_ends(); + + return remapper; + +} // create_horiz_remapper + +// ------------------------------------------------------------------------------------------- +template +std::shared_ptr +soilErodibilityFunctions::create_data_reader( + const std::shared_ptr &horiz_remapper, + const std::string &data_file) { + std::vector io_fields; + for(int i = 0; i < horiz_remapper->get_num_fields(); ++i) { + io_fields.push_back(horiz_remapper->get_src_field(i)); + } + const auto io_grid = horiz_remapper->get_src_grid(); + return std::make_shared(data_file, io_grid, io_fields, true); +} // create_data_reader + +// ------------------------------------------------------------------------------------------- +template +void soilErodibilityFunctions::update_soil_erodibility_data_from_file( + std::shared_ptr &scorpio_reader, + AbstractRemapper &horiz_interp, const_view_1d &input) { + start_timer("EAMxx::soilErodibility::update_soil_erodibility_data_from_file"); + + // 1. Read from file + start_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::read_" + "data"); + scorpio_reader->read_variables(); + stop_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::read_" + "data"); + + // 2. Run the horiz remapper (it is a do-nothing op if soilErodibility data is + // on same grid as model) + start_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::horiz_" + "remap"); + horiz_interp.remap(/*forward = */ true); + stop_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::horiz_" + "remap"); + + // 3. Get the tgt field of the remapper + start_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::get_" + "field"); + // Recall, the fields are registered in the order: + // Read the field from the file + input = horiz_interp.get_tgt_field(0).get_view(); + stop_timer( + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::get_" + "field"); + + stop_timer("EAMxx::soilErodibility::update_soil_erodibility_data_from_file"); + +} // END update_soil_erodibility_data_from_file + +// ------------------------------------------------------------------------------------------- +template +void soilErodibilityFunctions::init_soil_erodibility_file_read( + const int ncol, const std::string field_name, const std::string dim_name1, + const std::shared_ptr &grid, + const std::string &data_file, const std::string &mapping_file, + // output + std::shared_ptr &soilErodibilityHorizInterp, + std::shared_ptr &soilErodibilityDataReader) { + // Init horizontal remap + soilErodibilityHorizInterp = create_horiz_remapper( + grid, data_file, mapping_file, field_name, dim_name1); + + // Create reader (an AtmosphereInput object) + soilErodibilityDataReader = + create_data_reader(soilErodibilityHorizInterp, data_file); +} // init_soil_erodibility_file_read +} // namespace soil_erodibility +} // namespace scream + +#endif // SOIL_ERODIBILITY_IMPL_HPP diff --git a/components/eamxx/src/physics/mam/srf_emission.hpp b/components/eamxx/src/physics/mam/srf_emission.hpp index 29aaca421ea..8dc5be1d05a 100644 --- a/components/eamxx/src/physics/mam/srf_emission.hpp +++ b/components/eamxx/src/physics/mam/srf_emission.hpp @@ -4,8 +4,6 @@ #include "share/util/scream_timing.hpp" namespace scream::mam_coupling { -namespace { - template struct srfEmissFunctions { using Device = DeviceType; @@ -131,7 +129,6 @@ struct srfEmissFunctions { std::shared_ptr &SrfEmissDataReader); }; // struct srfEmissFunctions -} // namespace } // namespace scream::mam_coupling #endif // SRF_EMISSION_HPP diff --git a/components/eamxx/src/physics/mam/srf_emission_impl.hpp b/components/eamxx/src/physics/mam/srf_emission_impl.hpp index 48dc1fa7087..b8ebfdbe501 100644 --- a/components/eamxx/src/physics/mam/srf_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/srf_emission_impl.hpp @@ -6,8 +6,6 @@ #include "share/io/scream_scorpio_interface.hpp" namespace scream::mam_coupling { -namespace { - template std::shared_ptr srfEmissFunctions::create_horiz_remapper( @@ -201,11 +199,6 @@ void srfEmissFunctions::update_srfEmiss_data_from_file( // Recall, the fields are registered in the order: ps, ccn3, g_sw, ssa_sw, // tau_sw, tau_lw - const auto &layout = srfEmiss_horiz_interp.get_tgt_field(0) - .get_header() - .get_identifier() - .get_layout(); - // Read fields from the file for(int i = 0; i < srfEmiss_horiz_interp.get_num_fields(); ++i) { auto sector = @@ -279,8 +272,6 @@ void srfEmissFunctions::init_srf_emiss_objects( SrfEmissDataReader = create_srfEmiss_data_reader(SrfEmissHorizInterp, data_file); } // init_srf_emiss_objects - -} // namespace } // namespace scream::mam_coupling #endif // SRF_EMISSION_IMPL_HPP diff --git a/components/eamxx/tests/multi-process/physics_only/mam/mam4_srf_online_emiss_mam4_constituent_fluxes/input.yaml b/components/eamxx/tests/multi-process/physics_only/mam/mam4_srf_online_emiss_mam4_constituent_fluxes/input.yaml index 55c62d69aa2..48045295050 100644 --- a/components/eamxx/tests/multi-process/physics_only/mam/mam4_srf_online_emiss_mam4_constituent_fluxes/input.yaml +++ b/components/eamxx/tests/multi-process/physics_only/mam/mam4_srf_online_emiss_mam4_constituent_fluxes/input.yaml @@ -23,7 +23,9 @@ atmosphere_processes: srf_emis_specifier_for_pom_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_pom_a4_surf_ne2np4_2010_clim_c20240726.nc srf_emis_specifier_for_so4_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a1_surf_ne2np4_2010_clim_c20240726.nc srf_emis_specifier_for_so4_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a2_surf_ne2np4_2010_clim_c20240726.nc - + + soil_erodibility_file: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/dst_ne2np4_c20241028.nc + marine_organics_file: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne2np4_c20241030.nc grids_manager: Type: Mesh Free geo_data_source: IC_FILE @@ -40,6 +42,13 @@ initial_conditions: topography_filename: ${TOPO_DATA_DIR}/${EAMxx_tests_TOPO_FILE} pbl_height: 0.0 + dstflx : 0.0 + ocnfrac: 0.10000000000000000E+001 + sst: 0.30178553874977507E+003 + cldfrac_tot: 0.138584624960092 + horiz_winds: [-0.24988988196194634E+000, -0.23959782871450760E+000] + constituent_fluxes: 0.0 + # The parameters for I/O control Scorpio: diff --git a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt index c8e690b929f..65f377b4db1 100644 --- a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt +++ b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt @@ -36,6 +36,8 @@ set (TEST_INPUT_FILES scream/mam4xx/emissions/ne2np4/surface/cmip6_mam4_pom_a4_surf_ne2np4_2010_clim_c20240726.nc scream/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a1_surf_ne2np4_2010_clim_c20240726.nc scream/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a2_surf_ne2np4_2010_clim_c20240726.nc + scream/mam4xx/emissions/ne2np4/dst_ne2np4_c20241028.nc + scream/mam4xx/emissions/ne2np4/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne2np4_c20241030.nc ) foreach (file IN ITEMS ${TEST_INPUT_FILES}) GetInputFile(${file}) diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index e7af45178aa..0819562d959 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -23,6 +23,8 @@ atmosphere_processes: srf_emis_specifier_for_so4_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a1_surf_ne2np4_2010_clim_c20240726.nc srf_emis_specifier_for_so4_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so4_a2_surf_ne2np4_2010_clim_c20240726.nc + soil_erodibility_file: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/dst_ne2np4_c20241028.nc + marine_organics_file: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne2np4_c20241030.nc grids_manager: Type: Mesh Free geo_data_source: IC_FILE @@ -38,11 +40,15 @@ initial_conditions: Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} topography_filename: ${TOPO_DATA_DIR}/${EAMxx_tests_TOPO_FILE} phis : 1.0 - #These should come from the input file - - #we should get the following variables from other processes + + # we should get the following variables from other processes pbl_height : 1.0 - + dstflx : 0.0 + ocnfrac: 0.10000000000000000E+001 + sst: 0.30178553874977507E+003 + cldfrac_tot: 0.138584624960092 + horiz_winds: [-0.24988988196194634E+000, -0.23959782871450760E+000] + constituent_fluxes: 0.0 # The parameters for I/O control Scorpio: output_yaml_files: ["output.yaml"] diff --git a/externals/mam4xx b/externals/mam4xx index 4431bbd1eef..e26b5b6faa5 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 4431bbd1eef46de25be3a04e7091c9255bd0b819 +Subproject commit e26b5b6faa5cdf39a5421328165c49bdb728b038