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