From 9673d6bddd699d8c5987a18720f8e5fea9309bf6 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Tue, 10 Sep 2024 01:17:58 -0600 Subject: [PATCH 01/41] emissions interface--running and calling mam4xx. next diagnose error --- ...and_online_emissions_process_interface.cpp | 131 ++++++++++++------ ...and_online_emissions_process_interface.hpp | 33 +++-- .../eamxx/src/physics/mam/online_emission.hpp | 102 ++++++++++++++ .../src/physics/mam/online_emission_impl.hpp | 60 ++++++++ .../single-process/mam/emissions/input.yaml | 28 ++-- 5 files changed, 293 insertions(+), 61 deletions(-) create mode 100644 components/eamxx/src/physics/mam/online_emission.hpp create mode 100644 components/eamxx/src/physics/mam/online_emission_impl.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..7f85e931080 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 @@ -19,11 +19,11 @@ MAMSrfOnlineEmiss::MAMSrfOnlineEmiss(const ekat::Comm &comm, // ================================================================ void MAMSrfOnlineEmiss::set_grids( const std::shared_ptr grids_manager) { - grid_ = grids_manager->get_grid("Physics"); + grid_ = grids_manager->get_grid("Physics"); const auto &grid_name = grid_->name(); - ncol_ = grid_->get_num_local_dofs(); // Number of columns on this rank - nlev_ = grid_->get_num_vertical_levels(); // Number of levels per column + ncol_ = grid_->get_num_local_dofs(); // Number of columns on this rank + nlev_ = grid_->get_num_vertical_levels(); // Number of levels per column using namespace ekat::units; @@ -49,20 +49,21 @@ void MAMSrfOnlineEmiss::set_grids( //-------------------------------------------------------------------- srf_emiss_ dms; // File name, name and sectors - dms.data_file = m_params.get("srf_emis_specifier_for_DMS"); + dms.data_file = m_params.get("srf_emis_specifier_for_DMS"); dms.species_name = "dms"; - dms.sectors = {"DMS"}; - srf_emiss_species_.push_back(dms); // add to the vector + dms.sectors = {"DMS"}; + srf_emiss_species_.push_back(dms); // add to the vector //-------------------------------------------------------------------- // Init so2 srf emiss data structures //-------------------------------------------------------------------- srf_emiss_ so2; // File name, name and sectors - so2.data_file = m_params.get("srf_emis_specifier_for_SO2"); + so2.data_file = m_params.get("srf_emis_specifier_for_SO2"); so2.species_name = "so2"; - so2.sectors = {"AGR", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(so2); // add to the vector + 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 //-------------------------------------------------------------------- @@ -70,8 +71,8 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors bc_a4.data_file = m_params.get("srf_emis_specifier_for_bc_a4"); bc_a4.species_name = "bc_a4"; - bc_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(bc_a4); // add to the vector + bc_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; + srf_emiss_species_.push_back(bc_a4); // add to the vector //-------------------------------------------------------------------- // Init num_a1 srf emiss data structures @@ -80,9 +81,9 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a1.data_file = m_params.get("srf_emis_specifier_for_num_a1"); num_a1.species_name = "num_a1"; - num_a1.sectors = {"num_a1_SO4_AGR", "num_a1_SO4_SHP", "num_a1_SO4_SLV", - "num_a1_SO4_WST"}; - srf_emiss_species_.push_back(num_a1); // add to the vector + num_a1.sectors = {"num_a1_SO4_AGR", "num_a1_SO4_SHP", "num_a1_SO4_SLV", + "num_a1_SO4_WST"}; + srf_emiss_species_.push_back(num_a1); // add to the vector //-------------------------------------------------------------------- // Init num_a2 srf emiss data structures @@ -91,8 +92,8 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a2.data_file = m_params.get("srf_emis_specifier_for_num_a2"); num_a2.species_name = "num_a2"; - num_a2.sectors = {"num_a2_SO4_RCO", "num_a2_SO4_TRA"}; - srf_emiss_species_.push_back(num_a2); // add to the vector + num_a2.sectors = {"num_a2_SO4_RCO", "num_a2_SO4_TRA"}; + srf_emiss_species_.push_back(num_a2); // add to the vector //-------------------------------------------------------------------- // Init num_a4 srf emiss data structures @@ -101,12 +102,12 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a4.data_file = m_params.get("srf_emis_specifier_for_num_a4"); num_a4.species_name = "num_a4"; - num_a4.sectors = { - "num_a1_BC_AGR", "num_a1_BC_ENE", "num_a1_BC_IND", "num_a1_BC_RCO", - "num_a1_BC_SHP", "num_a1_BC_SLV", "num_a1_BC_TRA", "num_a1_BC_WST", - "num_a1_POM_AGR", "num_a1_POM_ENE", "num_a1_POM_IND", "num_a1_POM_RCO", - "num_a1_POM_SHP", "num_a1_POM_SLV", "num_a1_POM_TRA", "num_a1_POM_WST"}; - srf_emiss_species_.push_back(num_a4); // add to the vector + num_a4.sectors = { + "num_a1_BC_AGR", "num_a1_BC_ENE", "num_a1_BC_IND", "num_a1_BC_RCO", + "num_a1_BC_SHP", "num_a1_BC_SLV", "num_a1_BC_TRA", "num_a1_BC_WST", + "num_a1_POM_AGR", "num_a1_POM_ENE", "num_a1_POM_IND", "num_a1_POM_RCO", + "num_a1_POM_SHP", "num_a1_POM_SLV", "num_a1_POM_TRA", "num_a1_POM_WST"}; + srf_emiss_species_.push_back(num_a4); // add to the vector //-------------------------------------------------------------------- // Init pom_a4 srf emiss data structures @@ -116,7 +117,7 @@ void MAMSrfOnlineEmiss::set_grids( pom_a4.data_file = m_params.get("srf_emis_specifier_for_pom_a4"); pom_a4.species_name = "pom_a4"; pom_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(pom_a4); // add to the vector + srf_emiss_species_.push_back(pom_a4); // add to the vector //-------------------------------------------------------------------- // Init so4_a1 srf emiss data structures @@ -125,7 +126,7 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors so4_a1.data_file = m_params.get("srf_emis_specifier_for_so4_a1"); so4_a1.species_name = "so4_a1"; - so4_a1.sectors = {"AGR", "SHP", "SLV", "WST"}; + so4_a1.sectors = {"AGR", "SHP", "SLV", "WST"}; srf_emiss_species_.push_back(so4_a1); //-------------------------------------------------------------------- @@ -135,21 +136,20 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors so4_a2.data_file = m_params.get("srf_emis_specifier_for_so4_a2"); so4_a2.species_name = "so4_a2"; - so4_a2.sectors = {"RCO", "TRA"}; + so4_a2.sectors = {"RCO", "TRA"}; srf_emiss_species_.push_back(so4_a2); //-------------------------------------------------------------------- // Init data structures to read and interpolate //-------------------------------------------------------------------- - for(srf_emiss_ &ispec_srf : srf_emiss_species_) { + for (srf_emiss_ &ispec_srf : srf_emiss_species_) { srfEmissFunc::init_srf_emiss_objects( ncol_, grid_, ispec_srf.data_file, ispec_srf.sectors, srf_map_file, // output ispec_srf.horizInterp_, ispec_srf.data_start_, ispec_srf.data_end_, ispec_srf.data_out_, ispec_srf.dataReader_); } - -} // set_grid ends +} // set_grid ends // ================================================================ // REQUEST_BUFFER_SIZE_IN_BYTES @@ -167,9 +167,9 @@ size_t MAMSrfOnlineEmiss::requested_buffer_size_in_bytes() const { // intermediate (dry) quantities on the given number of columns with the given // number of vertical levels. Returns the number of bytes allocated. void MAMSrfOnlineEmiss::init_buffers(const ATMBufferManager &buffer_manager) { - EKAT_REQUIRE_MSG( - buffer_manager.allocated_bytes() >= requested_buffer_size_in_bytes(), - "Error! Insufficient buffer size.\n"); + EKAT_REQUIRE_MSG(buffer_manager.allocated_bytes() >= + requested_buffer_size_in_bytes(), + "Error! Insufficient buffer size.\n"); size_t used_mem = mam_coupling::init_buffer(buffer_manager, ncol_, nlev_, buffer_); @@ -206,18 +206,26 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //-------------------------------------------------------------------- // Update surface emissions from file //-------------------------------------------------------------------- - for(srf_emiss_ &ispec_srf : srf_emiss_species_) { + for (srf_emiss_ &ispec_srf : srf_emiss_species_) { srfEmissFunc::update_srfEmiss_data_from_file( ispec_srf.dataReader_, timestamp(), curr_month, *ispec_srf.horizInterp_, - ispec_srf.data_end_); // output + ispec_srf.data_end_); // output } + //-------------------------------------------------------------------- + // Initialize online emissions from file + //-------------------------------------------------------------------- + online_emis_data.init(ncol_); + onlineEmiss::init_from_input_file(m_params, online_emis_data); + //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- preprocess_.initialize(constituent_fluxes_); + onlineEmiss::transfer_to_cflux(online_emis_data, spcIndex_in_pcnst_, + constituent_fluxes_); -} // end initialize_impl() +} // end initialize_impl() // ================================================================ // RUN_IMPL @@ -225,15 +233,60 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { void MAMSrfOnlineEmiss::run_impl(const double dt) { // Zero-out output Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); + // copy current values to online-emissions-local version + Kokkos::deep_copy(online_emis_data.constituent_fluxes, constituent_fluxes_); // Gather time and state information for interpolation auto ts = timestamp() + dt; + // Parallel loop over all the columns to update units + Kokkos::parallel_for( + "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + // need to initialize values for: + // SeasaltEmissionsData.{mpoly, mprot, mlip} + // DustEmissionsData.dust_dmt_vwr (done here for now) + // OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, + // v_bottom, z_bottom, ocean_frac} + // NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, + // which is a chunk-wise variable at this point + // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom + // col-value once inside aero_model_emissions() + + view_1d fluxes_col = Kokkos::subview(online_emis_data.constituent_fluxes, + icol, Kokkos::ALL()); + mam4::aero_model_emissions::aero_model_emissions(fluxes_col); + }); + + // for (onlineEmissData &ispec_online : online_emis_data) { + // //-------------------------------------------------------------------- + // // Modify units to MKS units (from molecules/cm2/s to kg/m2/s) + // //-------------------------------------------------------------------- + // // Get species index in array with pcnst dimension + // // (e.g., state_q or constituent_fluxes_) + // const int species_index = + // spcIndex_in_pcnst_.at(ispec_online.species_name); + + // // 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_]; + // // Parallel loop over all the columns to update units + // Kokkos::parallel_for( + // "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + // fluxes_in_mks_units(icol) = + // ispec_online.data_out_.emiss_sectors(0, icol) * mfactor; + // constituent_fluxes(icol, species_index) = + // fluxes_in_mks_units(icol); + // }); + + // } // for loop for species + //-------------------------------------------------------------------- // Interpolate srf emiss data //-------------------------------------------------------------------- - for(srf_emiss_ &ispec_srf : srf_emiss_species_) { + for (srf_emiss_ &ispec_srf : srf_emiss_species_) { // Update TimeState, note the addition of dt ispec_srf.timeState_.t_now = ts.frac_of_year_in_days(); @@ -256,7 +309,7 @@ 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_; + auto constituent_fluxes = this->constituent_fluxes_; const Real mfactor = amufac * mam4::gas_chemistry::adv_mass[species_index - offset_]; // Parallel loop over all the columns to update units @@ -267,9 +320,9 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); - } // for loop for species + } // for loop for species Kokkos::fence(); -} // run_imple ends +} // run_impl ends // ============================================================================= -} // namespace scream +} // 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..65ac4f6375e 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 @@ -6,6 +6,7 @@ // For MAM4 aerosol configuration #include +#include #include // For declaring surface and online emission class derived from atm process @@ -20,7 +21,7 @@ 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 KT = ekat::KokkosTypes; using view_1d = typename KT::template view_1d; using view_2d = typename KT::template view_2d; @@ -40,10 +41,12 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { view_1d fluxes_in_mks_units_; // Unified atomic mass unit used for unit conversion (BAD constant) - static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu + static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu - public: +public: using srfEmissFunc = mam_coupling::srfEmissFunctions; + using onlineEmiss = + mam_coupling::onlineEmissions; // Constructor MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms); @@ -59,8 +62,8 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { std::string name() const { return "mam_srf_online_emissions"; } // grid - void set_grids( - const std::shared_ptr grids_manager) override; + void + set_grids(const std::shared_ptr grids_manager) override; // management of common atm process memory size_t requested_buffer_size_in_bytes() const override; @@ -85,9 +88,9 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { } // local variables for preprocess struct view_2d constituent_fluxes_pre_; - }; // MAMSrfOnlineEmiss::Preprocess + }; // MAMSrfOnlineEmiss::Preprocess - private: +private: // preprocessing scratch pad Preprocess preprocess_; @@ -95,9 +98,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,12 +127,14 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // A vector for carrying emissions for all the species std::vector srf_emiss_species_; + onlineEmiss::onlineEmissData online_emis_data; + // offset for converting pcnst index to gas_pcnst index static constexpr int offset_ = mam4::aero_model::pcnst - mam4::gas_chemistry::gas_pcnst; -}; // MAMSrfOnlineEmiss +}; // MAMSrfOnlineEmiss -} // namespace scream +} // namespace scream -#endif // EAMXX_MAM_SRF_ONLINE_EMISS_HPP +#endif // EAMXX_MAM_SRF_ONLINE_EMISS_HPP diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp new file mode 100644 index 00000000000..0a2b3290e68 --- /dev/null +++ b/components/eamxx/src/physics/mam/online_emission.hpp @@ -0,0 +1,102 @@ +#ifndef ONLINE_EMISSION_HPP +#define ONLINE_EMISSION_HPP + +#include "share/util/scream_timing.hpp" + +namespace scream::mam_coupling { +namespace { + +template struct onlineEmissions { + using Device = DeviceType; + + using KT = KokkosTypes; + using MemberType = typename KT::MemberType; + + struct onlineEmissTimeState { + onlineEmissTimeState() = 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; + }; // onlineEmissTimeState + + struct onlineEmissData { + // Basic spatial dimensions of the data + int ncols; + view_2d flux_data; + // local copy of main fluxes array + view_2d constituent_fluxes; + // FIXME: read this from input or get from mam4xx::aero_model_emissions? + const std::vector spec_names = {"so2", "soag", "bc_a4", + "num_a1", "num_a2", "num_a4", + "pom_a4", "so4_a1", "so4_a2"}; + // FIXME: change this when the above is dynamically-determined + int nspec = 9; + const std::string root_IC_str = "initial_condition_"; + + onlineEmissData() = default; + onlineEmissData(const int ncol_, const int nspec_) + : ncols(ncol_), nspec(nspec_) { + init(ncols, nspec, true); + } + + onlineEmissData(const int ncol_) : ncols(ncol_) { + init(ncols, nspec, true); + } + + void init(const int ncol_, const int nspec_, const bool allocate_) { + ncols = ncol_; + nspec = nspec_; + if (allocate_) + flux_data = view_2d("onlineEmissData", nspec, ncols); + } // onlineEmissData init + void init(const int ncol_, const bool allocate_) { + ncols = ncol_; + if (allocate_) + flux_data = view_2d("onlineEmissData", nspec, ncols); + } // onlineEmissData init + void init(const int ncol_) { + ncols = ncol_; + flux_data = view_2d("onlineEmissData", nspec, ncols); + } // onlineEmissData init + }; // onlineEmissData + + // The output is really just onlineEmissData, but for clarity it might + // help to see a onlineEmissOutput along a onlineEmissInput in functions + // signatures + // using onlineEmissOutput = onlineEmissData; + + // --------------------------------------------------------------------------- + // Online emissions routines + // --------------------------------------------------------------------------- + // FIXME: + // static void onlineEmiss_main(const onlineEmissTimeState &time_state, + // const onlineEmissInput &data_beg, + // const onlineEmissInput &data_end, + // const onlineEmissOutput &data_out); + + static void init_from_input_file(const ekat::ParameterList &m_params, + onlineEmissData &data); + static void + transfer_to_cflux(const onlineEmissData &data, + const std::map idx_map, + view_2d &fluxes); + // static void update_onlineEmiss_timestate( + // std::shared_ptr &scorpio_reader, + // const util::TimeStamp &ts, AbstractRemapper &onlineEmiss_horiz_interp, + // onlineEmissTimeState &time_state, onlineEmissInput &onlineEmiss_beg, + // onlineEmissInput &onlineEmiss_end); + +}; // struct onlineEmissions +} // namespace +} // namespace scream::mam_coupling +#endif // ONLINE_EMISSION_HPP + +#include "online_emission_impl.hpp" diff --git a/components/eamxx/src/physics/mam/online_emission_impl.hpp b/components/eamxx/src/physics/mam/online_emission_impl.hpp new file mode 100644 index 00000000000..5d374bfba76 --- /dev/null +++ b/components/eamxx/src/physics/mam/online_emission_impl.hpp @@ -0,0 +1,60 @@ +#ifndef ONLINE_EMISSION_IMPL_HPP +#define ONLINE_EMISSION_IMPL_HPP + +namespace scream::mam_coupling { +namespace { + +template +void onlineEmissions::init_from_input_file( + const ekat::ParameterList ¶ms, onlineEmissData &data) { + + const int nspec = data.nspec; + const int ncols = data.ncols; + using ExeSpace = typename KT::ExeSpace; + using ESU = ekat::ExeSpaceUtils; + const auto policy = ESU::get_default_team_policy(ncols, nspec); + + start_timer("EAMxx::onlineEmiss::init_onlineEmiss_data_from_input_file"); + // 1. Read from input file + // FIXME: currently reading a single placeholder scalar--should be + // ncols-sized array + for (int ispec = 0; ispec < nspec; ++ispec) { + Real init_cond_val = + params.get(data.root_IC_str + data.spec_names[ispec]); + // FIXME: is this overkill--i.e., would a mirror/deep_copy make more sense? + Kokkos::parallel_for( + policy, KOKKOS_LAMBDA(const MemberType &team) { + const int jcol = team.league_rank(); // column index + data.flux_data(ispec, jcol) = init_cond_val; + }); + } + stop_timer("EAMxx::onlineEmiss::init_onlineEmiss_data_from_input_file"); + +} // end init_from_input_file() + +template +void onlineEmissions::transfer_to_cflux( + const onlineEmissData &data, const std::map idx_map, + view_2d &fluxes) { + // FIXME: move out to onlineEmissions struct? + const int nspec = data.nspec; + const int ncols = data.ncols; + using ExeSpace = typename KT::ExeSpace; + using ESU = ekat::ExeSpaceUtils; + const auto policy = ESU::get_default_team_policy(ncols, nspec); + + Kokkos::parallel_for( + policy, KOKKOS_LAMBDA(const MemberType &team) { + const int jcol = team.league_rank(); // column index + Kokkos::parallel_for( + Kokkos::TeamThreadRange(team, nspec), [&](const int ispec) { + auto s_idx = idx_map.at(data.spec_names[ispec]); + fluxes(jcol, s_idx) = data.flux_data(ispec, jcol); + }); + }); +} + +} // namespace +} // namespace scream::mam_coupling + +#endif // ONLINE_EMISSION_IMPL_HPP diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index e7af45178aa..3c7bd352f72 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -13,15 +13,25 @@ atmosphere_processes: mam4_srf_online_emiss: # MAM4xx-Surface-Emissions srf_remap_file: "" - srf_emis_specifier_for_DMS: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/DMSflux.2010.ne2np4_conserv.POPmonthlyClimFromACES4BGC_c20240726.nc - srf_emis_specifier_for_SO2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so2_surf_ne2np4_2010_clim_c20240723.nc - srf_emis_specifier_for_bc_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_bc_a4_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a1_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a2_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a4_surf_ne2np4_2010_clim_c20240726.nc - 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 + srf_emis_specifier_for_DMS: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/DMSflux.2010.ne2np4_conserv.POPmonthlyClimFromACES4BGC_c20240726.nc + srf_emis_specifier_for_SO2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so2_surf_ne2np4_2010_clim_c20240723.nc + srf_emis_specifier_for_bc_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_bc_a4_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a1_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a2_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a4_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_pom_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_pom_a4_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_so4_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so4_a1_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_so4_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so4_a2_surf_ne2np4_2010_clim_c20240726.nc + # FIXME: just set everything to a constant scalar for testing + initial_condition_so2: 1.0 + initial_condition_soag: 2.0 + initial_condition_bc_a4: 3.0 + initial_condition_num_a1: 4.0 + initial_condition_num_a2: 5.0 + initial_condition_num_a4: 6.0 + initial_condition_pom_a4: 7.0 + initial_condition_so4_a1: 8.0 + initial_condition_so4_a2: 9.0 grids_manager: Type: Mesh Free From c3f2ebff570a2ab8bcc576fbcf92eaba8c621e2c Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Wed, 9 Oct 2024 15:47:02 -0600 Subject: [PATCH 02/41] cleanup to fix warnings and change path to emissions input files --- .../eamxx/src/physics/mam/online_emission.hpp | 9 -------- .../src/physics/mam/online_emission_impl.hpp | 4 ---- .../eamxx/src/physics/mam/srf_emission.hpp | 3 --- .../src/physics/mam/srf_emission_impl.hpp | 21 ++++++++++++------- .../single-process/mam/emissions/input.yaml | 18 ++++++++-------- 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp index 0a2b3290e68..bc5a510ccf7 100644 --- a/components/eamxx/src/physics/mam/online_emission.hpp +++ b/components/eamxx/src/physics/mam/online_emission.hpp @@ -4,8 +4,6 @@ #include "share/util/scream_timing.hpp" namespace scream::mam_coupling { -namespace { - template struct onlineEmissions { using Device = DeviceType; @@ -88,14 +86,7 @@ template struct onlineEmissions { transfer_to_cflux(const onlineEmissData &data, const std::map idx_map, view_2d &fluxes); - // static void update_onlineEmiss_timestate( - // std::shared_ptr &scorpio_reader, - // const util::TimeStamp &ts, AbstractRemapper &onlineEmiss_horiz_interp, - // onlineEmissTimeState &time_state, onlineEmissInput &onlineEmiss_beg, - // onlineEmissInput &onlineEmiss_end); - }; // struct onlineEmissions -} // namespace } // namespace scream::mam_coupling #endif // ONLINE_EMISSION_HPP diff --git a/components/eamxx/src/physics/mam/online_emission_impl.hpp b/components/eamxx/src/physics/mam/online_emission_impl.hpp index 5d374bfba76..860a4cac2be 100644 --- a/components/eamxx/src/physics/mam/online_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/online_emission_impl.hpp @@ -2,8 +2,6 @@ #define ONLINE_EMISSION_IMPL_HPP namespace scream::mam_coupling { -namespace { - template void onlineEmissions::init_from_input_file( const ekat::ParameterList ¶ms, onlineEmissData &data) { @@ -53,8 +51,6 @@ void onlineEmissions::transfer_to_cflux( }); }); } - -} // namespace } // namespace scream::mam_coupling #endif // ONLINE_EMISSION_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..b090e3746cb 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( @@ -104,6 +102,9 @@ void srfEmissFunctions::perform_time_interpolation( // NOTE: we *assume* data_beg and data_end have the *same* hybrid v coords. // IF this ever ceases to be the case, you can interp those too. + 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; @@ -181,6 +182,9 @@ void srfEmissFunctions::update_srfEmiss_data_from_file( const int time_index, // zero-based AbstractRemapper &srfEmiss_horiz_interp, srfEmissInput &srfEmiss_input) { using namespace ShortFieldTagsNames; + // NOTE: these are currently unused + // using ESU = ekat::ExeSpaceUtils; + // using Member = typename KokkosTypes::MemberType; start_timer("EAMxx::srfEmiss::update_srfEmiss_data_from_file"); @@ -201,10 +205,13 @@ 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(); + // NOTE: these are currently unused + // const auto &layout = srfEmiss_horiz_interp.get_tgt_field(0) + // .get_header() + // .get_identifier() + // .get_layout(); + + const int ncols = layout.dim(COL); // Read fields from the file for(int i = 0; i < srfEmiss_horiz_interp.get_num_fields(); ++i) { @@ -279,8 +286,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/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 3c7bd352f72..93f187e7de3 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -13,15 +13,15 @@ atmosphere_processes: mam4_srf_online_emiss: # MAM4xx-Surface-Emissions srf_remap_file: "" - srf_emis_specifier_for_DMS: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/DMSflux.2010.ne2np4_conserv.POPmonthlyClimFromACES4BGC_c20240726.nc - srf_emis_specifier_for_SO2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so2_surf_ne2np4_2010_clim_c20240723.nc - srf_emis_specifier_for_bc_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_bc_a4_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a1_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a2_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_num_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_num_a4_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_pom_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_pom_a4_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_so4_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so4_a1_surf_ne2np4_2010_clim_c20240726.nc - srf_emis_specifier_for_so4_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/cmip6_mam4_so4_a2_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_DMS: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/DMSflux.2010.ne2np4_conserv.POPmonthlyClimFromACES4BGC_c20240726.nc + srf_emis_specifier_for_SO2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_so2_surf_ne2np4_2010_clim_c20240723.nc + srf_emis_specifier_for_bc_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_bc_a4_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a1: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a1_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a2: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a2_surf_ne2np4_2010_clim_c20240726.nc + srf_emis_specifier_for_num_a4: ${SCREAM_DATA_DIR}/mam4xx/emissions/ne2np4/surface/cmip6_mam4_num_a4_surf_ne2np4_2010_clim_c20240726.nc + 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 # FIXME: just set everything to a constant scalar for testing initial_condition_so2: 1.0 initial_condition_soag: 2.0 From 749f2076bf667c874f9d6c541b0b44e839f3a464 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Tue, 15 Oct 2024 06:59:46 -0600 Subject: [PATCH 03/41] got online emis. interface working --- ...and_online_emissions_process_interface.cpp | 78 +++++++------------ ...and_online_emissions_process_interface.hpp | 2 +- .../eamxx/src/physics/mam/online_emission.hpp | 42 ++++++---- .../src/physics/mam/online_emission_impl.hpp | 19 +++-- .../single-process/mam/emissions/input.yaml | 21 ++--- 5 files changed, 79 insertions(+), 83 deletions(-) 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 7f85e931080..b50da0d4445 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 @@ -33,6 +33,9 @@ void MAMSrfOnlineEmiss::set_grids( // ------------------------------------------------------------- // These variables are "Computed" or outputs for the process + // FIXME: check into whether this should be an "Updated" field because + // I haven't yet determined if an updated field needs to be output + // as a tendency, and thus the flux would need dt [s] factored out // ------------------------------------------------------------- static constexpr Units m2(m * m, "m2"); // Constituent fluxes of species in [kg/m2/s] @@ -215,15 +218,13 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //-------------------------------------------------------------------- // Initialize online emissions from file //-------------------------------------------------------------------- - online_emis_data.init(ncol_); - onlineEmiss::init_from_input_file(m_params, online_emis_data); + online_emissions.online_emis_data.init(ncol_); + online_emissions.init_from_input_file(m_params); //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- preprocess_.initialize(constituent_fluxes_); - onlineEmiss::transfer_to_cflux(online_emis_data, spcIndex_in_pcnst_, - constituent_fluxes_); } // end initialize_impl() @@ -233,55 +234,10 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { void MAMSrfOnlineEmiss::run_impl(const double dt) { // Zero-out output Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); - // copy current values to online-emissions-local version - Kokkos::deep_copy(online_emis_data.constituent_fluxes, constituent_fluxes_); // Gather time and state information for interpolation auto ts = timestamp() + dt; - // Parallel loop over all the columns to update units - Kokkos::parallel_for( - "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - // need to initialize values for: - // SeasaltEmissionsData.{mpoly, mprot, mlip} - // DustEmissionsData.dust_dmt_vwr (done here for now) - // OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, - // v_bottom, z_bottom, ocean_frac} - // NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, - // which is a chunk-wise variable at this point - // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom - // col-value once inside aero_model_emissions() - - view_1d fluxes_col = Kokkos::subview(online_emis_data.constituent_fluxes, - icol, Kokkos::ALL()); - mam4::aero_model_emissions::aero_model_emissions(fluxes_col); - }); - - // for (onlineEmissData &ispec_online : online_emis_data) { - // //-------------------------------------------------------------------- - // // Modify units to MKS units (from molecules/cm2/s to kg/m2/s) - // //-------------------------------------------------------------------- - // // Get species index in array with pcnst dimension - // // (e.g., state_q or constituent_fluxes_) - // const int species_index = - // spcIndex_in_pcnst_.at(ispec_online.species_name); - - // // 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_]; - // // Parallel loop over all the columns to update units - // Kokkos::parallel_for( - // "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - // fluxes_in_mks_units(icol) = - // ispec_online.data_out_.emiss_sectors(0, icol) * mfactor; - // constituent_fluxes(icol, species_index) = - // fluxes_in_mks_units(icol); - // }); - - // } // for loop for species - //-------------------------------------------------------------------- // Interpolate srf emiss data //-------------------------------------------------------------------- @@ -314,13 +270,35 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { amufac * mam4::gas_chemistry::adv_mass[species_index - offset_]; // Parallel loop over all the columns to update units Kokkos::parallel_for( - "fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + "srf_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { fluxes_in_mks_units(icol) = ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor; constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); } // for loop for species + + // TODO: check that units are consistent with srf emissions! + // copy current values to online-emissions-local version + Kokkos::deep_copy(online_emis_data.cfluxes, constituent_fluxes_); + // TODO: potentially combine with above parfor(icol) loop? + Kokkos::parallel_for( + "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + // need to initialize values for: + // SeasaltEmissionsData.{mpoly, mprot, mlip} + // DustEmissionsData.dust_dmt_vwr + // OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, + // v_bottom, z_bottom, ocean_frac} + // NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, + // which is a chunk-wise variable at this point + // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom + // col-value once inside aero_model_emissions() + + view_1d fluxes_col = Kokkos::subview( + online_emissions.online_emis_data.cfluxes, icol, Kokkos::ALL()); + mam4::aero_model_emissions::aero_model_emissions(fluxes_col); + }); + Kokkos::deep_copy(constituent_fluxes_, online_emis_data.cfluxes); Kokkos::fence(); } // run_impl ends 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 65ac4f6375e..8bfee35f2d9 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 @@ -127,7 +127,7 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // A vector for carrying emissions for all the species std::vector srf_emiss_species_; - onlineEmiss::onlineEmissData online_emis_data; + onlineEmiss online_emissions; // offset for converting pcnst index to gas_pcnst index static constexpr int offset_ = diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp index bc5a510ccf7..9841c874f85 100644 --- a/components/eamxx/src/physics/mam/online_emission.hpp +++ b/components/eamxx/src/physics/mam/online_emission.hpp @@ -9,6 +9,7 @@ template struct onlineEmissions { using KT = KokkosTypes; using MemberType = typename KT::MemberType; + static constexpr int pcnst = mam4::aero_model::pcnst; struct onlineEmissTimeState { onlineEmissTimeState() = default; @@ -30,14 +31,14 @@ template struct onlineEmissions { int ncols; view_2d flux_data; // local copy of main fluxes array - view_2d constituent_fluxes; + view_2d cfluxes; // FIXME: read this from input or get from mam4xx::aero_model_emissions? - const std::vector spec_names = {"so2", "soag", "bc_a4", - "num_a1", "num_a2", "num_a4", - "pom_a4", "so4_a1", "so4_a2"}; + const std::vector spec_names = { + "ncl_a1", "ncl_a2", "ncl_a3", "mom_a1", "mom_a2", "mom_a4", + "num_a1", "num_a2", "num_a3", "num_a4", "dst_a1", "dst_a3"}; // FIXME: change this when the above is dynamically-determined - int nspec = 9; - const std::string root_IC_str = "initial_condition_"; + int nspec = spec_names.size(); + const std::string root_IC_str = "online_emis_IC_"; onlineEmissData() = default; onlineEmissData(const int ncol_, const int nspec_) @@ -52,22 +53,29 @@ template struct onlineEmissions { void init(const int ncol_, const int nspec_, const bool allocate_) { ncols = ncol_; nspec = nspec_; - if (allocate_) + if (allocate_) { flux_data = view_2d("onlineEmissData", nspec, ncols); + cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); + } } // onlineEmissData init void init(const int ncol_, const bool allocate_) { ncols = ncol_; - if (allocate_) + if (allocate_) { flux_data = view_2d("onlineEmissData", nspec, ncols); + cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); + } } // onlineEmissData init void init(const int ncol_) { ncols = ncol_; flux_data = view_2d("onlineEmissData", nspec, ncols); + cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); } // onlineEmissData init }; // onlineEmissData + onlineEmissData online_emis_data; + // The output is really just onlineEmissData, but for clarity it might - // help to see a onlineEmissOutput along a onlineEmissInput in functions + // help to see a onlineEmissOutput along with onlineEmissInput in functions // signatures // using onlineEmissOutput = onlineEmissData; @@ -80,12 +88,16 @@ template struct onlineEmissions { // const onlineEmissInput &data_end, // const onlineEmissOutput &data_out); - static void init_from_input_file(const ekat::ParameterList &m_params, - onlineEmissData &data); - static void - transfer_to_cflux(const onlineEmissData &data, - const std::map idx_map, - view_2d &fluxes); + void init_from_input_file(const ekat::ParameterList &m_params); + void transfer_to_cflux(const onlineEmissData &data, + const std::map idx_map, + view_2d &fluxes); + // static void update_onlineEmiss_timestate( + // std::shared_ptr &scorpio_reader, + // const util::TimeStamp &ts, AbstractRemapper &onlineEmiss_horiz_interp, + // onlineEmissTimeState &time_state, onlineEmissInput &onlineEmiss_beg, + // onlineEmissInput &onlineEmiss_end); + }; // struct onlineEmissions } // namespace scream::mam_coupling #endif // ONLINE_EMISSION_HPP diff --git a/components/eamxx/src/physics/mam/online_emission_impl.hpp b/components/eamxx/src/physics/mam/online_emission_impl.hpp index 860a4cac2be..ec415c036a3 100644 --- a/components/eamxx/src/physics/mam/online_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/online_emission_impl.hpp @@ -3,11 +3,9 @@ namespace scream::mam_coupling { template -void onlineEmissions::init_from_input_file( - const ekat::ParameterList ¶ms, onlineEmissData &data) { - - const int nspec = data.nspec; - const int ncols = data.ncols; +void onlineEmissions::init_from_input_file(const ekat::ParameterList ¶ms) { + const int nspec = online_emis_data.nspec; + const int ncols = online_emis_data.ncols; using ExeSpace = typename KT::ExeSpace; using ESU = ekat::ExeSpaceUtils; const auto policy = ESU::get_default_team_policy(ncols, nspec); @@ -18,12 +16,12 @@ void onlineEmissions::init_from_input_file( // ncols-sized array for (int ispec = 0; ispec < nspec; ++ispec) { Real init_cond_val = - params.get(data.root_IC_str + data.spec_names[ispec]); + params.get(online_emis_data.root_IC_str + online_emis_data.spec_names[ispec]); // FIXME: is this overkill--i.e., would a mirror/deep_copy make more sense? Kokkos::parallel_for( policy, KOKKOS_LAMBDA(const MemberType &team) { const int jcol = team.league_rank(); // column index - data.flux_data(ispec, jcol) = init_cond_val; + online_emis_data.flux_data(ispec, jcol) = init_cond_val; }); } stop_timer("EAMxx::onlineEmiss::init_onlineEmiss_data_from_input_file"); @@ -47,9 +45,14 @@ void onlineEmissions::transfer_to_cflux( Kokkos::parallel_for( Kokkos::TeamThreadRange(team, nspec), [&](const int ispec) { auto s_idx = idx_map.at(data.spec_names[ispec]); - fluxes(jcol, s_idx) = data.flux_data(ispec, jcol); + data.cfluxes(jcol, s_idx) = data.flux_data(ispec, jcol); + std::cout << "=========================================" << "\n"; + std::cout << "data.cfluxes(jcol, s_idx) = " << data.cfluxes(jcol, s_idx) << "\n"; + std::cout << "=========================================" << "\n"; }); }); + + Kokkos::deep_copy(fluxes, data.cfluxes); } } // namespace scream::mam_coupling diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 93f187e7de3..589f08fba47 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -23,15 +23,18 @@ 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 # FIXME: just set everything to a constant scalar for testing - initial_condition_so2: 1.0 - initial_condition_soag: 2.0 - initial_condition_bc_a4: 3.0 - initial_condition_num_a1: 4.0 - initial_condition_num_a2: 5.0 - initial_condition_num_a4: 6.0 - initial_condition_pom_a4: 7.0 - initial_condition_so4_a1: 8.0 - initial_condition_so4_a2: 9.0 + online_emis_IC_ncl_a1: 1.0 + online_emis_IC_ncl_a2: 2.0 + online_emis_IC_ncl_a3: 3.0 + online_emis_IC_mom_a1: 4.0 + online_emis_IC_mom_a2: 5.0 + online_emis_IC_mom_a4: 6.0 + online_emis_IC_num_a1: 7.0 + online_emis_IC_num_a2: 8.0 + online_emis_IC_num_a3: 9.0 + online_emis_IC_num_a4: 10.0 + online_emis_IC_dst_a1: 11.0 + online_emis_IC_dst_a3: 12.0 grids_manager: Type: Mesh Free From 484d2c17d009dbb6eb96f1a3c0a538d7fac47930 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Wed, 16 Oct 2024 16:42:59 -0600 Subject: [PATCH 04/41] online emissions interface appears to be working --- ...and_online_emissions_process_interface.cpp | 28 +++++++------ ...and_online_emissions_process_interface.hpp | 4 +- .../eamxx/src/physics/mam/online_emission.hpp | 39 ++----------------- .../src/physics/mam/online_emission_impl.hpp | 17 +++----- .../single-process/mam/emissions/input.yaml | 38 ++++++++++++------ externals/mam4xx | 2 +- 6 files changed, 54 insertions(+), 74 deletions(-) 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 b50da0d4445..1db0535cce8 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 @@ -33,9 +33,13 @@ void MAMSrfOnlineEmiss::set_grids( // ------------------------------------------------------------- // These variables are "Computed" or outputs for the process - // FIXME: check into whether this should be an "Updated" field because - // I haven't yet determined if an updated field needs to be output - // as a tendency, and thus the flux would need dt [s] factored out + // FIXME: this should likely be an updated field, since online emissisons + // expects input values for constituent_fluxes + // NOTE: the other option is that we do something like: + // add_field("constituent_fluxes_input", scalar2d_pcnct, + // kg / m2 / s, grid_name); + // and then bundle the online emissions computations as tendencies into the + // Computed constituent_fluxes field // ------------------------------------------------------------- static constexpr Units m2(m * m, "m2"); // Constituent fluxes of species in [kg/m2/s] @@ -275,16 +279,19 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor; constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); - } // for loop for species + auto &online_data = online_emissions.online_emis_data; // TODO: check that units are consistent with srf emissions! // copy current values to online-emissions-local version - Kokkos::deep_copy(online_emis_data.cfluxes, constituent_fluxes_); + Kokkos::deep_copy(online_data.cfluxes, constituent_fluxes_); // TODO: potentially combine with above parfor(icol) loop? Kokkos::parallel_for( "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - // need to initialize values for: + // NOTE: calling aero_model_emissions() this way hides the fact that + // some hard-coded data is initialized within mam4::aero_model_emissions + // some of these values definitely need to be read from here column-wise + // the structs.{values} holding the above are: // SeasaltEmissionsData.{mpoly, mprot, mlip} // DustEmissionsData.dust_dmt_vwr // OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, @@ -293,14 +300,13 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // which is a chunk-wise variable at this point // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom // col-value once inside aero_model_emissions() - - view_1d fluxes_col = Kokkos::subview( - online_emissions.online_emis_data.cfluxes, icol, Kokkos::ALL()); + view_1d fluxes_col = Kokkos::subview(online_data.cfluxes, icol, Kokkos::ALL()); mam4::aero_model_emissions::aero_model_emissions(fluxes_col); }); - Kokkos::deep_copy(constituent_fluxes_, online_emis_data.cfluxes); + // 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 + Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); Kokkos::fence(); } // 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 8bfee35f2d9..80c2efcccd7 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 @@ -13,7 +13,6 @@ // class #include -// #include #include namespace scream { @@ -45,8 +44,7 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { public: using srfEmissFunc = mam_coupling::srfEmissFunctions; - using onlineEmiss = - mam_coupling::onlineEmissions; + using onlineEmiss = mam_coupling::onlineEmissions; // Constructor MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms); diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp index 9841c874f85..b844f5b18e6 100644 --- a/components/eamxx/src/physics/mam/online_emission.hpp +++ b/components/eamxx/src/physics/mam/online_emission.hpp @@ -6,37 +6,21 @@ namespace scream::mam_coupling { template struct onlineEmissions { using Device = DeviceType; - using KT = KokkosTypes; using MemberType = typename KT::MemberType; static constexpr int pcnst = mam4::aero_model::pcnst; - struct onlineEmissTimeState { - onlineEmissTimeState() = 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; - }; // onlineEmissTimeState - struct onlineEmissData { // Basic spatial dimensions of the data int ncols; view_2d flux_data; // local copy of main fluxes array view_2d cfluxes; - // FIXME: read this from input or get from mam4xx::aero_model_emissions? + // FIXME: read this from elsewhere? input? const std::vector spec_names = { "ncl_a1", "ncl_a2", "ncl_a3", "mom_a1", "mom_a2", "mom_a4", "num_a1", "num_a2", "num_a3", "num_a4", "dst_a1", "dst_a3"}; - // FIXME: change this when the above is dynamically-determined + // FIXME: change this when/if the above is dynamically-determined int nspec = spec_names.size(); const std::string root_IC_str = "online_emis_IC_"; @@ -50,6 +34,8 @@ template struct onlineEmissions { init(ncols, nspec, true); } + // overloads of init() in case npsec is not hard-coded, or if you want to + // control allocation via bool flag void init(const int ncol_, const int nspec_, const bool allocate_) { ncols = ncol_; nspec = nspec_; @@ -74,30 +60,13 @@ template struct onlineEmissions { onlineEmissData online_emis_data; - // The output is really just onlineEmissData, but for clarity it might - // help to see a onlineEmissOutput along with onlineEmissInput in functions - // signatures - // using onlineEmissOutput = onlineEmissData; - // --------------------------------------------------------------------------- // Online emissions routines // --------------------------------------------------------------------------- - // FIXME: - // static void onlineEmiss_main(const onlineEmissTimeState &time_state, - // const onlineEmissInput &data_beg, - // const onlineEmissInput &data_end, - // const onlineEmissOutput &data_out); - void init_from_input_file(const ekat::ParameterList &m_params); void transfer_to_cflux(const onlineEmissData &data, const std::map idx_map, view_2d &fluxes); - // static void update_onlineEmiss_timestate( - // std::shared_ptr &scorpio_reader, - // const util::TimeStamp &ts, AbstractRemapper &onlineEmiss_horiz_interp, - // onlineEmissTimeState &time_state, onlineEmissInput &onlineEmiss_beg, - // onlineEmissInput &onlineEmiss_end); - }; // struct onlineEmissions } // namespace scream::mam_coupling #endif // ONLINE_EMISSION_HPP diff --git a/components/eamxx/src/physics/mam/online_emission_impl.hpp b/components/eamxx/src/physics/mam/online_emission_impl.hpp index ec415c036a3..1f0ac91a854 100644 --- a/components/eamxx/src/physics/mam/online_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/online_emission_impl.hpp @@ -4,35 +4,32 @@ namespace scream::mam_coupling { template void onlineEmissions::init_from_input_file(const ekat::ParameterList ¶ms) { + // FIXME: move this out to the onlineEmissions struct to avoid extra work + // by doing it again below? const int nspec = online_emis_data.nspec; const int ncols = online_emis_data.ncols; using ExeSpace = typename KT::ExeSpace; using ESU = ekat::ExeSpaceUtils; const auto policy = ESU::get_default_team_policy(ncols, nspec); - - start_timer("EAMxx::onlineEmiss::init_onlineEmiss_data_from_input_file"); - // 1. Read from input file + // Read from input file // FIXME: currently reading a single placeholder scalar--should be - // ncols-sized array + // ncols-sized array when we know what the input data looks like for (int ispec = 0; ispec < nspec; ++ispec) { Real init_cond_val = params.get(online_emis_data.root_IC_str + online_emis_data.spec_names[ispec]); - // FIXME: is this overkill--i.e., would a mirror/deep_copy make more sense? + // TODO: is this overkill?--i.e., would a mirror/deep_copy make more sense? Kokkos::parallel_for( policy, KOKKOS_LAMBDA(const MemberType &team) { const int jcol = team.league_rank(); // column index online_emis_data.flux_data(ispec, jcol) = init_cond_val; }); } - stop_timer("EAMxx::onlineEmiss::init_onlineEmiss_data_from_input_file"); - } // end init_from_input_file() template void onlineEmissions::transfer_to_cflux( const onlineEmissData &data, const std::map idx_map, view_2d &fluxes) { - // FIXME: move out to onlineEmissions struct? const int nspec = data.nspec; const int ncols = data.ncols; using ExeSpace = typename KT::ExeSpace; @@ -46,12 +43,8 @@ void onlineEmissions::transfer_to_cflux( Kokkos::TeamThreadRange(team, nspec), [&](const int ispec) { auto s_idx = idx_map.at(data.spec_names[ispec]); data.cfluxes(jcol, s_idx) = data.flux_data(ispec, jcol); - std::cout << "=========================================" << "\n"; - std::cout << "data.cfluxes(jcol, s_idx) = " << data.cfluxes(jcol, s_idx) << "\n"; - std::cout << "=========================================" << "\n"; }); }); - Kokkos::deep_copy(fluxes, data.cfluxes); } } // namespace scream::mam_coupling diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 589f08fba47..7a3dfb43f22 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -23,18 +23,32 @@ 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 # FIXME: just set everything to a constant scalar for testing - online_emis_IC_ncl_a1: 1.0 - online_emis_IC_ncl_a2: 2.0 - online_emis_IC_ncl_a3: 3.0 - online_emis_IC_mom_a1: 4.0 - online_emis_IC_mom_a2: 5.0 - online_emis_IC_mom_a4: 6.0 - online_emis_IC_num_a1: 7.0 - online_emis_IC_num_a2: 8.0 - online_emis_IC_num_a3: 9.0 - online_emis_IC_num_a4: 10.0 - online_emis_IC_dst_a1: 11.0 - online_emis_IC_dst_a3: 12.0 + online_emis_IC_ncl_a1: 0.0 + online_emis_IC_ncl_a2: 0.0 + online_emis_IC_ncl_a3: 0.0 + online_emis_IC_mom_a1: 0.16838301005552275E-013 + online_emis_IC_mom_a2: 0.26554873160224250E-016 + online_emis_IC_mom_a4: 0.0 + online_emis_IC_num_a1: 0.0 + online_emis_IC_num_a2: 0.0 + online_emis_IC_num_a3: 0.0 + online_emis_IC_num_a4: 0.0 + online_emis_IC_dst_a1: 0.0 + online_emis_IC_dst_a3: 0.18720550166902007E+003 + +# NOTE: these are the single-call results from the mam validation test +# (20) ncl_a1: 2.659900637e-13 +# (25) ncl_a2: 0 +# (29) ncl_a3: 2.391492482e-11 +# (21) mom_a1: 1.683830101e-14 +# (26) mom_a2: 2.655487316e-17 +# (38) mom_a4: 0 +# (22) num_a1: 2.594942753e-15 +# (27) num_a2: 4.092359179e-18 +# (35) num_a3: 0 +# (39) num_a4: 0 +# (19) dst_a1: 0 +# (28) dst_a3: 403.2611563 grids_manager: Type: Mesh Free diff --git a/externals/mam4xx b/externals/mam4xx index 4431bbd1eef..c537bab2695 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 4431bbd1eef46de25be3a04e7091c9255bd0b819 +Subproject commit c537bab2695af76290cc0b82462393761b5820ed From 829c560df200b3c7dcd7ef012e557948be909f6f Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Wed, 16 Oct 2024 17:34:24 -0600 Subject: [PATCH 05/41] bump mam4xx submodule, add comments to srf_online_emis_interface --- ..._mam_srf_and_online_emissions_process_interface.cpp | 10 ++++++++++ externals/mam4xx | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) 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 1db0535cce8..c84bb803440 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 @@ -30,6 +30,16 @@ void MAMSrfOnlineEmiss::set_grids( static constexpr int pcnst = mam4::aero_model::pcnst; const FieldLayout scalar2d_pcnct = grid_->get_2d_vector_layout(pcnst, "num_phys_constituents"); + // auto vector3d_mid = grid_->get_3d_vector_layout(true, 2); + + // FIXME: online emissions requires the following quantities for the + // OnlineEmissionsData struct: {surface_temp, u_bottom, v_bottom, z_bottom, + // ocean_frac} + // // Temperature + // add_field("T_mid", scalar3d_layout_mid, K, grid_name); + // add_field("horiz_winds", vector3d_mid, m/s, grid_name); + // vertical wind? + // ocean_frac? // ------------------------------------------------------------- // These variables are "Computed" or outputs for the process diff --git a/externals/mam4xx b/externals/mam4xx index c537bab2695..67c309117ab 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit c537bab2695af76290cc0b82462393761b5820ed +Subproject commit 67c309117abe0be649d085c7aeaeb1e949f6b550 From b2dbb85c4b7bee72cd88b26f16bbbed936479aaa Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Wed, 16 Oct 2024 20:43:48 -0600 Subject: [PATCH 06/41] update mam4xx to main --- externals/mam4xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/mam4xx b/externals/mam4xx index 67c309117ab..6bc2697f84b 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 67c309117abe0be649d085c7aeaeb1e949f6b550 +Subproject commit 6bc2697f84b38503cc8fe638a6281ea107180e40 From 24d3e11c04506a572e04de5718d048e531754401 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Mon, 21 Oct 2024 13:00:52 -0600 Subject: [PATCH 07/41] comment out error-inducing code in mam4_amicphys.cpp and small reorg change in online_emission.hpp --- ...mxx_mam_microphysics_process_interface.cpp | 11 + .../src/physics/mam/impl/mam4_amicphys.cpp | 1819 +++++++++++++++++ .../eamxx/src/physics/mam/online_emission.hpp | 26 +- .../src/physics/mam/online_emission_impl.hpp | 52 - 4 files changed, 1850 insertions(+), 58 deletions(-) create mode 100644 components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp delete mode 100644 components/eamxx/src/physics/mam/online_emission_impl.hpp diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 57eaf927548..41713b97735 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -70,9 +70,20 @@ void MAMMicrophysics::set_grids( const FieldLayout scalar3d_mid = grid_->get_3d_scalar_layout(true); const FieldLayout scalar3d_int = grid_->get_3d_scalar_layout(false); +<<<<<<< HEAD using namespace ekat::units; 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 +======= + config_.amicphys.nucleation = {}; + // config_.amicphys.nucleation.dens_so4a_host = 1770.0; + // config_.amicphys.nucleation.mw_so4a_host = 115.0; + // config_.amicphys.nucleation.newnuc_method_user_choice = 2; + // config_.amicphys.nucleation.pbl_nuc_wang2008_user_choice = 1; + // config_.amicphys.nucleation.adjust_factor_pbl_ratenucl = 1.0; + // config_.amicphys.nucleation.accom_coef_h2so4 = 1.0; + config_.amicphys.nucleation.newnuc_adjust_factor_dnaitdt = 1.0; +>>>>>>> comment out error-inducing code in mam4_amicphys.cpp and small reorg change in online_emission.hpp // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process diff --git a/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp b/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp new file mode 100644 index 00000000000..e241b5ec428 --- /dev/null +++ b/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp @@ -0,0 +1,1819 @@ +#include +#include +#include +#include +#include +#include + +namespace scream::impl { + +#define MAX_FILENAME_LEN 256 + +using namespace mam4; + +// number of constituents in gas chemistry "work arrays" +KOKKOS_INLINE_FUNCTION +constexpr int gas_pcnst() { + constexpr int gas_pcnst_ = mam4::gas_chemistry::gas_pcnst; + return gas_pcnst_; +} + +// number of aerosol/gas species tendencies +KOKKOS_INLINE_FUNCTION +constexpr int nqtendbb() { return 4; } + +// MAM4 aerosol microphysics configuration data +struct AmicPhysConfig { + // these switches activate various aerosol microphysics processes + bool do_cond; // condensation (a.k.a gas-aerosol exchange) + bool do_rename; // mode "renaming" + bool do_newnuc; // gas -> aerosol nucleation + bool do_coag; // aerosol coagulation + + // configurations for specific aerosol microphysics + mam4::GasAerExchProcess::ProcessConfig condensation; + mam4::NucleationProcess::ProcessConfig nucleation; + + // controls treatment of h2so4 condensation in mam_gasaerexch_1subarea + // 1 = sequential calc. of gas-chem prod then condensation loss + // 2 = simultaneous calc. of gas-chem prod and condensation loss + int gaexch_h2so4_uptake_optaa; + + // controls how nucleation interprets h2so4 concentrations + int newnuc_h2so4_conc_optaa; +}; + +namespace { + +KOKKOS_INLINE_FUNCTION constexpr int nqtendaa() { return 5; } +KOKKOS_INLINE_FUNCTION constexpr int nqqcwtendaa() { return 1; } +KOKKOS_INLINE_FUNCTION constexpr int nqqcwtendbb() { return 1; } +KOKKOS_INLINE_FUNCTION constexpr int iqtend_cond() { return 0; } +KOKKOS_INLINE_FUNCTION constexpr int iqtend_rnam() { return 1; } +KOKKOS_INLINE_FUNCTION constexpr int iqtend_nnuc() { return 2; } +KOKKOS_INLINE_FUNCTION constexpr int iqtend_coag() { return 3; } +KOKKOS_INLINE_FUNCTION constexpr int iqtend_cond_only() { return 4; } +KOKKOS_INLINE_FUNCTION constexpr int iqqcwtend_rnam() { return 0; } +KOKKOS_INLINE_FUNCTION constexpr int maxsubarea() { return 2; } + +// conversion factors +KOKKOS_INLINE_FUNCTION Real fcvt_gas(int gas_id) { + static const Real fcvt_gas_[AeroConfig::num_gas_ids()] = {1, 1, 1}; + return fcvt_gas_[gas_id]; +} +KOKKOS_INLINE_FUNCTION Real fcvt_aer(int aero_id) { + static const Real fcvt_aer_[AeroConfig::num_aerosol_ids()] = {1, 1, 1, 1, 1, 1, 1}; + return fcvt_aer_[aero_id]; +} + +// leave number mix-ratios unchanged (#/kmol-air) +KOKKOS_INLINE_FUNCTION Real fcvt_num() { return 1.0; } +// factor for converting aerosol water mix-ratios from (kg/kg) to (mol/mol) +KOKKOS_INLINE_FUNCTION Real fcvt_wtr() { return 1.0; } + +KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_nul() { return 0; } +KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_gas() { return 1; } +KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_aer() { return 2; } +KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_num() { return 3; } +KOKKOS_INLINE_FUNCTION int lmapcc_all(int index) { + static const int lmapcc_all_[gas_pcnst()] = { + lmapcc_val_nul(), lmapcc_val_gas(), lmapcc_val_nul(), lmapcc_val_nul(), + lmapcc_val_gas(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_aer(), lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_aer(), lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), + lmapcc_val_aer(), lmapcc_val_num()}; + return lmapcc_all_[index]; +} + +// Where lmapcc_val_num are defined in lmapcc_all +KOKKOS_INLINE_FUNCTION int numptr_amode(int mode) { + static const int numptr_amode_[AeroConfig::num_modes()] = {12, 17, 25, 29}; + return numptr_amode_[mode]; +} + +// Where lmapcc_val_gas are defined in lmapcc_all +KOKKOS_INLINE_FUNCTION int lmap_gas(int mode) { + static const int lmap_gas_[AeroConfig::num_modes()] = {4, 1}; + return lmap_gas_[mode]; +} +// Where lmapcc_val_aer are defined in lmapcc_all +KOKKOS_INLINE_FUNCTION int lmassptr_amode(int aero_id, int mode) { + static const int lmassptr_amode_[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] = { + {5, 13, 18, 26}, {6, 14, 19, 27}, {7, 15, 20, 28}, {8, 16, 21, -6}, + {9, -6, 22, -6}, {10, -6, 23, -6}, {11, -6, 24, -6}}; + return lmassptr_amode_[aero_id][mode]; +} + +KOKKOS_INLINE_FUNCTION +void subarea_partition_factors( + const Real + q_int_cell_avg, // in grid cell mean interstitial aerosol mixing ratio + const Real + q_cbn_cell_avg, // in grid cell mean cloud-borne aerosol mixing ratio + const Real fcldy, // in cloudy fraction of the grid cell + const Real fclea, // in clear fraction of the grid cell + Real &part_fac_q_int_clea, // out + Real &part_fac_q_int_cldy) // out +{ + // Calculate mixing ratios of each subarea + + // cloud-borne, cloudy subarea + const Real tmp_q_cbn_cldy = q_cbn_cell_avg / fcldy; + // interstitial, cloudy subarea + const Real tmp_q_int_cldy = + haero::max(0.0, ((q_int_cell_avg + q_cbn_cell_avg) - tmp_q_cbn_cldy)); + // interstitial, clear subarea + const Real tmp_q_int_clea = (q_int_cell_avg - fcldy * tmp_q_int_cldy) / fclea; + + // Calculate the corresponding paritioning factors for interstitial aerosols + // using the above-derived subarea mixing ratios plus the constraint that + // the cloud fraction weighted average of subarea mean need to match grid box + // mean. + + // *** question *** + // use same part_fac_q_int_clea/cldy for everything ? + // use one for number and one for all masses (based on total mass) ? + // use separate ones for everything ? + // maybe one for number and one for all masses is best, + // because number and mass have different activation fractions + // *** question *** + + Real tmp_aa = haero::max(1.e-35, tmp_q_int_clea * fclea) / + haero::max(1.e-35, q_int_cell_avg); + tmp_aa = haero::max(0.0, haero::min(1.0, tmp_aa)); + + part_fac_q_int_clea = tmp_aa / fclea; + part_fac_q_int_cldy = (1.0 - tmp_aa) / fcldy; +} + +KOKKOS_INLINE_FUNCTION +void construct_subareas_1gridcell( + const Real cld, // in + const Real relhumgcm, // in + const Real q_pregaschem[gas_pcnst()], // in q TMRs before + // gas-phase chemistry + const Real q_precldchem[gas_pcnst()], // in q TMRs before + // cloud chemistry + const Real qqcw_precldchem[gas_pcnst()], // in qqcw TMRs before + // cloud chemistry + const Real q[gas_pcnst()], // in current tracer mixing ratios (TMRs) + // *** MUST BE #/kmol-air for number + // *** MUST BE mol/mol-air for mass + const Real qqcw[gas_pcnst()], // in like q but for + // cloud-borner tracers + int &nsubarea, // out + int &ncldy_subarea, // out + int &jclea, // out + int &jcldy, // out + bool iscldy_subarea[maxsubarea()], // out + Real afracsub[maxsubarea()], // out + Real relhumsub[maxsubarea()], // out + Real qsub1[gas_pcnst()][maxsubarea()], // out interstitial + Real qsub2[gas_pcnst()][maxsubarea()], // out interstitial + Real qsub3[gas_pcnst()][maxsubarea()], // out interstitial + Real qqcwsub1[gas_pcnst()][maxsubarea()], // out cloud-borne + Real qqcwsub2[gas_pcnst()][maxsubarea()], // out cloud-borne + Real qqcwsub3[gas_pcnst()][maxsubarea()], // outcloud-borne + Real qaerwatsub3[AeroConfig::num_modes()] + [maxsubarea()], // out aerosol water mixing ratios (mol/mol) + Real qaerwat[AeroConfig::num_modes()] // in aerosol water mixing ratio + // (kg/kg, NOT mol/mol) +) { + static constexpr int num_modes = AeroConfig::num_modes(); + // cloud chemistry is only on when cld(i,k) >= 1.0e-5_wp + // it may be that the macrophysics has a higher threshold that this + const Real fcld_locutoff = 1.0e-5; + const Real fcld_hicutoff = 0.999; + + // qgcmN and qqcwgcmN (N=1:4) are grid-cell mean tracer mixing ratios (TMRs, + // mol/mol or #/kmol) + // N=1 - before gas-phase chemistry + // N=2 - before cloud chemistry + // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) + // qgcm1, qgcm2, qgcm3 + // qqcwgcm2, qqcwgcm3 + // qaerwatgcm3 ! aerosol water mixing ratios (mol/mol) + + // -------------------------------------------------------------------------------------- + // Determine the number of sub-areas, their fractional areas, and relative + // humidities + // -------------------------------------------------------------------------------------- + // if cloud fraction ~= 0, the grid-cell has a single clear sub-area + // (nsubarea = 1) if cloud fraction ~= 1, the grid-cell has a single cloudy + // sub-area (nsubarea = 1) otherwise, the grid-cell has a + // clear and a cloudy sub-area (nsubarea = 2) + + Real zfcldy = 0; + nsubarea = 0; + ncldy_subarea = 0; + jclea = 0; + jcldy = 0; + + if (cld < fcld_locutoff) { + nsubarea = 1; + jclea = 1; + } else if (cld > fcld_hicutoff) { + zfcldy = 1.0; + nsubarea = 1; + ncldy_subarea = 1; + jcldy = 1; + } else { + zfcldy = cld; + nsubarea = 2; + ncldy_subarea = 1; + jclea = 1; + jcldy = 2; + } + + const Real zfclea = 1.0 - zfcldy; + for (int i = 0; i < maxsubarea(); ++i) + iscldy_subarea[i] = false; + if (jcldy > 0) + iscldy_subarea[jcldy - 1] = true; + for (int i = 0; i < maxsubarea(); ++i) + afracsub[i] = 0.0; + if (jclea > 0) + afracsub[jclea - 1] = zfclea; + if (jcldy > 0) + afracsub[jcldy - 1] = zfcldy; + + // cldy_rh_sameas_clear is just to match mam_refactor. Compiler should + // optimize away. + const int cldy_rh_sameas_clear = 0; + if (ncldy_subarea <= 0) { + for (int i = 0; i < maxsubarea(); ++i) + relhumsub[i] = relhumgcm; + } else if (cldy_rh_sameas_clear > 0) { + for (int i = 0; i < maxsubarea(); ++i) + relhumsub[i] = relhumgcm; + } else { + if (jcldy > 0) { + relhumsub[jcldy - 1] = 1.0; + if (jclea > 0) { + const Real tmpa = + (relhumgcm - afracsub[jcldy - 1]) / afracsub[jclea - 1]; + relhumsub[jclea - 1] = haero::max(0.0, haero::min(1.0, tmpa)); + } + } + } + + // ---------------------------------------------------------------------------- + // Copy grid cell mean mixing ratios. + // These values, together with cloud fraction and a few assumptions, are used + // in the remainder of the subroutine to calculate the sub-area mean mixing + // ratios. + // ---------------------------------------------------------------------------- + // Interstitial aerosols + Real qgcm1[gas_pcnst()], qgcm2[gas_pcnst()], qgcm3[gas_pcnst()]; + for (int i = 0; i < gas_pcnst(); ++i) { + qgcm1[i] = haero::max(0.0, q_pregaschem[i]); + qgcm2[i] = haero::max(0.0, q_precldchem[i]); + qgcm3[i] = haero::max(0.0, q[i]); + } + + // Cloud-borne aerosols + Real qqcwgcm2[gas_pcnst()], qqcwgcm3[gas_pcnst()]; + for (int i = 0; i < gas_pcnst(); ++i) { + qqcwgcm2[i] = haero::max(0.0, qqcw_precldchem[i]); + qqcwgcm3[i] = haero::max(0.0, qqcw[i]); + } + + // aerosol water + Real qaerwatgcm3[num_modes] = {}; + for (int i = 0; i < num_modes; ++i) { + qaerwatgcm3[i] = haero::max(0.0, qaerwat[i]); + } + + // ---------------------------------------------------------------------------- + // Initialize the subarea mean mixing ratios + // ---------------------------------------------------------------------------- + { + const int n = haero::min(maxsubarea(), nsubarea + 1); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < gas_pcnst(); ++j) { + qsub1[j][i] = 0.0; + qsub2[j][i] = 0.0; + qsub3[j][i] = 0.0; + qqcwsub1[j][i] = 0.0; + qqcwsub2[j][i] = 0.0; + qqcwsub3[j][i] = 0.0; + } + for (int j = 0; j < num_modes; ++j) { + qaerwatsub3[j][i] = 0.0; + } + } + } + + // ************************************************************************************************* + // Calculate initial (i.e., before cond/rnam/nnuc/coag) tracer mixing + // ratios within the sub-areas + // - for all-clear or all-cloudy cases, the sub-area TMRs are equal to the + // grid-cell means + // - for partly cloudy case, they are different. This is primarily + // because the + // interstitial aerosol mixing ratios are assumed lower in the cloudy + // sub-area than in the clear sub-area, because much of the aerosol is + // activated in the cloudy sub-area. + // ************************************************************************************************* + // Category I: partly cloudy case + // ************************************************************************************************* + if ((jclea > 0) && (jcldy > 0) && (jclea + jcldy == 3) && (nsubarea == 2)) { + + // --------------------------------------------------------------------- + // Set GAS mixing ratios in sub-areas (for the condensing gases only!!) + // --------------------------------------------------------------------- + for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { + if (lmapcc_all(lmz) == lmapcc_val_gas()) { + + // assume gas in both sub-areas before gas-chem and cloud-chem equal + // grid-cell mean + for (int i = 0; i < nsubarea; ++i) { + qsub1[lmz][i] = qgcm1[lmz]; + qsub2[lmz][i] = qgcm2[lmz]; + } + // assume gas in clear sub-area after cloud-chem equals before + // cloud-chem value + qsub3[lmz][jclea - 1] = qsub2[lmz][jclea - 1]; + // gas in cloud sub-area then determined by grid-cell mean and clear + // values + qsub3[lmz][jcldy - 1] = + (qgcm3[lmz] - zfclea * qsub3[lmz][jclea - 1]) / zfcldy; + + // check that this does not produce a negative value + if (qsub3[lmz][jcldy - 1] < 0.0) { + qsub3[lmz][jcldy - 1] = 0.0; + qsub3[lmz][jclea - 1] = qgcm3[lmz] / zfclea; + } + } + } + // --------------------------------------------------------------------- + // Set CLOUD-BORNE AEROSOL mixing ratios in sub-areas. + // This is straightforward, as the same partitioning factors (0 or 1/f) + // are applied to all mass and number mixing ratios in all modes. + // --------------------------------------------------------------------- + // loop thru log-normal modes + for (int n = 0; n < num_modes; ++n) { + // number - then mass of individual species - of a mode + for (int l2 = -1; l2 < num_species_mode(n); ++l2) { + int lc; + if (l2 == -1) + lc = numptr_amode(n); + else + lc = lmassptr_amode(l2, n); + qqcwsub2[lc][jclea - 1] = 0.0; + qqcwsub2[lc][jcldy - 1] = qqcwgcm2[lc] / zfcldy; + qqcwsub3[lc][jclea - 1] = 0.0; + qqcwsub3[lc][jcldy - 1] = qqcwgcm3[lc] / zfcldy; + } + } + + // --------------------------------------------------------------------- + // Set INTERSTITIAL AEROSOL mixing ratios in sub-areas. + // --------------------------------------------------------------------- + for (int n = 0; n < num_modes; ++n) { + // ------------------------------------- + // Aerosol number + // ------------------------------------- + // grid cell mean, interstitial + Real tmp_q_cellavg_int = qgcm2[numptr_amode(n)]; + // grid cell mean, cloud-borne + Real tmp_q_cellavg_cbn = qqcwgcm2[numptr_amode(n)]; + + Real nmbr_part_fac_clea = 0; + Real nmbr_part_fac_cldy = 0; + subarea_partition_factors(tmp_q_cellavg_int, tmp_q_cellavg_cbn, zfcldy, + zfclea, nmbr_part_fac_clea, nmbr_part_fac_cldy); + + // Apply the partitioning factors to calculate sub-area mean number + // mixing ratios + + const int la = numptr_amode(n); + + qsub2[la][jclea - 1] = qgcm2[la] * nmbr_part_fac_clea; + qsub2[la][jcldy - 1] = qgcm2[la] * nmbr_part_fac_cldy; + qsub3[la][jclea - 1] = qgcm3[la] * nmbr_part_fac_clea; + qsub3[la][jcldy - 1] = qgcm3[la] * nmbr_part_fac_cldy; + + //------------------------------------- + // Aerosol mass + //------------------------------------- + // For aerosol mass, we use the total grid cell mean + // interstitial/cloud-borne mass mixing ratios to come up with the same + // partitioning for all species in the mode. + + // Compute the total mixing ratios by summing up the individual species + + tmp_q_cellavg_int = 0.0; // grid cell mean, interstitial + tmp_q_cellavg_cbn = 0.0; // grid cell mean, cloud-borne + + for (int l2 = 0; l2 < num_species_mode(n); ++l2) { + tmp_q_cellavg_int += qgcm2[lmassptr_amode(l2, n)]; + tmp_q_cellavg_cbn += qqcwgcm2[lmassptr_amode(l2, n)]; + } + Real mass_part_fac_clea = 0; + Real mass_part_fac_cldy = 0; + // Calculate the partitioning factors + subarea_partition_factors(tmp_q_cellavg_int, tmp_q_cellavg_cbn, zfcldy, + zfclea, mass_part_fac_clea, mass_part_fac_cldy); + + // Apply the partitioning factors to calculate sub-area mean mass mixing + // ratios + + for (int l2 = 0; l2 < num_species_mode(n); ++l2) { + const int la = lmassptr_amode(l2, n); + + qsub2[la][jclea - 1] = qgcm2[la] * mass_part_fac_clea; + qsub2[la][jcldy - 1] = qgcm2[la] * mass_part_fac_cldy; + qsub3[la][jclea - 1] = qgcm3[la] * mass_part_fac_clea; + qsub3[la][jcldy - 1] = qgcm3[la] * mass_part_fac_cldy; + } + } + + // ************************************************************************************************* + // Category II: all clear, or cld < 1e-5 + // In this case, zfclea=1 and zfcldy=0 + // ************************************************************************************************* + } else if ((jclea == 1) && (jcldy == 0) && (nsubarea == 1)) { + // + // put all the gases and interstitial aerosols in the clear sub-area + // and set mix-ratios = 0 in cloudy sub-area + // for cloud-borne aerosol, do nothing + // because the grid-cell-mean cloud-borne aerosol will be left + // unchanged (i.e., this routine only changes qqcw when cld >= 1e-5) + // + + for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { + if (0 < lmapcc_all(lmz)) { + qsub1[lmz][jclea - 1] = qgcm1[lmz]; + qsub2[lmz][jclea - 1] = qgcm2[lmz]; + qsub3[lmz][jclea - 1] = qgcm3[lmz]; + qqcwsub2[lmz][jclea - 1] = qqcwgcm2[lmz]; + qqcwsub3[lmz][jclea - 1] = qqcwgcm3[lmz]; + } + } + // ************************************************************************************************* + // Category III: all cloudy, or cld > 0.999 + // in this case, zfcldy= and zfclea=0 + // ************************************************************************************************* + } else if ((jclea == 0) && (jcldy == 1) && (nsubarea == 1)) { + // + // put all the gases and interstitial aerosols in the cloudy sub-area + // and set mix-ratios = 0 in clear sub-area + // + for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { + if (0 < lmapcc_all(lmz)) { + qsub1[lmz][jcldy - 1] = qgcm1[lmz]; + qsub2[lmz][jcldy - 1] = qgcm2[lmz]; + qsub3[lmz][jcldy - 1] = qgcm3[lmz]; + qqcwsub2[lmz][jcldy - 1] = qqcwgcm2[lmz]; + qqcwsub3[lmz][jcldy - 1] = qqcwgcm3[lmz]; + } + } + // ************************************************************************************************* + } else { // this should not happen + EKAT_KERNEL_REQUIRE_MSG(false, "*** modal_aero_amicphys - bad jclea, jcldy, nsubarea!"); + } + // ************************************************************************************************* + + // ------------------------------------------------------------------------------------ + // aerosol water -- how to treat this in sub-areas needs more work/thinking + // currently modal_aero_water_uptake calculates qaerwat using + // the grid-cell mean interstital-aerosol mix-rats and the clear-area rh + for (int jsub = 0; jsub < nsubarea; ++jsub) + for (int i = 0; i < num_modes; ++i) + qaerwatsub3[i][jsub] = qaerwatgcm3[i]; + + // ------------------------------------------------------------------------------------ + if (nsubarea == 1) { + // the j=1 subarea is used for some diagnostics + // but is not used in actual calculations + const int j = 1; + for (int i = 0; i < gas_pcnst(); ++i) { + qsub1[i][j] = 0.0; + qsub2[i][j] = 0.0; + qsub3[i][j] = 0.0; + qqcwsub2[i][j] = 0.0; + qqcwsub3[i][j] = 0.0; + } + } +} + +KOKKOS_INLINE_FUNCTION +void mam_amicphys_1subarea_clear( + const AmicPhysConfig& config, const int nstep, const Real deltat, const int jsub, + const int nsubarea, const bool iscldy_subarea, const Real afracsub, + const Real temp, const Real pmid, const Real pdel, const Real zmid, + const Real pblh, const Real relhum, Real dgn_a[AeroConfig::num_modes()], + Real dgn_awet[AeroConfig::num_modes()], + Real wetdens[AeroConfig::num_modes()], + const Real qgas1[AeroConfig::num_gas_ids()], + const Real qgas3[AeroConfig::num_gas_ids()], + Real qgas4[AeroConfig::num_gas_ids()], + Real qgas_delaa[AeroConfig::num_gas_ids()][nqtendaa()], + const Real qnum3[AeroConfig::num_modes()], + Real qnum4[AeroConfig::num_modes()], + Real qnum_delaa[AeroConfig::num_modes()][nqtendaa()], + const Real qaer3[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], + Real qaer4[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], + Real qaer_delaa[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] + [nqtendaa()], + const Real qwtr3[AeroConfig::num_modes()], + Real qwtr4[AeroConfig::num_modes()]) { + static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); + static constexpr int num_modes = AeroConfig::num_modes(); + static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); + + static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); + // Turn off nh3 for now. This is a future enhancement. + static constexpr int igas_nh3 = -999888777; // Same as mam_refactor + static constexpr int iaer_so4 = static_cast(AeroId::SO4); + static constexpr int iaer_pom = static_cast(AeroId::POM); + + const AeroId gas_to_aer[num_gas_ids] = {AeroId::SOA, AeroId::SO4, + AeroId::None}; + + const bool l_gas_condense_to_mode[num_gas_ids][num_modes] = { + {true, true, true, true}, + {true, true, true, true}, + {false, false, false, false}}; + enum { NA, ANAL, IMPL }; + const int eqn_and_numerics_category[num_gas_ids] = {IMPL, ANAL, ANAL}; + + // air molar density (kmol/m3) + // const Real r_universal = Constants::r_gas; // [mJ/(K mol)] + const Real r_universal = 8.314467591; // [mJ/(mol)] as in mam_refactor + const Real aircon = pmid / (1000 * r_universal * temp); + const Real alnsg_aer[num_modes] = {0.58778666490211906, 0.47000362924573563, + 0.58778666490211906, 0.47000362924573563}; + const Real uptk_rate_factor[num_gas_ids] = {0.81, 1.0, 1.0}; + // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) + // qgas3, qaer3, qnum3 are the current incoming TMRs + // qgas4, qaer4, qnum4 are the updated outgoing TMRs + // + // this routine calculates changes involving + // gas-aerosol exchange (condensation/evaporation) + // growth from smaller to larger modes (renaming) due to condensation + // new particle nucleation + // coagulation + // transfer of particles from hydrophobic modes to hydrophilic modes + // (aging) + // due to condensation and coagulation + // + // qXXXN (X=gas,aer,wat,num; N=1:4) are sub-area mixing ratios + // XXX=gas - gas species + // XXX=aer - aerosol mass species (excluding water) + // XXX=wat - aerosol water + // XXX=num - aerosol number + // N=1 - before gas-phase chemistry + // N=2 - before cloud chemistry + // N=3 - current incoming values (before gas-aerosol exchange, newnuc, + // coag) N=4 - updated outgoing values (after gas-aerosol exchange, + // newnuc, coag) + // + // qXXX_delaa are TMR changes (not tendencies) + // for different processes, which are used to produce history output + // for a clear sub-area, the processes are condensation/evaporation (and + // associated aging), renaming, coagulation, and nucleation + + Real qgas_cur[num_gas_ids]; + for (int i = 0; i < num_gas_ids; ++i) + qgas_cur[i] = qgas3[i]; + Real qaer_cur[num_aerosol_ids][num_modes]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_cur[i][j] = qaer3[i][j]; + + Real qnum_cur[num_modes]; + for (int j = 0; j < num_modes; ++j) + qnum_cur[j] = qnum3[j]; + Real qwtr_cur[num_modes]; + for (int j = 0; j < num_modes; ++j) + qwtr_cur[j] = qwtr3[j]; + + // qgas_netprod_otrproc = gas net production rate from other processes + // such as gas-phase chemistry and emissions (mol/mol/s) + // this allows the condensation (gasaerexch) routine to apply production and + // condensation loss + // together, which is more accurate numerically + // NOTE - must be >= zero, as numerical method can fail when it is negative + // NOTE - currently only the values for h2so4 and nh3 should be non-zero + Real qgas_netprod_otrproc[num_gas_ids] = {}; + if (config.do_cond && config.gaexch_h2so4_uptake_optaa == 2) { + for (int igas = 0; igas < num_gas_ids; ++igas) { + if (igas == igas_h2so4 || igas == igas_nh3) { + // if config.gaexch_h2so4_uptake_optaa == 2, then + // if qgas increases from pre-gaschem to post-cldchem, + // start from the pre-gaschem mix-ratio and add in the production + // during the integration + // if it decreases, + // start from post-cldchem mix-ratio + // *** currently just do this for h2so4 and nh3 + qgas_netprod_otrproc[igas] = (qgas3[igas] - qgas1[igas]) / deltat; + if (qgas_netprod_otrproc[igas] >= 0.0) + qgas_cur[igas] = qgas1[igas]; + else + qgas_netprod_otrproc[igas] = 0.0; + } + } + } + Real qgas_del_cond[num_gas_ids] = {}; + Real qgas_del_nnuc[num_gas_ids] = {}; + Real qgas_del_cond_only[num_gas_ids] = {}; + Real qaer_del_cond[num_aerosol_ids][num_modes] = {}; + Real qaer_del_rnam[num_aerosol_ids][num_modes] = {}; + Real qaer_del_nnuc[num_aerosol_ids][num_modes] = {}; + Real qaer_del_coag[num_aerosol_ids][num_modes] = {}; + Real qaer_delsub_coag_in[num_aerosol_ids][AeroConfig::max_agepair()] = {}; + Real qaer_delsub_cond[num_aerosol_ids][num_modes] = {}; + Real qaer_delsub_coag[num_aerosol_ids][num_modes] = {}; + Real qaer_del_cond_only[num_aerosol_ids][num_modes] = {}; + Real qnum_del_cond[num_modes] = {}; + Real qnum_del_rnam[num_modes] = {}; + Real qnum_del_nnuc[num_modes] = {}; + Real qnum_del_coag[num_modes] = {}; + Real qnum_delsub_cond[num_modes] = {}; + Real qnum_delsub_coag[num_modes] = {}; + Real qnum_del_cond_only[num_modes] = {}; + Real dnclusterdt = 0.0; + + const int ntsubstep = 1; + Real dtsubstep = deltat; + if (ntsubstep > 1) + dtsubstep = deltat / ntsubstep; + Real del_h2so4_gasprod = + haero::max(qgas3[igas_h2so4] - qgas1[igas_h2so4], 0.0) / ntsubstep; + + // loop over multiple time sub-steps + for (int jtsubstep = 1; jtsubstep <= ntsubstep; ++jtsubstep) { + // gas-aerosol exchange + Real uptkrate_h2so4 = 0.0; + Real del_h2so4_aeruptk = 0.0; + Real qaer_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; + Real qgas_avg[num_gas_ids] = {}; + Real qnum_sv1[num_modes] = {}; + Real qaer_sv1[num_aerosol_ids][num_modes] = {}; + Real qgas_sv1[num_gas_ids] = {}; + + if (config.do_cond) { + + const bool l_calc_gas_uptake_coeff = jtsubstep == 1; + Real uptkaer[num_gas_ids][num_modes] = {}; + + for (int i = 0; i < num_gas_ids; ++i) + qgas_sv1[i] = qgas_cur[i]; + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_sv1[j][i] = qaer_cur[j][i]; + + // time sub-step + const Real dtsub_soa_fixed = -1.0; + // Integration order + const int nghq = 2; + const int ntot_soamode = 4; + int niter_out = 0; + Real g0_soa_out = 0; + // gasaerexch::mam_gasaerexch_1subarea( + // nghq, igas_h2so4, igas_nh3, ntot_soamode, gas_to_aer, iaer_so4, + // iaer_pom, l_calc_gas_uptake_coeff, l_gas_condense_to_mode, + // eqn_and_numerics_category, dtsubstep, dtsub_soa_fixed, temp, pmid, + // aircon, num_gas_ids, qgas_cur, qgas_avg, qgas_netprod_otrproc, + // qaer_cur, qnum_cur, dgn_awet, alnsg_aer, uptk_rate_factor, uptkaer, + // uptkrate_h2so4, niter_out, g0_soa_out); + + if (config.newnuc_h2so4_conc_optaa == 11) + qgas_avg[igas_h2so4] = + 0.5 * (qgas_sv1[igas_h2so4] + qgas_cur[igas_h2so4]); + else if (config.newnuc_h2so4_conc_optaa == 12) + qgas_avg[igas_h2so4] = qgas_cur[igas_h2so4]; + + for (int i = 0; i < num_gas_ids; ++i) + qgas_del_cond[i] += + (qgas_cur[i] - (qgas_sv1[i] + qgas_netprod_otrproc[i] * dtsubstep)); + + for (int i = 0; i < num_modes; ++i) + qnum_delsub_cond[i] = qnum_cur[i] - qnum_sv1[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_delsub_cond[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; + + // qaer_del_grow4rnam = change in qaer_del_cond during latest condensation + // calculations + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_delsub_grow4rnam[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; + for (int i = 0; i < num_gas_ids; ++i) + qgas_del_cond_only[i] = qgas_del_cond[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_del_cond_only[i][j] = qaer_delsub_cond[i][j]; + for (int i = 0; i < num_modes; ++i) + qnum_del_cond_only[i] = qnum_delsub_cond[i]; + del_h2so4_aeruptk = + qgas_cur[igas_h2so4] - + (qgas_sv1[igas_h2so4] + qgas_netprod_otrproc[igas_h2so4] * dtsubstep); + } else { + for (int i = 0; i < num_gas_ids; ++i) + qgas_avg[i] = qgas_cur[i]; + } + + // renaming after "continuous growth" + if (config.do_rename) { + constexpr int nmodes = AeroConfig::num_modes(); + constexpr int naerosol_species = AeroConfig::num_aerosol_ids(); + const Real smallest_dryvol_value = 1.0e-25; // BAD_CONSTANT + const int dest_mode_of_mode[nmodes] = {-1, 0, -1, -1}; + + Real qnumcw_cur[num_modes] = {}; + Real qaercw_cur[num_aerosol_ids][num_modes] = {}; + Real qaercw_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; + Real mean_std_dev[nmodes]; + Real fmode_dist_tail_fac[nmodes]; + Real v2n_lo_rlx[nmodes]; + Real v2n_hi_rlx[nmodes]; + Real ln_diameter_tail_fac[nmodes]; + int num_pairs = 0; + Real diameter_cutoff[nmodes]; + Real ln_dia_cutoff[nmodes]; + Real diameter_threshold[nmodes]; + Real mass_2_vol[naerosol_species] = {0.15, + 6.4971751412429377e-002, + 0.15, + 7.0588235294117650e-003, + 3.0789473684210526e-002, + 5.1923076923076926e-002, + 156.20986883198000}; + + rename::find_renaming_pairs(dest_mode_of_mode, // in + mean_std_dev, // out + fmode_dist_tail_fac, // out + v2n_lo_rlx, // out + v2n_hi_rlx, // out + ln_diameter_tail_fac, // out + num_pairs, // out + diameter_cutoff, // out + ln_dia_cutoff, diameter_threshold); + + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_sv1[j][i] = qaer_cur[j][i]; + Real dgnum_amode[nmodes]; + for (int m = 0; m < nmodes; ++m) { + dgnum_amode[m] = modes(m).nom_diameter; + } + + { + Real qmol_i_cur[num_modes][num_aerosol_ids]; + Real qmol_i_del[num_modes][num_aerosol_ids]; + Real qmol_c_cur[num_modes][num_aerosol_ids]; + Real qmol_c_del[num_modes][num_aerosol_ids]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) { + qmol_i_cur[i][j] = qaer_cur[j][i]; + qmol_i_del[i][j] = qaer_delsub_grow4rnam[j][i]; + qmol_c_cur[i][j] = qaercw_cur[j][i]; + qmol_c_del[i][j] = qaercw_delsub_grow4rnam[j][i]; + } + Rename rename; + rename.mam_rename_1subarea_( + iscldy_subarea, smallest_dryvol_value, dest_mode_of_mode, + mean_std_dev, fmode_dist_tail_fac, v2n_lo_rlx, v2n_hi_rlx, + ln_diameter_tail_fac, num_pairs, diameter_cutoff, ln_dia_cutoff, + diameter_threshold, mass_2_vol, dgnum_amode, qnum_cur, qmol_i_cur, + qmol_i_del, qnumcw_cur, qmol_c_cur, qmol_c_del); + + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) { + qaer_cur[j][i] = qmol_i_cur[i][j]; + qaer_delsub_grow4rnam[j][i] = qmol_i_del[i][j]; + qaercw_cur[j][i] = qmol_c_cur[i][j]; + qaercw_delsub_grow4rnam[j][i] = qmol_c_del[i][j]; + } + } + + for (int i = 0; i < num_modes; ++i) + qnum_del_rnam[i] += qnum_cur[i] - qnum_sv1[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_del_rnam[i][j] += qaer_cur[i][j] - qaer_sv1[i][j]; + } + + // new particle formation (nucleation) + if (config.do_newnuc) { + for (int i = 0; i < num_gas_ids; ++i) + qgas_sv1[i] = qgas_cur[i]; + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + Real qaer_cur_tmp[num_modes][num_aerosol_ids]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) { + qaer_sv1[j][i] = qaer_cur[j][i]; + qaer_cur_tmp[i][j] = qaer_cur[j][i]; + } + Real dnclusterdt_substep = 0; + Real dndt_ait = 0; + Real dmdt_ait = 0; + Real dso4dt_ait = 0; + Real dnh4dt_ait = 0; + Nucleation nucleation; + Nucleation::Config config; + // config.dens_so4a_host = 1770; + config.mw_nh4a_host = 115; + config.mw_so4a_host = 115; + // config.accom_coef_h2so4 = 0.65; + AeroConfig aero_config; + nucleation.init(aero_config, config); + // new version after switching from box model version of nucleation + // compute_tendencies_( + // // Real deltat, + // // Real temp_in, + // // Real press_in, + // // Real zm_in, + // // Real pblh_in, + // // Real relhum, + // // Real uptkrate_h2so4, + // const int nsize, // missing? + // const Real dp_lo_mode, + // const Real dp_hi_mode, + // // const Real qgas_cur[num_gases], + // // const Real qgas_avg[num_gases], + // int &isize_nuc, + // // Real &qnuma_del, + // // Real &qso4a_del, + // // Real &qnh4a_del, + // // Real &qh2so4_del, + // Real &qnh3_del, + // // Real &dnclusterdt + // ) + // old, box-model-based version + // nucleation.compute_tendencies_( + // dtsubstep, temp, pmid, aircon, zmid, pblh, relhum, uptkrate_h2so4, + // del_h2so4_gasprod, del_h2so4_aeruptk, qgas_cur, qgas_avg, qnum_cur, + // qaer_cur_tmp, qwtr_cur, dndt_ait, dmdt_ait, dso4dt_ait, dnh4dt_ait, + // dnclusterdt_substep); + // nucleation.compute_tendencies_( + // dtsubstep, // deltat + // temp, // temp_in + // pmid, // press_in + // aircon, // ? + // zmid, // zm_in + // pblh, // pblh_in + // relhum, // relhum + // uptkrate_h2so4, // uptkrate_h2so4 + // // nsize? + // // dp_lo_mode? + // // dp_hi_mode? + // del_h2so4_aeruptk, // uptkrate_h2so4? + // qgas_cur, // qgas_cur[num_gases]? + // qgas_avg, // qgas_avg[num_gases]? + // // isize_nuc? + // qnum_cur, // qnuma_del? + // qaer_cur_tmp, // ? + // qwtr_cur, // ? + // dndt_ait, // + // dmdt_ait, // + // dso4dt_ait, // qso4a_del + // dnh4dt_ait, // qnh4a_del + // del_h2so4_gasprod, // qh2so4_del? + // // qnh3_del? + // dnclusterdt_substep // dnclusterdt + // ); + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_cur[j][i] = qaer_cur_tmp[i][j]; + + //! Apply the tendencies to the prognostics. + const int nait = static_cast(ModeIndex::Aitken); + qnum_cur[nait] += dndt_ait * dtsubstep; + + if (dso4dt_ait > 0.0) { + static constexpr int iaer_so4 = static_cast(AeroId::SO4); + static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); + + Real delta_q = dso4dt_ait * dtsubstep; + qaer_cur[iaer_so4][nait] += delta_q; + delta_q = haero::min(delta_q, qgas_cur[igas_h2so4]); + qgas_cur[igas_h2so4] -= delta_q; + } + + if (igas_nh3 > 0 && dnh4dt_ait > 0.0) { + static constexpr int iaer_nh4 = + -9999999; // static_cast(AeroId::NH4); + + Real delta_q = dnh4dt_ait * dtsubstep; + qaer_cur[iaer_nh4][nait] += delta_q; + delta_q = haero::min(delta_q, qgas_cur[igas_nh3]); + qgas_cur[igas_nh3] -= delta_q; + } + for (int i = 0; i < num_gas_ids; ++i) + qgas_del_nnuc[i] += (qgas_cur[i] - qgas_sv1[i]); + for (int i = 0; i < num_modes; ++i) + qnum_del_nnuc[i] += (qnum_cur[i] - qnum_sv1[i]); + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_del_nnuc[j][i] += (qaer_cur[j][i] - qaer_sv1[j][i]); + + dnclusterdt = dnclusterdt + dnclusterdt_substep * (dtsubstep / deltat); + } + + // coagulation part + if (config.do_coag) { + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_sv1[j][i] = qaer_cur[j][i]; + // coagulation::mam_coag_1subarea(dtsubstep, temp, pmid, aircon, dgn_a, + // dgn_awet, wetdens, qnum_cur, qaer_cur, + // qaer_delsub_coag_in); + for (int i = 0; i < num_modes; ++i) + qnum_delsub_coag[i] = qnum_cur[i] - qnum_sv1[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_delsub_coag[j][i] = qaer_cur[j][i] - qaer_sv1[j][i]; + } + + // primary carbon aging + + aging::mam_pcarbon_aging_1subarea( + dgn_a, qnum_cur, qnum_delsub_cond, qnum_delsub_coag, qaer_cur, + qaer_delsub_cond, qaer_delsub_coag, qaer_delsub_coag_in); + + // accumulate sub-step q-dels + if (config.do_coag) { + for (int i = 0; i < num_modes; ++i) + qnum_del_coag[i] += qnum_delsub_coag[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_del_coag[j][i] += qaer_delsub_coag[j][i]; + } + if (config.do_cond) { + for (int i = 0; i < num_modes; ++i) + qnum_del_cond[i] += qnum_delsub_cond[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_del_cond[j][i] += qaer_delsub_cond[j][i]; + } + } + + // final mix ratios + for (int i = 0; i < num_gas_ids; ++i) + qgas4[i] = qgas_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer4[j][i] = qaer_cur[j][i]; + for (int i = 0; i < num_modes; ++i) + qnum4[i] = qnum_cur[i]; + for (int i = 0; i < num_modes; ++i) + qwtr4[i] = qwtr_cur[i]; + + // final mix ratio changes + for (int i = 0; i < num_gas_ids; ++i) { + qgas_delaa[i][iqtend_cond()] = qgas_del_cond[i]; + qgas_delaa[i][iqtend_rnam()] = 0.0; + qgas_delaa[i][iqtend_nnuc()] = qgas_del_nnuc[i]; + qgas_delaa[i][iqtend_coag()] = 0.0; + qgas_delaa[i][iqtend_cond_only()] = qgas_del_cond_only[i]; + } + for (int i = 0; i < num_modes; ++i) { + qnum_delaa[i][iqtend_cond()] = qnum_del_cond[i]; + qnum_delaa[i][iqtend_rnam()] = qnum_del_rnam[i]; + qnum_delaa[i][iqtend_nnuc()] = qnum_del_nnuc[i]; + qnum_delaa[i][iqtend_coag()] = qnum_del_coag[i]; + qnum_delaa[i][iqtend_cond_only()] = qnum_del_cond_only[i]; + } + for (int j = 0; j < num_aerosol_ids; ++j) { + for (int i = 0; i < num_modes; ++i) { + qaer_delaa[j][i][iqtend_cond()] = qaer_del_cond[j][i]; + qaer_delaa[j][i][iqtend_rnam()] = qaer_del_rnam[j][i]; + qaer_delaa[j][i][iqtend_nnuc()] = qaer_del_nnuc[j][i]; + qaer_delaa[j][i][iqtend_coag()] = qaer_del_coag[j][i]; + qaer_delaa[j][i][iqtend_cond_only()] = qaer_del_cond_only[j][i]; + } + } +} + +KOKKOS_INLINE_FUNCTION +void mam_amicphys_1subarea_cloudy( + const AmicPhysConfig& config, const int nstep, const Real deltat, const int jsub, + const int nsubarea, const bool iscldy_subarea, const Real afracsub, + const Real temp, const Real pmid, const Real pdel, const Real zmid, + const Real pblh, const Real relhum, Real dgn_a[AeroConfig::num_modes()], + Real dgn_awet[AeroConfig::num_modes()], + Real wetdens[AeroConfig::num_modes()], + const Real qgas1[AeroConfig::num_gas_ids()], + const Real qgas3[AeroConfig::num_gas_ids()], + Real qgas4[AeroConfig::num_gas_ids()], + Real qgas_delaa[AeroConfig::num_gas_ids()][nqtendaa()], + const Real qnum3[AeroConfig::num_modes()], + Real qnum4[AeroConfig::num_modes()], + Real qnum_delaa[AeroConfig::num_modes()][nqtendaa()], + const Real qaer2[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], + const Real qaer3[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], + Real qaer4[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], + Real qaer_delaa[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] + [nqtendaa()], + const Real qwtr3[AeroConfig::num_modes()], + Real qwtr4[AeroConfig::num_modes()], + const Real qnumcw3[AeroConfig::num_modes()], + Real qnumcw4[AeroConfig::num_modes()], + Real qnumcw_delaa[AeroConfig::num_modes()][nqqcwtendaa()], + const Real qaercw2[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], + const Real qaercw3[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], + Real qaercw4[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], + Real qaercw_delaa[AeroConfig::num_gas_ids()][AeroConfig::num_modes()] + [nqqcwtendaa()]) { + + // + // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) + // qgas3, qaer3, qaercw3, qnum3, qnumcw3 are the current incoming TMRs + // qgas4, qaer4, qaercw4, qnum4, qnumcw4 are the updated outgoing TMRs + // + // when config.do_cond = false, this routine only calculates changes involving + // growth from smaller to larger modes (renaming) following cloud chemistry + // so gas TMRs are not changed + // when config.do_cond = true, this routine also calculates changes involving + // gas-aerosol exchange (condensation/evaporation) + // transfer of particles from hydrophobic modes to hydrophilic modes + // (aging) + // due to condensation + // currently this routine does not do + // new particle nucleation - because h2so4 gas conc. should be very low in + // cloudy air coagulation - because cloud-borne aerosol would need to be + // included + // + + // qXXXN (X=gas,aer,wat,num; N=1:4) are sub-area mixing ratios + // XXX=gas - gas species + // XXX=aer - aerosol mass species (excluding water) + // XXX=wat - aerosol water + // XXX=num - aerosol number + // N=1 - before gas-phase chemistry + // N=2 - before cloud chemistry + // N=3 - current incoming values (before gas-aerosol exchange, newnuc, + // coag) N=4 - updated outgoing values (after gas-aerosol exchange, + // newnuc, coag) + // + // qXXX_delaa are TMR changes (not tendencies) + // for different processes, which are used to produce history output + // for a clear sub-area, the processes are condensation/evaporation (and + // associated aging), + // renaming, coagulation, and nucleation + + // qxxx_del_yyyy are mix-ratio changes over full time step (deltat) + // qxxx_delsub_yyyy are mix-ratio changes over time sub-step (dtsubstep) + + static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); + static constexpr int num_modes = AeroConfig::num_modes(); + static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); + + static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); + // Turn off nh3 for now. This is a future enhancement. + static constexpr int igas_nh3 = -999888777; // Same as mam_refactor + static constexpr int iaer_so4 = static_cast(AeroId::SO4); + static constexpr int iaer_pom = static_cast(AeroId::POM); + + const AeroId gas_to_aer[num_gas_ids] = {AeroId::SOA, AeroId::SO4, + AeroId::None}; + const bool l_gas_condense_to_mode[num_gas_ids][num_modes] = { + {true, true, true, true}, + {true, true, true, true}, + {false, false, false, false}}; + enum { NA, ANAL, IMPL }; + const int eqn_and_numerics_category[num_gas_ids] = {IMPL, ANAL, ANAL}; + // air molar density (kmol/m3) + // In order to try to match the results in mam_refactor + // set r_universal as [mJ/(mol)] as in mam_refactor. + // const Real r_universal = Constants::r_gas; // [mJ/(K mol)] + const Real r_universal = 8.314467591; // [mJ/(mol)] as in mam_refactor + const Real aircon = pmid / (1000 * r_universal * temp); + const Real alnsg_aer[num_modes] = {0.58778666490211906, 0.47000362924573563, + 0.58778666490211906, 0.47000362924573563}; + const Real uptk_rate_factor[num_gas_ids] = {0.81, 1.0, 1.0}; + + Real qgas_cur[num_gas_ids]; + for (int i = 0; i < num_gas_ids; ++i) + qgas_cur[i] = qgas3[i]; + Real qaer_cur[num_aerosol_ids][num_modes]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_cur[i][j] = qaer3[i][j]; + + Real qnum_cur[num_modes]; + for (int j = 0; j < num_modes; ++j) + qnum_cur[j] = qnum3[j]; + Real qwtr_cur[num_modes]; + for (int j = 0; j < num_modes; ++j) + qwtr_cur[j] = qwtr3[j]; + + Real qnumcw_cur[num_modes]; + for (int j = 0; j < num_modes; ++j) + qnumcw_cur[j] = qnumcw3[j]; + + Real qaercw_cur[num_gas_ids][num_modes]; + for (int i = 0; i < num_gas_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw_cur[i][j] = qaercw3[i][j]; + + Real qgas_netprod_otrproc[num_gas_ids] = {}; + if (config.do_cond && config.gaexch_h2so4_uptake_optaa == 2) { + for (int igas = 0; igas < num_gas_ids; ++igas) { + if (igas == igas_h2so4 || igas == igas_nh3) { + // if gaexch_h2so4_uptake_optaa == 2, then + // if qgas increases from pre-gaschem to post-cldchem, + // start from the pre-gaschem mix-ratio and add in the production + // during the integration + // if it decreases, + // start from post-cldchem mix-ratio + // *** currently just do this for h2so4 and nh3 + qgas_netprod_otrproc[igas] = (qgas3[igas] - qgas1[igas]) / deltat; + if (qgas_netprod_otrproc[igas] >= 0.0) + qgas_cur[igas] = qgas1[igas]; + else + qgas_netprod_otrproc[igas] = 0.0; + } + } + } + Real qgas_del_cond[num_gas_ids] = {}; + Real qgas_del_nnuc[num_gas_ids] = {}; + Real qgas_del_cond_only[num_gas_ids] = {}; + Real qaer_del_cond[num_aerosol_ids][num_modes] = {}; + Real qaer_del_rnam[num_aerosol_ids][num_modes] = {}; + Real qaer_del_nnuc[num_aerosol_ids][num_modes] = {}; + Real qaer_del_coag[num_aerosol_ids][num_modes] = {}; + Real qaer_delsub_cond[num_aerosol_ids][num_modes] = {}; + Real qaer_delsub_coag[num_aerosol_ids][num_modes] = {}; + Real qaer_del_cond_only[num_aerosol_ids][num_modes] = {}; + Real qaercw_del_rnam[num_aerosol_ids][num_modes] = {}; + Real qnum_del_cond[num_modes] = {}; + Real qnum_del_rnam[num_modes] = {}; + Real qnum_del_nnuc[num_modes] = {}; + Real qnum_del_coag[num_modes] = {}; + Real qnum_delsub_cond[num_modes] = {}; + Real qnum_delsub_coag[num_modes] = {}; + Real qnum_del_cond_only[num_modes] = {}; + Real qnumcw_del_rnam[num_modes] = {}; + Real qaer_delsub_coag_in[num_aerosol_ids][AeroConfig::max_agepair()] = {}; + + const int ntsubstep = 1; + Real dtsubstep = deltat; + if (ntsubstep > 1) + dtsubstep = deltat / ntsubstep; + + // loop over multiple time sub-steps + + for (int jtsubstep = 1; jtsubstep <= ntsubstep; ++jtsubstep) { + // gas-aerosol exchange + Real uptkrate_h2so4 = 0.0; + Real qgas_avg[num_gas_ids] = {}; + Real qgas_sv1[num_gas_ids] = {}; + Real qnum_sv1[num_modes] = {}; + Real qaer_sv1[num_aerosol_ids][num_modes] = {}; + Real qaer_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; + + if (config.do_cond) { + + const bool l_calc_gas_uptake_coeff = jtsubstep == 1; + Real uptkaer[num_gas_ids][num_modes] = {}; + + for (int i = 0; i < num_gas_ids; ++i) + qgas_sv1[i] = qgas_cur[i]; + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_sv1[j][i] = qaer_cur[j][i]; + + const int nghq = 2; + const int ntot_soamode = 4; + int niter_out = 0; + Real g0_soa_out = 0; + // time sub-step + const Real dtsub_soa_fixed = -1.0; + // gasaerexch::mam_gasaerexch_1subarea( + // nghq, igas_h2so4, igas_nh3, ntot_soamode, gas_to_aer, iaer_so4, + // iaer_pom, l_calc_gas_uptake_coeff, l_gas_condense_to_mode, + // eqn_and_numerics_category, dtsubstep, dtsub_soa_fixed, temp, pmid, + // aircon, num_gas_ids, qgas_cur, qgas_avg, qgas_netprod_otrproc, + // qaer_cur, qnum_cur, dgn_awet, alnsg_aer, uptk_rate_factor, uptkaer, + // uptkrate_h2so4, niter_out, g0_soa_out); + + if (config.newnuc_h2so4_conc_optaa == 11) + qgas_avg[igas_h2so4] = + 0.5 * (qgas_sv1[igas_h2so4] + qgas_cur[igas_h2so4]); + else if (config.newnuc_h2so4_conc_optaa == 12) + qgas_avg[igas_h2so4] = qgas_cur[igas_h2so4]; + + for (int i = 0; i < num_gas_ids; ++i) + qgas_del_cond[i] += + (qgas_cur[i] - (qgas_sv1[i] + qgas_netprod_otrproc[i] * dtsubstep)); + + for (int i = 0; i < num_modes; ++i) + qnum_delsub_cond[i] = qnum_cur[i] - qnum_sv1[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_delsub_cond[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; + + // qaer_del_grow4rnam = change in qaer_del_cond during latest condensation + // calculations + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_delsub_grow4rnam[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; + for (int i = 0; i < num_gas_ids; ++i) + qgas_del_cond_only[i] = qgas_del_cond[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_del_cond_only[i][j] = qaer_delsub_cond[i][j]; + for (int i = 0; i < num_modes; ++i) + qnum_del_cond_only[i] = qnum_delsub_cond[i]; + + } else { + for (int i = 0; i < num_gas_ids; ++i) + qgas_avg[i] = qgas_cur[i]; + } + // renaming after "continuous growth" + if (config.do_rename) { + constexpr int nmodes = AeroConfig::num_modes(); + constexpr int naerosol_species = AeroConfig::num_aerosol_ids(); + const Real smallest_dryvol_value = 1.0e-25; // BAD_CONSTANT + const int dest_mode_of_mode[nmodes] = {-1, 0, -1, -1}; + + Real qnumcw_cur[num_modes] = {}; + Real qaercw_cur[num_aerosol_ids][num_modes] = {}; + Real qaercw_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; + Real mean_std_dev[nmodes]; + Real fmode_dist_tail_fac[nmodes]; + Real v2n_lo_rlx[nmodes]; + Real v2n_hi_rlx[nmodes]; + Real ln_diameter_tail_fac[nmodes]; + int num_pairs = 0; + Real diameter_cutoff[nmodes]; + Real ln_dia_cutoff[nmodes]; + Real diameter_threshold[nmodes]; + Real mass_2_vol[naerosol_species] = {0.15, + 6.4971751412429377e-002, + 0.15, + 7.0588235294117650e-003, + 3.0789473684210526e-002, + 5.1923076923076926e-002, + 156.20986883198000}; + + rename::find_renaming_pairs(dest_mode_of_mode, // in + mean_std_dev, // out + fmode_dist_tail_fac, // out + v2n_lo_rlx, // out + v2n_hi_rlx, // out + ln_diameter_tail_fac, // out + num_pairs, // out + diameter_cutoff, // out + ln_dia_cutoff, diameter_threshold); + + for (int i = 0; i < num_modes; ++i) + qnum_sv1[i] = qnum_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_sv1[j][i] = qaer_cur[j][i]; + Real dgnum_amode[nmodes]; + for (int m = 0; m < nmodes; ++m) { + dgnum_amode[m] = modes(m).nom_diameter; + } + + // qaercw_delsub_grow4rnam = change in qaercw from cloud chemistry + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw_delsub_grow4rnam[i][j] = + (qaercw3[i][j] - qaercw2[i][j]) / ntsubstep; + Real qnumcw_sv1[num_modes]; + for (int i = 0; i < num_modes; ++i) + qnumcw_sv1[i] = qnumcw_cur[i]; + Real qaercw_sv1[num_aerosol_ids][num_modes]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw_sv1[i][j] = qaercw_cur[i][j]; + + { + Real qmol_i_cur[num_modes][num_aerosol_ids]; + Real qmol_i_del[num_modes][num_aerosol_ids]; + Real qmol_c_cur[num_modes][num_aerosol_ids]; + Real qmol_c_del[num_modes][num_aerosol_ids]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) { + qmol_i_cur[i][j] = qaer_cur[j][i]; + qmol_i_del[i][j] = qaer_delsub_grow4rnam[j][i]; + qmol_c_cur[i][j] = qaercw_cur[j][i]; + qmol_c_del[i][j] = qaercw_delsub_grow4rnam[j][i]; + } + + Rename rename; + rename.mam_rename_1subarea_( + iscldy_subarea, smallest_dryvol_value, dest_mode_of_mode, + mean_std_dev, fmode_dist_tail_fac, v2n_lo_rlx, v2n_hi_rlx, + ln_diameter_tail_fac, num_pairs, diameter_cutoff, ln_dia_cutoff, + diameter_threshold, mass_2_vol, dgnum_amode, qnum_cur, qmol_i_cur, + qmol_i_del, qnumcw_cur, qmol_c_cur, qmol_c_del); + + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) { + qaer_cur[j][i] = qmol_i_cur[i][j]; + qaer_delsub_grow4rnam[j][i] = qmol_i_del[i][j]; + qaercw_cur[j][i] = qmol_c_cur[i][j]; + qaercw_delsub_grow4rnam[j][i] = qmol_c_del[i][j]; + } + } + for (int i = 0; i < num_modes; ++i) + qnum_del_rnam[i] += qnum_cur[i] - qnum_sv1[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaer_del_rnam[i][j] += qaer_cur[i][j] - qaer_sv1[i][j]; + for (int i = 0; i < num_modes; ++i) + qnumcw_del_rnam[i] += qnumcw_cur[i] - qnumcw_sv1[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw_del_rnam[i][j] += qaercw_cur[i][j] - qaercw_sv1[i][j]; + } + + // primary carbon aging + if (config.do_cond) { + aging::mam_pcarbon_aging_1subarea( + dgn_a, qnum_cur, qnum_delsub_cond, qnum_delsub_coag, qaer_cur, + qaer_delsub_cond, qaer_delsub_coag, qaer_delsub_coag_in); + } + // accumulate sub-step q-dels + if (config.do_cond) { + for (int i = 0; i < num_modes; ++i) + qnum_del_cond[i] += qnum_delsub_cond[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer_del_cond[j][i] += qaer_delsub_cond[j][i]; + } + } + // final mix ratios + for (int i = 0; i < num_gas_ids; ++i) + qgas4[i] = qgas_cur[i]; + for (int j = 0; j < num_aerosol_ids; ++j) + for (int i = 0; i < num_modes; ++i) + qaer4[j][i] = qaer_cur[j][i]; + for (int i = 0; i < num_modes; ++i) + qnum4[i] = qnum_cur[i]; + for (int i = 0; i < num_modes; ++i) + qwtr4[i] = qwtr_cur[i]; + for (int i = 0; i < num_modes; ++i) + qnumcw4[i] = qnumcw_cur[i]; + for (int i = 0; i < num_gas_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw4[i][j] = qaercw_cur[i][j]; + + // final mix ratio changes + for (int i = 0; i < num_gas_ids; ++i) { + qgas_delaa[i][iqtend_cond()] = qgas_del_cond[i]; + qgas_delaa[i][iqtend_rnam()] = 0.0; + qgas_delaa[i][iqtend_nnuc()] = qgas_del_nnuc[i]; + qgas_delaa[i][iqtend_coag()] = 0.0; + qgas_delaa[i][iqtend_cond_only()] = qgas_del_cond_only[i]; + } + for (int i = 0; i < num_modes; ++i) { + qnum_delaa[i][iqtend_cond()] = qnum_del_cond[i]; + qnum_delaa[i][iqtend_rnam()] = qnum_del_rnam[i]; + qnum_delaa[i][iqtend_nnuc()] = qnum_del_nnuc[i]; + qnum_delaa[i][iqtend_coag()] = qnum_del_coag[i]; + qnum_delaa[i][iqtend_cond_only()] = qnum_del_cond_only[i]; + } + for (int j = 0; j < num_aerosol_ids; ++j) { + for (int i = 0; i < num_modes; ++i) { + qaer_delaa[j][i][iqtend_cond()] = qaer_del_cond[j][i]; + qaer_delaa[j][i][iqtend_rnam()] = qaer_del_rnam[j][i]; + qaer_delaa[j][i][iqtend_nnuc()] = qaer_del_nnuc[j][i]; + qaer_delaa[j][i][iqtend_coag()] = qaer_del_coag[j][i]; + qaer_delaa[j][i][iqtend_cond_only()] = qaer_del_cond_only[j][i]; + } + } + for (int i = 0; i < num_modes; ++i) + qnumcw_delaa[i][iqqcwtend_rnam()] = qnumcw_del_rnam[i]; + for (int i = 0; i < num_aerosol_ids; ++i) + for (int j = 0; j < num_modes; ++j) + qaercw_delaa[i][j][iqqcwtend_rnam()] = qaercw_del_rnam[i][j]; +} + +KOKKOS_INLINE_FUNCTION +void mam_amicphys_1gridcell( + const AmicPhysConfig& config, const int nstep, const Real deltat, const int nsubarea, + const int ncldy_subarea, const bool iscldy_subarea[maxsubarea()], + const Real afracsub[maxsubarea()], const Real temp, const Real pmid, + const Real pdel, const Real zmid, const Real pblh, + const Real relhumsub[maxsubarea()], Real dgn_a[AeroConfig::num_modes()], + Real dgn_awet[AeroConfig::num_modes()], + Real wetdens[AeroConfig::num_modes()], + const Real qsub1[AeroConfig::num_gas_ids()][maxsubarea()], + const Real qsub2[AeroConfig::num_gas_ids()][maxsubarea()], + const Real qqcwsub2[AeroConfig::num_gas_ids()][maxsubarea()], + const Real qsub3[AeroConfig::num_gas_ids()][maxsubarea()], + const Real qqcwsub3[AeroConfig::num_gas_ids()][maxsubarea()], + Real qaerwatsub3[AeroConfig::num_modes()][maxsubarea()], + Real qsub4[AeroConfig::num_gas_ids()][maxsubarea()], + Real qqcwsub4[AeroConfig::num_gas_ids()][maxsubarea()], + Real qaerwatsub4[AeroConfig::num_modes()][maxsubarea()], + Real qsub_tendaa[AeroConfig::num_gas_ids()][nqtendaa()][maxsubarea()], + Real qqcwsub_tendaa[AeroConfig::num_gas_ids()][nqqcwtendaa()][maxsubarea()]) { + + // + // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) + // qsub3 and qqcwsub3 are the incoming current TMRs + // qsub4 and qqcwsub4 are the outgoing updated TMRs + // + // qsubN and qqcwsubN (N=1:4) are tracer mixing ratios (TMRs, mol/mol or + // #/kmol) in sub-areas + // currently there are just clear and cloudy sub-areas + // the N=1:4 have same meanings as for qgcmN + // N=1 - before gas-phase chemistry + // N=2 - before cloud chemistry + // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) + // N=4 - outgoing values (after gas-aerosol exchange, newnuc, coag) + // qsub_tendaa and qqcwsub_tendaa are TMR tendencies + // for different processes, which are used to produce history output + // the processes are condensation/evaporation (and associated aging), + // renaming, coagulation, and nucleation + + static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); + static constexpr int num_modes = AeroConfig::num_modes(); + static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); + + // the q--4 values will be equal to q--3 values unless they get changed + for (int i = 0; i < num_gas_ids; ++i) + for (int j = 0; j < maxsubarea(); ++j) { + qsub4[i][j] = qsub3[i][j]; + qqcwsub4[i][j] = qqcwsub3[i][j]; + } + for (int i = 0; i < num_modes; ++i) + for (int j = 0; j < maxsubarea(); ++j) + qaerwatsub4[i][j] = qaerwatsub3[i][j]; + for (int i = 0; i < num_gas_ids; ++i) + for (int j = 0; j < nqtendaa(); ++j) + for (int k = 0; k < maxsubarea(); ++k) + qsub_tendaa[i][j][k] = 0; + for (int i = 0; i < num_gas_ids; ++i) + for (int j = 0; j < nqqcwtendaa(); ++j) + for (int k = 0; k < maxsubarea(); ++k) + qqcwsub_tendaa[i][j][k] = 0.0; + + for (int jsub = 0; jsub < nsubarea; ++jsub) { + AmicPhysConfig sub_config = config; + if (iscldy_subarea[jsub]) { + sub_config.do_cond = config.do_cond; + sub_config.do_rename = config.do_rename; + sub_config.do_newnuc = false; + sub_config.do_coag = false; + } + const bool do_map_gas_sub = sub_config.do_cond || sub_config.do_newnuc; + + // map incoming sub-area mix-ratios to gas/aer/num arrays + Real qgas1[num_gas_ids] = {}; + Real qgas3[num_gas_ids] = {}; + Real qgas4[num_gas_ids] = {}; + if (do_map_gas_sub) { + // for cldy subarea, only do gases if doing gaexch + for (int igas = 0; igas < 2; ++igas) { + const int l = lmap_gas(igas); + qgas1[igas] = qsub1[l][jsub] * fcvt_gas(igas); + qgas3[igas] = qsub3[l][jsub] * fcvt_gas(igas); + qgas4[igas] = qgas3[igas]; + } + } + Real qaer2[num_aerosol_ids][num_modes] = {}; + Real qaer3[num_aerosol_ids][num_modes] = {}; + Real qnum3[num_modes] = {}; + Real qaer4[num_aerosol_ids][num_modes] = {}; + Real qnum4[num_modes] = {}; + Real qwtr3[num_modes] = {}; + Real qwtr4[num_modes] = {}; + for (int n = 0; n < num_modes; ++n) { + qnum3[n] = qsub3[n][jsub] * fcvt_num(); + qnum4[n] = qnum3[n]; + for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { + qaer2[iaer][n] = qsub2[iaer][jsub] * fcvt_aer(iaer); + qaer3[iaer][n] = qsub3[iaer][jsub] * fcvt_aer(iaer); + qaer4[iaer][n] = qaer3[iaer][n]; + } + qwtr3[n] = qaerwatsub3[n][jsub] * fcvt_wtr(); + qwtr4[n] = qwtr3[n]; + } + Real qaercw2[num_aerosol_ids][num_modes] = {}; + Real qaercw3[num_aerosol_ids][num_modes] = {}; + Real qnumcw3[num_modes] = {}; + Real qaercw4[num_aerosol_ids][num_modes] = {}; + Real qnumcw4[num_modes] = {}; + if (iscldy_subarea[jsub]) { + // only do cloud-borne for cloudy + for (int n = 0; n < num_modes; ++n) { + qnumcw3[n] = qqcwsub3[n][jsub] * fcvt_num(); + qnumcw4[n] = qnumcw3[n]; + for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { + qaercw2[iaer][n] = qqcwsub2[n][jsub] * fcvt_aer(iaer); + qaercw3[iaer][n] = qqcwsub3[n][jsub] * fcvt_aer(iaer); + qaercw4[iaer][n] = qaercw3[iaer][n]; + } + } + } + + Real qgas_delaa[num_gas_ids][nqtendaa()] = {}; + Real qnum_delaa[num_modes][nqtendaa()] = {}; + Real qnumcw_delaa[num_modes][nqqcwtendaa()] = {}; + Real qaer_delaa[num_aerosol_ids][num_modes][nqtendaa()] = {}; + Real qaercw_delaa[num_aerosol_ids][num_modes][nqqcwtendaa()] = {}; + + if (iscldy_subarea[jsub]) { + mam_amicphys_1subarea_cloudy(sub_config, nstep, deltat, + jsub, nsubarea, iscldy_subarea[jsub], afracsub[jsub], temp, pmid, + pdel, zmid, pblh, relhumsub[jsub], dgn_a, dgn_awet, wetdens, qgas1, + qgas3, qgas4, qgas_delaa, qnum3, qnum4, qnum_delaa, qaer2, qaer3, + qaer4, qaer_delaa, qwtr3, qwtr4, qnumcw3, qnumcw4, qnumcw_delaa, + qaercw2, qaercw3, qaercw4, qaercw_delaa); + } else { + mam_amicphys_1subarea_clear(sub_config, nstep, deltat, + jsub, nsubarea, iscldy_subarea[jsub], afracsub[jsub], temp, pmid, + pdel, zmid, pblh, relhumsub[jsub], dgn_a, dgn_awet, wetdens, qgas1, + qgas3, qgas4, qgas_delaa, qnum3, qnum4, qnum_delaa, qaer3, qaer4, + qaer_delaa, qwtr3, qwtr4); + // map gas/aer/num arrays (mix-ratio and del=change) back to sub-area + // arrays + + if (do_map_gas_sub) { + for (int igas = 0; igas < 2; ++igas) { + const int l = lmap_gas(igas); + qsub4[l][jsub] = qgas4[igas] / fcvt_gas(igas); + for (int i = 0; i < nqtendaa(); ++i) + qsub_tendaa[l][i][jsub] = + qgas_delaa[igas][i] / (fcvt_gas(igas) * deltat); + } + } + for (int n = 0; n < num_modes; ++n) { + qsub4[n][jsub] = qnum4[n] / fcvt_num(); + for (int i = 0; i < nqtendaa(); ++i) + qsub_tendaa[n][i][jsub] = qnum_delaa[n][i] / (fcvt_num() * deltat); + for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { + qsub4[iaer][jsub] = qaer4[iaer][n] / fcvt_aer(iaer); + for (int i = 0; i < nqtendaa(); ++i) + qsub_tendaa[iaer][i][jsub] = + qaer_delaa[iaer][n][i] / (fcvt_aer(iaer) * deltat); + } + qaerwatsub4[n][jsub] = qwtr4[n] / fcvt_wtr(); + + if (iscldy_subarea[jsub]) { + qqcwsub4[n][jsub] = qnumcw4[n] / fcvt_num(); + for (int i = 0; i < nqqcwtendaa(); ++i) + qqcwsub_tendaa[n][i][jsub] = + qnumcw_delaa[n][i] / (fcvt_num() * deltat); + for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { + qqcwsub4[iaer][jsub] = qaercw4[iaer][n] / fcvt_aer(iaer); + for (int i = 0; i < nqqcwtendaa(); ++i) + qqcwsub_tendaa[iaer][i][jsub] = + qaercw_delaa[iaer][n][i] / (fcvt_aer(iaer) * deltat); + } + } + } + } + } +} + +} // anonymous namespace + +KOKKOS_INLINE_FUNCTION +void modal_aero_amicphys_intr( + const AmicPhysConfig& config, const int nstep, const Real deltat, const Real t, const Real pmid, const Real pdel, + const Real zm, const Real pblh, const Real qv, const Real cld, + Real q[gas_pcnst()], Real qqcw[gas_pcnst()], const Real q_pregaschem[gas_pcnst()], + const Real q_precldchem[gas_pcnst()], const Real qqcw_precldchem[gas_pcnst()], + Real q_tendbb[gas_pcnst()][nqtendbb()], Real qqcw_tendbb[gas_pcnst()][nqtendbb()], + Real dgncur_a[AeroConfig::num_modes()], + Real dgncur_awet[AeroConfig::num_modes()], + Real wetdens_host[AeroConfig::num_modes()], + Real qaerwat[AeroConfig::num_modes()]) { + + /* + nstep ! model time-step number + nqtendbb ! dimension for q_tendbb + nqqcwtendbb ! dimension f + deltat ! + q(ncol,pver,pcnstxx) ! current tracer mixing ratios (TMRs) + these values are updated (so out /= in) + *** MUST BE #/kmol-air for number + *** MUST BE mol/mol-air for mass + *** NOTE ncol dimension + qqcw(ncol,pver,pcnstxx) + like q but for cloud-borner tracers + these values are updated + q_pregaschem(ncol,pver,pcnstxx) ! q TMRs before gas-phase + chemistry q_precldchem(ncol,pver,pcnstxx) ! q TMRs before cloud + chemistry qqcw_precldchem(ncol,pver,pcnstxx) ! qqcw TMRs before cloud + chemistry q_tendbb(ncol,pver,pcnstxx,nqtendbb()) ! TMR tendencies for + box-model diagnostic output qqcw_tendbb(ncol,pver,pcnstx t(pcols,pver) ! + temperature at model levels (K) pmid(pcols,pver) ! pressure at model + level centers (Pa) pdel(pcols,pver) ! pressure thickness of levels + (Pa) zm(pcols,pver) ! altitude (above ground) at level centers (m) + pblh(pcols) ! planetary boundary layer depth (m) + qv(pcols,pver) ! specific humidity (kg/kg) + cld(ncol,pver) ! cloud fraction (-) *** NOTE ncol dimension + dgncur_a(pcols,pver,ntot_amode) + dgncur_awet(pcols,pver,ntot_amode) + ! dry & wet geo. mean dia. (m) of + number distrib. wetdens_host(pcols,pver,ntot_amode) ! interstitial + aerosol wet density (kg/m3) + + qaerwat(pcols,pver,ntot_amode aerosol water mixing ratio (kg/kg, + NOT mol/mol) + + */ + + // !DESCRIPTION: + // calculates changes to gas and aerosol TMRs (tracer mixing ratios) from + // gas-aerosol exchange (condensation/evaporation) + // growth from smaller to larger modes (renaming) due to both + // condensation and cloud chemistry + // new particle nucleation + // coagulation + // transfer of particles from hydrophobic modes to hydrophilic modes + // (aging) + // due to condensation and coagulation + // + // the incoming mixing ratios (q and qqcw) are updated before output + // + // !REVISION HISTORY: + // RCE 07.04.13: Adapted from earlier version of CAM5 modal aerosol + // routines + // for these processes + // + + static constexpr int num_modes = AeroConfig::num_modes(); + + // qgcmN and qqcwgcmN (N=1:4) are grid-cell mean tracer mixing ratios + // (TMRs, mol/mol or #/kmol) + // N=1 - before gas-phase chemistry + // N=2 - before cloud chemistry + // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) + // N=4 - outgoing values (after gas-aerosol exchange, newnuc, coag) + + // qsubN and qqcwsubN (N=1:4) are TMRs in sub-areas + // currently there are just clear and cloudy sub-areas + // the N=1:4 have same meanings as for qgcmN + + // q_coltendaa and qqcw_coltendaa are column-integrated tendencies + // for different processes, which are output to history + // the processes are condensation/evaporation (and associated aging), + // renaming, coagulation, and nucleation + + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqtendbb(); ++j) + q_tendbb[i][j] = 0.0, qqcw_tendbb[i][j] = 0.0; + + // get saturation mixing ratio + // call qsat( t(1:ncol,1:pver), pmid(1:ncol,1:pvnner), & + // ev_sat(1:ncol,1:pver), qv_sat(1:ncol,1:pver) ) + const Real epsqs = haero::Constants::weight_ratio_h2o_air; + // Saturation vapor pressure + const Real ev_sat = conversions::vapor_saturation_pressure_magnus(t, pmid); + // Saturation specific humidity + const Real qv_sat = epsqs * ev_sat / (pmid - (1 - epsqs) * ev_sat); + + const Real relhumgcm = haero::max(0.0, haero::min(1.0, qv / qv_sat)); + + // Set up cloudy/clear subareas inside a grid cell + int nsubarea, ncldy_subarea, jclea, jcldy; + bool iscldy_subarea[maxsubarea()]; + Real afracsub[maxsubarea()]; + Real relhumsub[maxsubarea()]; + Real qsub1[gas_pcnst()][maxsubarea()]; + Real qsub2[gas_pcnst()][maxsubarea()]; + Real qsub3[gas_pcnst()][maxsubarea()]; + Real qqcwsub1[gas_pcnst()][maxsubarea()]; + Real qqcwsub2[gas_pcnst()][maxsubarea()]; + Real qqcwsub3[gas_pcnst()][maxsubarea()]; + // aerosol water mixing ratios (mol/mol) + Real qaerwatsub3[AeroConfig::num_modes()][maxsubarea()]; + construct_subareas_1gridcell(cld, relhumgcm, // in + q_pregaschem, q_precldchem, // in + qqcw_precldchem, // in + q, qqcw, // in + nsubarea, ncldy_subarea, jclea, jcldy, // out + iscldy_subarea, afracsub, relhumsub, // out + qsub1, qsub2, qsub3, // out + qqcwsub1, qqcwsub2, qqcwsub3, qaerwatsub3, // out + qaerwat // in + ); + + // Initialize the "after-amicphys" values + Real qsub4[gas_pcnst()][maxsubarea()] = {}; + Real qqcwsub4[gas_pcnst()][maxsubarea()] = {}; + Real qaerwatsub4[AeroConfig::num_modes()][maxsubarea()] = {}; + + // + // start integration + // + Real dgn_a[num_modes], dgn_awet[num_modes], wetdens[num_modes]; + for (int n = 0; n < num_modes; ++n) { + dgn_a[n] = dgncur_a[n]; + dgn_awet[n] = dgncur_awet[n]; + wetdens[n] = haero::max(1000.0, wetdens_host[n]); + } + Real qsub_tendaa[gas_pcnst()][nqtendaa()][maxsubarea()] = {}; + Real qqcwsub_tendaa[gas_pcnst()][nqqcwtendaa()][maxsubarea()] = {}; + mam_amicphys_1gridcell(config, nstep, deltat, + nsubarea, ncldy_subarea, iscldy_subarea, afracsub, t, + pmid, pdel, zm, pblh, relhumsub, dgn_a, dgn_awet, + wetdens, qsub1, qsub2, qqcwsub2, qsub3, qqcwsub3, + qaerwatsub3, qsub4, qqcwsub4, qaerwatsub4, qsub_tendaa, + qqcwsub_tendaa); + + // + // form new grid-mean mix-ratios + Real qgcm4[gas_pcnst()]; + Real qgcm_tendaa[gas_pcnst()][nqtendaa()]; + Real qaerwatgcm4[num_modes]; + if (nsubarea == 1) { + for (int i = 0; i < gas_pcnst(); ++i) + qgcm4[i] = qsub4[i][0]; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqtendaa(); ++j) + qgcm_tendaa[i][j] = qsub_tendaa[i][j][0]; + for (int i = 0; i < num_modes; ++i) + qaerwatgcm4[i] = qaerwatsub4[i][0]; + } else { + for (int i = 0; i < gas_pcnst(); ++i) + qgcm4[i] = 0.0; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqtendaa(); ++j) + qgcm_tendaa[i][j] = 0.0; + for (int n = 0; n < nsubarea; ++n) { + for (int i = 0; i < gas_pcnst(); ++i) + qgcm4[i] += qsub4[i][n] * afracsub[n]; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqtendaa(); ++j) + qgcm_tendaa[i][j] = + qgcm_tendaa[i][j] + qsub_tendaa[i][j][n] * afracsub[n]; + } + for (int i = 0; i < num_modes; ++i) + // for aerosol water use the clear sub-area value + qaerwatgcm4[i] = qaerwatsub4[i][jclea - 1]; + } + Real qqcwgcm4[gas_pcnst()]; + Real qqcwgcm_tendaa[gas_pcnst()][nqqcwtendaa()]; + if (ncldy_subarea <= 0) { + for (int i = 0; i < gas_pcnst(); ++i) + qqcwgcm4[i] = haero::max(0.0, qqcw[i]); + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqqcwtendaa(); ++j) + qqcwgcm_tendaa[i][j] = 0.0; + } else if (nsubarea == 1) { + for (int i = 0; i < gas_pcnst(); ++i) + qqcwgcm4[i] = qqcwsub4[i][0]; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqqcwtendaa(); ++j) + qqcwgcm_tendaa[i][j] = qqcwsub_tendaa[i][j][0]; + } else { + for (int i = 0; i < gas_pcnst(); ++i) + qqcwgcm4[i] = 0.0; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqqcwtendaa(); ++j) + qqcwgcm_tendaa[i][j] = 0.0; + for (int n = 0; n < nsubarea; ++n) { + if (iscldy_subarea[n]) { + for (int i = 0; i < gas_pcnst(); ++i) + qqcwgcm4[i] += qqcwsub4[i][n] * afracsub[n]; + for (int i = 0; i < gas_pcnst(); ++i) + for (int j = 0; j < nqqcwtendaa(); ++j) + qqcwgcm_tendaa[i][j] += qqcwsub_tendaa[i][j][n] * afracsub[n]; + } + } + } + + for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { + if (lmapcc_all(lmz) > 0) { + // HW, to ensure non-negative + q[lmz] = haero::max(qgcm4[lmz], 0.0); + if (lmapcc_all(lmz) >= lmapcc_val_aer()) { + // HW, to ensure non-negative + qqcw[lmz] = haero::max(qqcwgcm4[lmz], 0.0); + } + } + } + for (int i = 0; i < gas_pcnst(); ++i) { + if (iqtend_cond() < nqtendbb()) + q_tendbb[i][iqtend_cond()] = qgcm_tendaa[i][iqtend_cond()]; + if (iqtend_rnam() < nqtendbb()) + q_tendbb[i][iqtend_rnam()] = qgcm_tendaa[i][iqtend_rnam()]; + if (iqtend_nnuc() < nqtendbb()) + q_tendbb[i][iqtend_nnuc()] = qgcm_tendaa[i][iqtend_nnuc()]; + if (iqtend_coag() < nqtendbb()) + q_tendbb[i][iqtend_coag()] = qgcm_tendaa[i][iqtend_coag()]; + if (iqqcwtend_rnam() < nqqcwtendbb()) + qqcw_tendbb[i][iqqcwtend_rnam()] = qqcwgcm_tendaa[i][iqqcwtend_rnam()]; + } + for (int i = 0; i < num_modes; ++i) + qaerwat[i] = qaerwatgcm4[i]; +} + +} // namespace scream::impl diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp index b844f5b18e6..c8318dcc05c 100644 --- a/components/eamxx/src/physics/mam/online_emission.hpp +++ b/components/eamxx/src/physics/mam/online_emission.hpp @@ -63,12 +63,26 @@ template struct onlineEmissions { // --------------------------------------------------------------------------- // Online emissions routines // --------------------------------------------------------------------------- - void init_from_input_file(const ekat::ParameterList &m_params); - void transfer_to_cflux(const onlineEmissData &data, - const std::map idx_map, - view_2d &fluxes); + void init_from_input_file(const ekat::ParameterList ¶ms) { + const int nspec = online_emis_data.nspec; + const int ncols = online_emis_data.ncols; + using ExeSpace = typename KT::ExeSpace; + using ESU = ekat::ExeSpaceUtils; + const auto policy = ESU::get_default_team_policy(ncols, nspec); + // Read from input file + // FIXME: currently reading a single placeholder scalar--should be + // ncols-sized array when we know what the input data looks like + for (int ispec = 0; ispec < nspec; ++ispec) { + Real init_cond_val = + params.get(online_emis_data.root_IC_str + online_emis_data.spec_names[ispec]); + // TODO: is this overkill?--i.e., would a mirror/deep_copy make more sense? + Kokkos::parallel_for( + policy, KOKKOS_LAMBDA(const MemberType &team) { + const int jcol = team.league_rank(); // column index + online_emis_data.flux_data(ispec, jcol) = init_cond_val; + }); + } + } // end init_from_input_file() }; // struct onlineEmissions } // namespace scream::mam_coupling #endif // ONLINE_EMISSION_HPP - -#include "online_emission_impl.hpp" diff --git a/components/eamxx/src/physics/mam/online_emission_impl.hpp b/components/eamxx/src/physics/mam/online_emission_impl.hpp deleted file mode 100644 index 1f0ac91a854..00000000000 --- a/components/eamxx/src/physics/mam/online_emission_impl.hpp +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef ONLINE_EMISSION_IMPL_HPP -#define ONLINE_EMISSION_IMPL_HPP - -namespace scream::mam_coupling { -template -void onlineEmissions::init_from_input_file(const ekat::ParameterList ¶ms) { - // FIXME: move this out to the onlineEmissions struct to avoid extra work - // by doing it again below? - const int nspec = online_emis_data.nspec; - const int ncols = online_emis_data.ncols; - using ExeSpace = typename KT::ExeSpace; - using ESU = ekat::ExeSpaceUtils; - const auto policy = ESU::get_default_team_policy(ncols, nspec); - // Read from input file - // FIXME: currently reading a single placeholder scalar--should be - // ncols-sized array when we know what the input data looks like - for (int ispec = 0; ispec < nspec; ++ispec) { - Real init_cond_val = - params.get(online_emis_data.root_IC_str + online_emis_data.spec_names[ispec]); - // TODO: is this overkill?--i.e., would a mirror/deep_copy make more sense? - Kokkos::parallel_for( - policy, KOKKOS_LAMBDA(const MemberType &team) { - const int jcol = team.league_rank(); // column index - online_emis_data.flux_data(ispec, jcol) = init_cond_val; - }); - } -} // end init_from_input_file() - -template -void onlineEmissions::transfer_to_cflux( - const onlineEmissData &data, const std::map idx_map, - view_2d &fluxes) { - const int nspec = data.nspec; - const int ncols = data.ncols; - using ExeSpace = typename KT::ExeSpace; - using ESU = ekat::ExeSpaceUtils; - const auto policy = ESU::get_default_team_policy(ncols, nspec); - - Kokkos::parallel_for( - policy, KOKKOS_LAMBDA(const MemberType &team) { - const int jcol = team.league_rank(); // column index - Kokkos::parallel_for( - Kokkos::TeamThreadRange(team, nspec), [&](const int ispec) { - auto s_idx = idx_map.at(data.spec_names[ispec]); - data.cfluxes(jcol, s_idx) = data.flux_data(ispec, jcol); - }); - }); - Kokkos::deep_copy(fluxes, data.cfluxes); -} -} // namespace scream::mam_coupling - -#endif // ONLINE_EMISSION_IMPL_HPP From 405ce81346acd04960adbcb5acc8a6863045e77e Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Sat, 26 Oct 2024 12:38:49 -0700 Subject: [PATCH 08/41] Adds sst variable from the ocean model in EAMxx via coupler --- .../src/control/atmosphere_surface_coupling_importer.cpp | 3 +++ components/eamxx/src/mct_coupling/scream_cpl_indices.F90 | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp index ee3e21e7461..f0eca885ddc 100644 --- a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp +++ b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp @@ -57,6 +57,9 @@ void SurfaceCouplingImporter::set_grids(const std::shared_ptr("fv", scalar2d_layout, m/s, grid_name); // Aerodynamical resistance add_field("ram1", scalar2d_layout, s/m, grid_name); + // Sea surface temperature [K] + //FIXME: Verify the units + add_field("sst", scalar2d_layout, K, 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..2627ac722fd 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 = 20 integer, parameter, public :: num_scream_exports = 17 integer, public :: num_cpl_imports, num_cpl_exports, import_field_size, export_field_size @@ -92,6 +92,7 @@ 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' ! CPL indices import_cpl_indices(1) = mct_avect_indexra(x2a,'Sx_avsdr') @@ -113,6 +114,7 @@ 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') + import_cpl_indices(20) = mct_avect_indexra(x2a,'So_t') ! Vector components import_vector_components(11) = 0 From e38b45bd7caf336280e36a61cfa5298d3960be61 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Sat, 26 Oct 2024 13:22:22 -0700 Subject: [PATCH 09/41] Adds dstfluxes (4 values per col) from coupler to eamxx --- .../atmosphere_surface_coupling_importer.cpp | 52 ++++++++++--------- .../src/mct_coupling/scream_cpl_indices.F90 | 14 ++++- ...and_online_emissions_process_interface.cpp | 2 +- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp index f0eca885ddc..e87bebd34e1 100644 --- a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp +++ b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp @@ -28,38 +28,42 @@ 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] //FIXME: Verify the units - add_field("sst", scalar2d_layout, K, grid_name); + 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 2627ac722fd..15df178d29e 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 = 20 + 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 @@ -93,6 +93,10 @@ subroutine scream_set_cpl_indices (x2a, a2x) 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') @@ -114,7 +118,15 @@ 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 import_vector_components(11) = 0 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 c84bb803440..5e44fd1e35e 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 @@ -51,7 +51,7 @@ void MAMSrfOnlineEmiss::set_grids( // and then bundle the online emissions computations as tendencies into the // Computed constituent_fluxes field // ------------------------------------------------------------- - static constexpr Units m2(m * m, "m2"); + constexpr auto m2 = pow(m, 2); // Constituent fluxes of species in [kg/m2/s] add_field("constituent_fluxes", scalar2d_pcnct, kg / m2 / s, grid_name); From 13f6cfcfb939b7f90a42ce16b553d9fb41520bb2 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Sun, 27 Oct 2024 22:48:21 -0700 Subject: [PATCH 10/41] Adds all input variables for online emissions into the interface cpp --- .../atmosphere_surface_coupling_importer.cpp | 1 - ...and_online_emissions_process_interface.cpp | 223 +++++++++++++----- ...and_online_emissions_process_interface.hpp | 68 ++++-- .../single-process/mam/emissions/input.yaml | 4 + 4 files changed, 217 insertions(+), 79 deletions(-) diff --git a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp index e87bebd34e1..2c3360a3b4f 100644 --- a/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp +++ b/components/eamxx/src/control/atmosphere_surface_coupling_importer.cpp @@ -59,7 +59,6 @@ void SurfaceCouplingImporter::set_grids(const std::shared_ptr("ram1", scalar2d, s/m, grid_name); // Sea surface temperature [K] - //FIXME: Verify the units 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); 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 5e44fd1e35e..b495c6614ef 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 @@ -19,41 +19,101 @@ MAMSrfOnlineEmiss::MAMSrfOnlineEmiss(const ekat::Comm &comm, // ================================================================ void MAMSrfOnlineEmiss::set_grids( const std::shared_ptr grids_manager) { - grid_ = grids_manager->get_grid("Physics"); + grid_ = grids_manager->get_grid("Physics"); const auto &grid_name = grid_->name(); - ncol_ = grid_->get_num_local_dofs(); // Number of columns on this rank - nlev_ = grid_->get_num_vertical_levels(); // Number of levels per column + ncol_ = grid_->get_num_local_dofs(); // Number of columns on this rank + 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"); - // auto vector3d_mid = grid_->get_3d_vector_layout(true, 2); + 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 (Require only for building + // DS) + 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); - // FIXME: online emissions requires the following quantities for the - // OnlineEmissionsData struct: {surface_temp, u_bottom, v_bottom, z_bottom, - // ocean_frac} - // // Temperature - // add_field("T_mid", scalar3d_layout_mid, K, grid_name); - // add_field("horiz_winds", vector3d_mid, m/s, grid_name); - // vertical wind? - // ocean_frac? + // 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); + + // 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); // ------------------------------------------------------------- - // These variables are "Computed" or outputs for the process - // FIXME: this should likely be an updated field, since online emissisons - // expects input values for constituent_fluxes - // NOTE: the other option is that we do something like: - // add_field("constituent_fluxes_input", scalar2d_pcnct, - // kg / m2 / s, grid_name); - // and then bundle the online emissions computations as tendencies into the - // Computed constituent_fluxes field + // These variables are "Updated" or input-outputs for the process // ------------------------------------------------------------- - constexpr auto m2 = pow(m, 2); + + 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, + // FIXME: confirm if it is Updated or Computed + add_field("constituent_fluxes", vector2d_pcnst, kg / m2 / s, grid_name); // Surface emissions remapping file @@ -66,20 +126,20 @@ void MAMSrfOnlineEmiss::set_grids( //-------------------------------------------------------------------- srf_emiss_ dms; // File name, name and sectors - dms.data_file = m_params.get("srf_emis_specifier_for_DMS"); + dms.data_file = m_params.get("srf_emis_specifier_for_DMS"); dms.species_name = "dms"; - dms.sectors = {"DMS"}; - srf_emiss_species_.push_back(dms); // add to the vector + dms.sectors = {"DMS"}; + srf_emiss_species_.push_back(dms); // add to the vector //-------------------------------------------------------------------- // Init so2 srf emiss data structures //-------------------------------------------------------------------- srf_emiss_ so2; // File name, name and sectors - so2.data_file = m_params.get("srf_emis_specifier_for_SO2"); + so2.data_file = m_params.get("srf_emis_specifier_for_SO2"); so2.species_name = "so2"; - so2.sectors = {"AGR", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(so2); // add to the vector + 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 @@ -88,8 +148,8 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors bc_a4.data_file = m_params.get("srf_emis_specifier_for_bc_a4"); bc_a4.species_name = "bc_a4"; - bc_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(bc_a4); // add to the vector + bc_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; + srf_emiss_species_.push_back(bc_a4); // add to the vector //-------------------------------------------------------------------- // Init num_a1 srf emiss data structures @@ -98,9 +158,9 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a1.data_file = m_params.get("srf_emis_specifier_for_num_a1"); num_a1.species_name = "num_a1"; - num_a1.sectors = {"num_a1_SO4_AGR", "num_a1_SO4_SHP", "num_a1_SO4_SLV", - "num_a1_SO4_WST"}; - srf_emiss_species_.push_back(num_a1); // add to the vector + num_a1.sectors = {"num_a1_SO4_AGR", "num_a1_SO4_SHP", "num_a1_SO4_SLV", + "num_a1_SO4_WST"}; + srf_emiss_species_.push_back(num_a1); // add to the vector //-------------------------------------------------------------------- // Init num_a2 srf emiss data structures @@ -109,8 +169,8 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a2.data_file = m_params.get("srf_emis_specifier_for_num_a2"); num_a2.species_name = "num_a2"; - num_a2.sectors = {"num_a2_SO4_RCO", "num_a2_SO4_TRA"}; - srf_emiss_species_.push_back(num_a2); // add to the vector + num_a2.sectors = {"num_a2_SO4_RCO", "num_a2_SO4_TRA"}; + srf_emiss_species_.push_back(num_a2); // add to the vector //-------------------------------------------------------------------- // Init num_a4 srf emiss data structures @@ -119,12 +179,12 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors num_a4.data_file = m_params.get("srf_emis_specifier_for_num_a4"); num_a4.species_name = "num_a4"; - num_a4.sectors = { - "num_a1_BC_AGR", "num_a1_BC_ENE", "num_a1_BC_IND", "num_a1_BC_RCO", - "num_a1_BC_SHP", "num_a1_BC_SLV", "num_a1_BC_TRA", "num_a1_BC_WST", - "num_a1_POM_AGR", "num_a1_POM_ENE", "num_a1_POM_IND", "num_a1_POM_RCO", - "num_a1_POM_SHP", "num_a1_POM_SLV", "num_a1_POM_TRA", "num_a1_POM_WST"}; - srf_emiss_species_.push_back(num_a4); // add to the vector + num_a4.sectors = { + "num_a1_BC_AGR", "num_a1_BC_ENE", "num_a1_BC_IND", "num_a1_BC_RCO", + "num_a1_BC_SHP", "num_a1_BC_SLV", "num_a1_BC_TRA", "num_a1_BC_WST", + "num_a1_POM_AGR", "num_a1_POM_ENE", "num_a1_POM_IND", "num_a1_POM_RCO", + "num_a1_POM_SHP", "num_a1_POM_SLV", "num_a1_POM_TRA", "num_a1_POM_WST"}; + srf_emiss_species_.push_back(num_a4); // add to the vector //-------------------------------------------------------------------- // Init pom_a4 srf emiss data structures @@ -134,7 +194,7 @@ void MAMSrfOnlineEmiss::set_grids( pom_a4.data_file = m_params.get("srf_emis_specifier_for_pom_a4"); pom_a4.species_name = "pom_a4"; pom_a4.sectors = {"AGR", "ENE", "IND", "RCO", "SHP", "SLV", "TRA", "WST"}; - srf_emiss_species_.push_back(pom_a4); // add to the vector + srf_emiss_species_.push_back(pom_a4); // add to the vector //-------------------------------------------------------------------- // Init so4_a1 srf emiss data structures @@ -143,7 +203,7 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors so4_a1.data_file = m_params.get("srf_emis_specifier_for_so4_a1"); so4_a1.species_name = "so4_a1"; - so4_a1.sectors = {"AGR", "SHP", "SLV", "WST"}; + so4_a1.sectors = {"AGR", "SHP", "SLV", "WST"}; srf_emiss_species_.push_back(so4_a1); //-------------------------------------------------------------------- @@ -153,20 +213,20 @@ void MAMSrfOnlineEmiss::set_grids( // File name, name and sectors so4_a2.data_file = m_params.get("srf_emis_specifier_for_so4_a2"); so4_a2.species_name = "so4_a2"; - so4_a2.sectors = {"RCO", "TRA"}; + so4_a2.sectors = {"RCO", "TRA"}; srf_emiss_species_.push_back(so4_a2); //-------------------------------------------------------------------- // Init data structures to read and interpolate //-------------------------------------------------------------------- - for (srf_emiss_ &ispec_srf : srf_emiss_species_) { + for(srf_emiss_ &ispec_srf : srf_emiss_species_) { srfEmissFunc::init_srf_emiss_objects( ncol_, grid_, ispec_srf.data_file, ispec_srf.sectors, srf_map_file, // output ispec_srf.horizInterp_, ispec_srf.data_start_, ispec_srf.data_end_, ispec_srf.data_out_, ispec_srf.dataReader_); } -} // set_grid ends +} // set_grid ends // ================================================================ // REQUEST_BUFFER_SIZE_IN_BYTES @@ -184,9 +244,9 @@ size_t MAMSrfOnlineEmiss::requested_buffer_size_in_bytes() const { // intermediate (dry) quantities on the given number of columns with the given // number of vertical levels. Returns the number of bytes allocated. void MAMSrfOnlineEmiss::init_buffers(const ATMBufferManager &buffer_manager) { - EKAT_REQUIRE_MSG(buffer_manager.allocated_bytes() >= - requested_buffer_size_in_bytes(), - "Error! Insufficient buffer size.\n"); + EKAT_REQUIRE_MSG( + buffer_manager.allocated_bytes() >= requested_buffer_size_in_bytes(), + "Error! Insufficient buffer size.\n"); size_t used_mem = mam_coupling::init_buffer(buffer_manager, ncol_, nlev_, buffer_); @@ -199,6 +259,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 // --------------------------------------------------------------- @@ -223,10 +319,10 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //-------------------------------------------------------------------- // Update surface emissions from file //-------------------------------------------------------------------- - for (srf_emiss_ &ispec_srf : srf_emiss_species_) { + for(srf_emiss_ &ispec_srf : srf_emiss_species_) { srfEmissFunc::update_srfEmiss_data_from_file( ispec_srf.dataReader_, timestamp(), curr_month, *ispec_srf.horizInterp_, - ispec_srf.data_end_); // output + ispec_srf.data_end_); // output } //-------------------------------------------------------------------- @@ -238,15 +334,17 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- - preprocess_.initialize(constituent_fluxes_); + preprocess_.initialize(ncol_, nlev_, wet_atm_, dry_atm_, constituent_fluxes_); -} // end initialize_impl() +} // end initialize_impl() // ================================================================ // RUN_IMPL // ================================================================ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Zero-out output + // FIXME: Find out if we do it in the fortran code + // if we do, this should be a "Computed" field Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); // Gather time and state information for interpolation @@ -256,7 +354,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Interpolate srf emiss data //-------------------------------------------------------------------- - for (srf_emiss_ &ispec_srf : srf_emiss_species_) { + for(srf_emiss_ &ispec_srf : srf_emiss_species_) { // Update TimeState, note the addition of dt ispec_srf.timeState_.t_now = ts.frac_of_year_in_days(); @@ -279,7 +377,7 @@ 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_; + auto constituent_fluxes = this->constituent_fluxes_; const Real mfactor = amufac * mam4::gas_chemistry::adv_mass[species_index - offset_]; // Parallel loop over all the columns to update units @@ -289,7 +387,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor; constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); - } // for loop for species + } // for loop for species auto &online_data = online_emissions.online_emis_data; // TODO: check that units are consistent with srf emissions! @@ -310,13 +408,14 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // which is a chunk-wise variable at this point // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom // col-value once inside aero_model_emissions() - view_1d fluxes_col = Kokkos::subview(online_data.cfluxes, icol, Kokkos::ALL()); + view_1d fluxes_col = + Kokkos::subview(online_data.cfluxes, icol, Kokkos::ALL()); mam4::aero_model_emissions::aero_model_emissions(fluxes_col); }); // 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 Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); Kokkos::fence(); -} // run_impl ends +} // run_impl ends // ============================================================================= -} // namespace scream +} // 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 80c2efcccd7..97fbbda5aca 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 @@ -12,7 +12,6 @@ // For declaring surface and online emission class derived from atm process // class #include - #include namespace scream { @@ -20,19 +19,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_; @@ -40,11 +51,11 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { view_1d fluxes_in_mks_units_; // Unified atomic mass unit used for unit conversion (BAD constant) - static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu + static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu -public: + public: using srfEmissFunc = mam_coupling::srfEmissFunctions; - using onlineEmiss = mam_coupling::onlineEmissions; + using onlineEmiss = mam_coupling::onlineEmissions; // Constructor MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms); @@ -60,8 +71,8 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { std::string name() const { return "mam_srf_online_emissions"; } // grid - void - set_grids(const std::shared_ptr grids_manager) override; + void set_grids( + const std::shared_ptr grids_manager) override; // management of common atm process memory size_t requested_buffer_size_in_bytes() const override; @@ -81,14 +92,39 @@ 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) { + void initialize(const int ncol, const int nlev, + const mam_coupling::WetAtmosphere &wet_atm, + const mam_coupling::DryAtmosphere &dry_atm, + const view_2d &constituent_fluxes) { + ncol_pre_ = ncol; + nlev_pre_ = nlev; + wet_atm_pre_ = wet_atm; + dry_atm_pre_ = dry_atm; constituent_fluxes_pre_ = constituent_fluxes; } + KOKKOS_INLINE_FUNCTION + void operator()( + const Kokkos::TeamPolicy::member_type &team) const { + const int i = team.league_rank(); // column index + + compute_dry_mixing_ratios(team, wet_atm_pre_, dry_atm_pre_, i); + team.team_barrier(); + // vertical heights has to be computed after computing dry mixing ratios + // for atmosphere + compute_vertical_layer_heights(team, dry_atm_pre_, i); + compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, i); + } // 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_; -private: + // local atmospheric and aerosol state data + mam_coupling::WetAtmosphere wet_atm_pre_; + mam_coupling::DryAtmosphere dry_atm_pre_; + view_2d constituent_fluxes_pre_; + }; // MAMSrfOnlineEmiss::Preprocess + private: // preprocessing scratch pad Preprocess preprocess_; @@ -131,8 +167,8 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { static constexpr int offset_ = mam4::aero_model::pcnst - mam4::gas_chemistry::gas_pcnst; -}; // MAMSrfOnlineEmiss +}; // MAMSrfOnlineEmiss -} // namespace scream +} // namespace scream -#endif // EAMXX_MAM_SRF_ONLINE_EMISS_HPP +#endif // EAMXX_MAM_SRF_ONLINE_EMISS_HPP diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 7a3dfb43f22..b337ce3a503 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -69,6 +69,10 @@ initial_conditions: #we should get the following variables from other processes pbl_height : 1.0 + dstflx : [ -0.99391637990504811E-011, -0.53350997938896468E-010, -0.12510222703847123E-009, -0.11784238261270793E-009] + ocnfrc: 0.99999999999999989E+000 + sst: 0.30196620710190155E+003 + cldfrac_tot: 0.138584624960092 # The parameters for I/O control Scorpio: From 52afd72edeea8bc59545330623a551cca2fd17fd Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 28 Oct 2024 11:17:35 -0700 Subject: [PATCH 11/41] Commits changes in submodule MAM4xx --- ...and_online_emissions_process_interface.cpp | 76 +++++++++++-------- externals/mam4xx | 2 +- 2 files changed, 44 insertions(+), 34 deletions(-) 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 b495c6614ef..d2221690c5d 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 @@ -342,17 +342,43 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { // RUN_IMPL // ================================================================ void MAMSrfOnlineEmiss::run_impl(const double dt) { + 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(); + // Zero-out output // FIXME: Find out if we do it in the fortran code - // if we do, this should be a "Computed" field + // if we do, this should be a "Computed" field, + // ALSO this code should in the preprocessor struct now Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); - // Gather time and state information for interpolation - auto ts = timestamp() + dt; + //-------------------------------------------------------------------- + // Online emissions from dust and sea salt + //-------------------------------------------------------------------- + + const const_view_2d dstflx = get_field_in("dstflx").get_view(); + auto constituent_fluxes = this->constituent_fluxes_; + // TODO: check that units are consistent with srf emissions! + // copy current values to online-emissions-local version + // TODO: potentially combine with below parfor(icol) loop? + Kokkos::parallel_for( + "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); + mam4::aero_model_emissions::aero_model_emissions(dstflx, fluxes_col); + }); + // 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 + //Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); + //Kokkos::fence(); //-------------------------------------------------------------------- - // Interpolate srf emiss data + // Interpolate srf emiss data read in from emissions files //-------------------------------------------------------------------- + // Gather time and state information for interpolation + auto ts = timestamp() + dt; for(srf_emiss_ &ispec_srf : srf_emiss_species_) { // Update TimeState, note the addition of dt @@ -377,7 +403,6 @@ 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_]; // Parallel loop over all the columns to update units @@ -388,34 +413,19 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); } // for loop for species - - auto &online_data = online_emissions.online_emis_data; - // TODO: check that units are consistent with srf emissions! - // copy current values to online-emissions-local version - Kokkos::deep_copy(online_data.cfluxes, constituent_fluxes_); - // TODO: potentially combine with above parfor(icol) loop? - Kokkos::parallel_for( - "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - // NOTE: calling aero_model_emissions() this way hides the fact that - // some hard-coded data is initialized within mam4::aero_model_emissions - // some of these values definitely need to be read from here column-wise - // the structs.{values} holding the above are: - // SeasaltEmissionsData.{mpoly, mprot, mlip} - // DustEmissionsData.dust_dmt_vwr - // OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, - // v_bottom, z_bottom, ocean_frac} - // NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, - // which is a chunk-wise variable at this point - // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom - // col-value once inside aero_model_emissions() - view_1d fluxes_col = - Kokkos::subview(online_data.cfluxes, icol, Kokkos::ALL()); - mam4::aero_model_emissions::aero_model_emissions(fluxes_col); - }); - // 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 - Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); - Kokkos::fence(); } // run_impl ends // ============================================================================= } // namespace scream + +// NOTE: calling aero_model_emissions() this way hides the fact that +// some hard-coded data is initialized within mam4::aero_model_emissions +// some of these values definitely need to be read from here column-wise +// the structs.{values} holding the above are: +// SeasaltEmissionsData.{mpoly, mprot, mlip} +// DustEmissionsData.dust_dmt_vwr +// OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, +// v_bottom, z_bottom, ocean_frac} +// NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, +// which is a chunk-wise variable at this point +// (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom +// col-value once inside aero_model_emissions() \ No newline at end of file diff --git a/externals/mam4xx b/externals/mam4xx index 6bc2697f84b..c837e32f0f3 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 6bc2697f84b38503cc8fe638a6281ea107180e40 +Subproject commit c837e32f0f3ee63d2a7b50bd5bcf9e9b29845265 From 16a12172565d78664dc9eef5d6b90151e9a4b0dc Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 28 Oct 2024 18:07:49 -0700 Subject: [PATCH 12/41] EAMxx:Adds soil erodibility file read and sent to online emission read --- .../cime_config/namelist_defaults_scream.xml | 9 ++ ...and_online_emissions_process_interface.cpp | 48 +++++- ...and_online_emissions_process_interface.hpp | 5 + .../mam/readfiles/soil_erodibility.hpp | 57 +++++++ .../mam/readfiles/soil_erodibility_impl.hpp | 145 ++++++++++++++++++ .../single-process/mam/emissions/input.yaml | 2 + 6 files changed, 260 insertions(+), 6 deletions(-) create mode 100644 components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp create mode 100644 components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 195f46847ef..40211361376 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -371,7 +371,16 @@ be lost if SCREAM_HACK_XML is not enabled. ${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 +<<<<<<< HEAD +======= + + + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne30pg2_c20241028.nc + + + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne4pg2_c20241028.nc +>>>>>>> EAMxx:Adds soil erodibility file read and sent to online emission read ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/surface/DMSflux.2010.ne4pg2_conserv.POPmonthlyClimFromACES4BGC_c20240814.nc 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 d2221690c5d..f6ce5dc9f82 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,8 +1,13 @@ -//#include + #include +// For reading soil erodibility file +#include + namespace scream { +using soilErodibilityFunc = + soil_erodibility::soilErodibilityFunctions; // ================================================================ // Constructor // ================================================================ @@ -225,7 +230,27 @@ 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"); + // /compyfs/inputdata/atm/cam/dst/dst_1.9x2.5_c090203.nc + + // Field to be read from file + const std::string field_name = "mbl_bsn_fct_geo"; + + // Dimensions of the filed + const std::string dim_name1 = "ncol"; + + // initialize the file read + soilErodibilityFunc::init_soil_erodibility_file_read( + ncol_, field_name, dim_name1, grid_, soil_erodibility_data_file, + srf_map_file, horizInterp_, dataReader_); // output + } // set_grid ends // ================================================================ @@ -325,6 +350,15 @@ 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( + dataReader_, *horizInterp_, + soil_erodibility_); // output + //-------------------------------------------------------------------- // Initialize online emissions from file //-------------------------------------------------------------------- @@ -360,19 +394,21 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { //-------------------------------------------------------------------- const const_view_2d dstflx = get_field_in("dstflx").get_view(); - auto constituent_fluxes = this->constituent_fluxes_; + auto constituent_fluxes = this->constituent_fluxes_; + auto soil_erodibility = this->soil_erodibility_; // TODO: check that units are consistent with srf emissions! // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? Kokkos::parallel_for( "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); - mam4::aero_model_emissions::aero_model_emissions(dstflx, fluxes_col); + mam4::aero_model_emissions::aero_model_emissions( + dstflx, soil_erodibility, fluxes_col); }); // 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 - //Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); - //Kokkos::fence(); + // Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); + // Kokkos::fence(); //-------------------------------------------------------------------- // Interpolate srf emiss data read in from emissions files 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 97fbbda5aca..c1915cd81ba 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 @@ -53,6 +53,11 @@ 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 horizInterp_; + std::shared_ptr dataReader_; + const_view_1d soil_erodibility_; + public: using srfEmissFunc = mam_coupling::srfEmissFunctions; using onlineEmiss = mam_coupling::onlineEmissions; 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..9be7c0f817b --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp @@ -0,0 +1,57 @@ +#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; + + // ------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------- + + // Soil erodibility routines + 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_erodibilty +} // 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..2f6a5da6888 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp @@ -0,0 +1,145 @@ +#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/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index b337ce3a503..8a109337eec 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -22,6 +22,8 @@ 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 # FIXME: just set everything to a constant scalar for testing online_emis_IC_ncl_a1: 0.0 online_emis_IC_ncl_a2: 0.0 From 52f32c0015574e43572d19578e95ee5a3ed88a6d Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Tue, 29 Oct 2024 12:13:54 -0700 Subject: [PATCH 13/41] Changes needed to start validatioon with EAM --- ...and_online_emissions_process_interface.cpp | 31 ++++++++++++++++++- .../mam/emissions/CMakeLists.txt | 3 +- .../single-process/mam/emissions/input.yaml | 8 +++-- externals/mam4xx | 2 +- 4 files changed, 38 insertions(+), 6 deletions(-) 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 f6ce5dc9f82..5a327f4b1d4 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 @@ -6,14 +6,17 @@ 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 hfrom namelist?? /* Anything that can be initialized without grid information can be * initialized here. Like universal constants. */ @@ -47,6 +50,16 @@ void MAMSrfOnlineEmiss::set_grids( // For components of dust flux const FieldLayout vector4d = grid_->get_2d_vector_layout(4); + // FIXME: The following variables are used for validation ONLY!!! REMOVE + // THEM!!! + add_field("dstflx1", scalar2d, kg / m2 / s, grid_name); + add_field("dstflx2", scalar2d, kg / m2 / s, grid_name); + add_field("dstflx3", scalar2d, kg / m2 / s, grid_name); + add_field("dstflx4", scalar2d, kg / m2 / s, grid_name); + add_field("z_mid", scalar3d_m, kg / m2 / s, grid_name); + add_field("u_wind", scalar3d_m, kg / m2 / s, grid_name); + add_field("v_wind", scalar3d_m, kg / m2 / s, grid_name); + // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process // -------------------------------------------------------------------------- @@ -393,6 +406,20 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Online emissions from dust and sea salt //-------------------------------------------------------------------- + // FIXME: Remove the following vars as they are used only for validation + const const_view_2d z_mid = get_field_in("z_mid").get_view(); + const const_view_2d u_wind = get_field_in("u_wind").get_view(); + const const_view_2d v_wind = get_field_in("v_wind").get_view(); + const const_view_1d dstflx1 = + get_field_in("dstflx1").get_view(); + const const_view_1d dstflx2 = + get_field_in("dstflx2").get_view(); + const const_view_1d dstflx3 = + get_field_in("dstflx3").get_view(); + const const_view_1d dstflx4 = + get_field_in("dstflx4").get_view(); + // FIXME: Remove ^^^^^^ + const const_view_2d dstflx = get_field_in("dstflx").get_view(); auto constituent_fluxes = this->constituent_fluxes_; auto soil_erodibility = this->soil_erodibility_; @@ -400,11 +427,13 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? Kokkos::parallel_for( - "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + // "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { + "online_emis_fluxes", 1, KOKKOS_LAMBDA(int icol) { view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); mam4::aero_model_emissions::aero_model_emissions( dstflx, soil_erodibility, fluxes_col); }); + // 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 // Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); diff --git a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt index c8e690b929f..8191338fb66 100644 --- a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt +++ b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt @@ -13,7 +13,8 @@ CreateADUnitTest(${TEST_BASE_NAME} # Set AD configurable options set (ATM_TIME_STEP 1800) -SetVarDependingOnTestSize(NUM_STEPS 2 5 48) # 1h 2.5h 24h +#SetVarDependingOnTestSize(NUM_STEPS 2 5 48) # 1h 2.5h 24h +SetVarDependingOnTestSize(NUM_STEPS 1 1 1) set (RUN_T0 2021-10-12-45000) ## Copy (and configure) yaml files needed by tests diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 8a109337eec..48a658ab151 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -64,17 +64,19 @@ grids_manager: initial_conditions: # The name of the file containing the initial conditions for this test. - Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} + #Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} + Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_81p6352444000824_LON_44p7676953136545_ONLINE_EMISS_1st.nc 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 pbl_height : 1.0 - dstflx : [ -0.99391637990504811E-011, -0.53350997938896468E-010, -0.12510222703847123E-009, -0.11784238261270793E-009] + dstflx : [-0.21955382571753977E-009, -0.11785111846584436E-008, -0.27634792128795150E-008, -0.26031109314006694E-008] ocnfrc: 0.99999999999999989E+000 - sst: 0.30196620710190155E+003 + sst: 0.27286547802237800E+003 cldfrac_tot: 0.138584624960092 + horiz_winds: [-0.63549092155149021E+001, 0.10220040376206645E+001] # The parameters for I/O control Scorpio: diff --git a/externals/mam4xx b/externals/mam4xx index c837e32f0f3..16f1ab9d97c 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit c837e32f0f3ee63d2a7b50bd5bcf9e9b29845265 +Subproject commit 16f1ab9d97c10faa0bbd50755b7d1e08506aa01a From dd258dff26f9e213ea79fd9de3fcef609e78fbd3 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Tue, 29 Oct 2024 18:13:25 -0700 Subject: [PATCH 14/41] EAMxx: dust emissions matches EAMvs grdi cell --- ...and_online_emissions_process_interface.cpp | 45 +++++++++++++++++-- .../single-process/mam/emissions/input.yaml | 10 ++--- 2 files changed, 46 insertions(+), 9 deletions(-) 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 5a327f4b1d4..c82665361b1 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 @@ -115,10 +115,14 @@ void MAMSrfOnlineEmiss::set_grids( // 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 eacch column + // dust fluxes [kg/m^2/s]: Four flux values for each column add_field("dstflx", vector4d, kg / m2 / s, grid_name); // ------------------------------------------------------------- @@ -420,18 +424,51 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { get_field_in("dstflx4").get_view(); // FIXME: Remove ^^^^^^ + // dust fluxes [kg/m^2/s]: Four flux values for each column const const_view_2d dstflx = get_field_in("dstflx").get_view(); - auto constituent_fluxes = this->constituent_fluxes_; - auto soil_erodibility = this->soil_erodibility_; + + // 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_bottom = + get_field_in("horiz_winds").get_component(0).get_view(); + + // V wind component [m/s] + const const_view_2d v_bottom = + get_field_in("horiz_winds").get_component(1).get_view(); + + // Constituent fluxes [kg/m^2/s] + auto constituent_fluxes = this->constituent_fluxes_; + + // Soil edodibility [fraction] + auto soil_erodibility = this->soil_erodibility_; + // TODO: check that units are consistent with srf emissions! // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? + const int surf_lev = nlev_ - 1; + Kokkos::parallel_for( // "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { "online_emis_fluxes", 1, KOKKOS_LAMBDA(int icol) { + // gather all input + const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); + const const_view_1d u_bottom_icol = ekat::subview(u_bottom, icol); + const const_view_1d v_bottom_icol = ekat::subview(v_bottom, icol); + + // output view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); + + // comput online emissions mam4::aero_model_emissions::aero_model_emissions( - dstflx, soil_erodibility, fluxes_col); + sst(icol), ocnfrac(icol), u_bottom(icol, surf_lev), + v_bottom(icol, surf_lev), dstflx_icol, soil_erodibility(icol), + fluxes_col); }); // NOTE: mam4::aero_model_emissions calculates mass and number emission fluxes diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 48a658ab151..3f41c3d9180 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -65,18 +65,18 @@ grids_manager: initial_conditions: # The name of the file containing the initial conditions for this test. #Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} - Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_81p6352444000824_LON_44p7676953136545_ONLINE_EMISS_1st.nc + Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_39p0553303768561_LON_83p8339931334693_ONLINE_EMISS_1st.nc 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 pbl_height : 1.0 - dstflx : [-0.21955382571753977E-009, -0.11785111846584436E-008, -0.27634792128795150E-008, -0.26031109314006694E-008] - ocnfrc: 0.99999999999999989E+000 - sst: 0.27286547802237800E+003 + dstflx : [ -0.15639291491198312E-010, -0.83947887868839890E-010, -0.19684857140996447E-009, -0.18542519360366344E-009] + ocnfrac: 0.0 + sst: 0.0 cldfrac_tot: 0.138584624960092 - horiz_winds: [-0.63549092155149021E+001, 0.10220040376206645E+001] + horiz_winds: [0.70975328186226694E+001, 0.83157735469095362E+000] # The parameters for I/O control Scorpio: From 9e0ad96960f1fe94a944d61e0d7e57c03ca29028 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Tue, 29 Oct 2024 19:09:14 -0700 Subject: [PATCH 15/41] EAMxx:calculate_seasalt_numflux_in_bins validated --- ...and_online_emissions_process_interface.cpp | 30 ++++++++----------- externals/mam4xx | 2 +- 2 files changed, 14 insertions(+), 18 deletions(-) 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 c82665361b1..ff239861dd4 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 @@ -57,9 +57,6 @@ void MAMSrfOnlineEmiss::set_grids( add_field("dstflx3", scalar2d, kg / m2 / s, grid_name); add_field("dstflx4", scalar2d, kg / m2 / s, grid_name); add_field("z_mid", scalar3d_m, kg / m2 / s, grid_name); - add_field("u_wind", scalar3d_m, kg / m2 / s, grid_name); - add_field("v_wind", scalar3d_m, kg / m2 / s, grid_name); - // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process // -------------------------------------------------------------------------- @@ -411,9 +408,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { //-------------------------------------------------------------------- // FIXME: Remove the following vars as they are used only for validation - const const_view_2d z_mid = get_field_in("z_mid").get_view(); - const const_view_2d u_wind = get_field_in("u_wind").get_view(); - const const_view_2d v_wind = get_field_in("v_wind").get_view(); + const const_view_2d z_mid2 = get_field_in("z_mid").get_view(); const const_view_1d dstflx1 = get_field_in("dstflx1").get_view(); const const_view_1d dstflx2 = @@ -435,11 +430,11 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { const const_view_1d sst = get_field_in("sst").get_view(); // U wind component [m/s] - const const_view_2d u_bottom = + 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_bottom = + const const_view_2d v_wind = get_field_in("horiz_winds").get_component(1).get_view(); // Constituent fluxes [kg/m^2/s] @@ -448,6 +443,9 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Soil edodibility [fraction] auto soil_erodibility = this->soil_erodibility_; + // Vertical layer height at midpoints + const const_view_2d z_mid = dry_atm_.z_mid; + // TODO: check that units are consistent with srf emissions! // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? @@ -456,19 +454,17 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { Kokkos::parallel_for( // "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { "online_emis_fluxes", 1, KOKKOS_LAMBDA(int icol) { - // gather all input - const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); - const const_view_1d u_bottom_icol = ekat::subview(u_bottom, icol); - const const_view_1d v_bottom_icol = ekat::subview(v_bottom, icol); + // input + const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); // output view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); // comput online emissions mam4::aero_model_emissions::aero_model_emissions( - sst(icol), ocnfrac(icol), u_bottom(icol, surf_lev), - v_bottom(icol, surf_lev), dstflx_icol, soil_erodibility(icol), - fluxes_col); + sst(icol), ocnfrac(icol), u_wind(icol, surf_lev), + v_wind(icol, surf_lev), z_mid(icol, surf_lev), dstflx_icol, + soil_erodibility(icol), fluxes_col); }); // NOTE: mam4::aero_model_emissions calculates mass and number emission fluxes @@ -525,8 +521,8 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // the structs.{values} holding the above are: // SeasaltEmissionsData.{mpoly, mprot, mlip} // DustEmissionsData.dust_dmt_vwr -// OnlineEmissionsData.{dust_flux_in, surface_temp, u_bottom, -// v_bottom, z_bottom, ocean_frac} +// OnlineEmissionsData.{dust_flux_in, surface_temp, u_wind, +// v_wind, z_bottom, ocean_frac} // NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, // which is a chunk-wise variable at this point // (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom diff --git a/externals/mam4xx b/externals/mam4xx index 16f1ab9d97c..328abdcc7fb 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 16f1ab9d97c10faa0bbd50755b7d1e08506aa01a +Subproject commit 328abdcc7fba6ca9f122cb204da2773b1e7792cf From a7d47e949f778efd34e28f2650af84a97c0563b9 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 11:17:01 -0700 Subject: [PATCH 16/41] EAMxx: marine_organic_emis validated with hardwired file read mom --- ...and_online_emissions_process_interface.cpp | 39 +++++++++++-------- .../single-process/mam/emissions/input.yaml | 10 ++--- externals/mam4xx | 2 +- 3 files changed, 29 insertions(+), 22 deletions(-) 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 ff239861dd4..75524dbdc53 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 @@ -52,10 +52,6 @@ void MAMSrfOnlineEmiss::set_grids( // FIXME: The following variables are used for validation ONLY!!! REMOVE // THEM!!! - add_field("dstflx1", scalar2d, kg / m2 / s, grid_name); - add_field("dstflx2", scalar2d, kg / m2 / s, grid_name); - add_field("dstflx3", scalar2d, kg / m2 / s, grid_name); - add_field("dstflx4", scalar2d, kg / m2 / s, grid_name); add_field("z_mid", scalar3d_m, kg / m2 / s, grid_name); // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process @@ -252,18 +248,37 @@ void MAMSrfOnlineEmiss::set_grids( const std::string soil_erodibility_data_file = m_params.get("soil_erodibility_file"); - // /compyfs/inputdata/atm/cam/dst/dst_1.9x2.5_c090203.nc // Field to be read from file - const std::string field_name = "mbl_bsn_fct_geo"; + const std::string soil_erod_fld_name = "mbl_bsn_fct_geo"; // Dimensions of the filed - const std::string dim_name1 = "ncol"; + const std::string soil_erod_dname = "ncol"; // initialize the file read soilErodibilityFunc::init_soil_erodibility_file_read( - ncol_, field_name, dim_name1, grid_, soil_erodibility_data_file, + ncol_, soil_erod_fld_name, soil_erod_dname, grid_, + soil_erodibility_data_file, srf_map_file, horizInterp_, + dataReader_); // output + // ------------------------------------------------------------- + // setup to enable reading marine organics file + // ------------------------------------------------------------- +#if 0 + const std::string marine_organics_data_file = + m_params.get("marine_organics_file"); + + // Field to be read from file + const std::vector marine_org_fld_name = { + "CHL1", "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; + + // Dimensions of the filed + const std::string marine_org_dname = "ncol"; + + // initialize the file read + soilErodibilityFunc::init_soil_erodibility_file_read( + ncol_, marine_org_fld_name, marine_org_dname, grid_, marine_organics_data_file, srf_map_file, horizInterp_, dataReader_); // output +#endif } // set_grid ends @@ -409,14 +424,6 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // FIXME: Remove the following vars as they are used only for validation const const_view_2d z_mid2 = get_field_in("z_mid").get_view(); - const const_view_1d dstflx1 = - get_field_in("dstflx1").get_view(); - const const_view_1d dstflx2 = - get_field_in("dstflx2").get_view(); - const const_view_1d dstflx3 = - get_field_in("dstflx3").get_view(); - const const_view_1d dstflx4 = - get_field_in("dstflx4").get_view(); // FIXME: Remove ^^^^^^ // dust fluxes [kg/m^2/s]: Four flux values for each column diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 3f41c3d9180..b8b97e11185 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -65,18 +65,18 @@ grids_manager: initial_conditions: # The name of the file containing the initial conditions for this test. #Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} - Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_39p0553303768561_LON_83p8339931334693_ONLINE_EMISS_1st.nc + Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_m5p57727434969464_LON_83p9312243922771_ONLINE_EMISS_1st.nc 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 pbl_height : 1.0 - dstflx : [ -0.15639291491198312E-010, -0.83947887868839890E-010, -0.19684857140996447E-009, -0.18542519360366344E-009] - ocnfrac: 0.0 - sst: 0.0 + dstflx : [ 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000] + ocnfrac: 0.10000000000000000E+001 + sst: 0.30178553874977507E+003 cldfrac_tot: 0.138584624960092 - horiz_winds: [0.70975328186226694E+001, 0.83157735469095362E+000] + horiz_winds: [-0.24988988196194634E+000, -0.23959782871450760E+000] # The parameters for I/O control Scorpio: diff --git a/externals/mam4xx b/externals/mam4xx index 328abdcc7fb..4f6259f61ae 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 328abdcc7fba6ca9f122cb204da2773b1e7792cf +Subproject commit 4f6259f61aeda3814df542e541a7a48de35e255e From 059466761743ff86359241a83d02da13897e7c49 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 17:05:37 -0700 Subject: [PATCH 17/41] EAMxx: Partial implementation of marine orgaics file read --- .../cime_config/namelist_defaults_scream.xml | 16 +- ...and_online_emissions_process_interface.cpp | 21 ++- ...and_online_emissions_process_interface.hpp | 17 +- .../physics/mam/readfiles/marine_organics.hpp | 111 ++++++++++++ .../mam/readfiles/marine_organics_impl.hpp | 169 ++++++++++++++++++ .../mam/readfiles/soil_erodibility.hpp | 2 +- .../mam/readfiles/soil_erodibility_impl.hpp | 34 ++-- .../mam/emissions/CMakeLists.txt | 2 + .../single-process/mam/emissions/input.yaml | 1 + 9 files changed, 338 insertions(+), 35 deletions(-) create mode 100644 components/eamxx/src/physics/mam/readfiles/marine_organics.hpp create mode 100644 components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 40211361376..8b3a1131d2a 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -382,16 +382,10 @@ be lost if SCREAM_HACK_XML is not enabled. ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne4pg2_c20241028.nc >>>>>>> EAMxx:Adds soil erodibility file read and sent to online emission read - - ${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 @@ -618,6 +612,7 @@ be lost if SCREAM_HACK_XML is not enabled. ${DIN_LOC_ROOT}/atm/cam/topo/USGS-gtopo30_CA_ne32_x32_v1_pg2_16xdel2.nc + ${CASE}.scream 0.0 0.0 @@ -703,6 +698,7 @@ be lost if SCREAM_HACK_XML is not enabled. + ./${CASE}.scream default ${REST_N} 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 75524dbdc53..a7514c7ee5d 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 @@ -258,27 +258,30 @@ void MAMSrfOnlineEmiss::set_grids( // 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, horizInterp_, - dataReader_); // output + soil_erodibility_data_file, srf_map_file, serod_horizInterp_, + serod_dataReader_); // output // ------------------------------------------------------------- // setup to enable reading marine organics file // ------------------------------------------------------------- -#if 0 + const std::string marine_organics_data_file = m_params.get("marine_organics_file"); + // /compyfs/inputdata/atm/cam/chem/trop_mam/marine_BGC/monthly_macromolecules_0.1deg_bilinear_latlon_year01_merge_date.nc // Field to be read from file const std::vector marine_org_fld_name = { - "CHL1", "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; + "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; // Dimensions of the filed const std::string marine_org_dname = "ncol"; // initialize the file read - soilErodibilityFunc::init_soil_erodibility_file_read( - ncol_, marine_org_fld_name, marine_org_dname, grid_, marine_organics_data_file, - srf_map_file, horizInterp_, dataReader_); // output -#endif + 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 @@ -385,7 +388,7 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { // This data is time-independent, we read all data here for the // entire simulation soilErodibilityFunc::update_soil_erodibility_data_from_file( - dataReader_, *horizInterp_, + serod_dataReader_, *serod_horizInterp_, soil_erodibility_); // output //-------------------------------------------------------------------- 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 c1915cd81ba..fb162ee94e4 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 @@ -9,6 +9,9 @@ #include #include +// For reading marine organics file +#include + // For declaring surface and online emission class derived from atm process // class #include @@ -54,13 +57,16 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { static constexpr Real amufac = 1.65979e-23; // 1.e4* kg / amu // For reading soil erodibility file - std::shared_ptr horizInterp_; - std::shared_ptr dataReader_; + std::shared_ptr serod_horizInterp_; + std::shared_ptr serod_dataReader_; const_view_1d soil_erodibility_; public: using srfEmissFunc = mam_coupling::srfEmissFunctions; using onlineEmiss = mam_coupling::onlineEmissions; + // For reading marine organics file + using marineOrganicsFunc = + marine_organics::marineOrganicsFunctions; // Constructor MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms); @@ -168,6 +174,13 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { onlineEmiss online_emissions; + // 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..0c472fdb44c --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp @@ -0,0 +1,111 @@ +#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 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 nsectors_) + : ncols(ncol_), nsectors(nsectors_) { + 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 nsectors_) { + init(ncols_, nsectors_); + } + + void init(const int ncols_, const int nsectors_) { + data.init(ncols_, nsectors_, 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); + + // ------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------- +#if 0 + static void update_marine_organics_data_from_file( + std::shared_ptr &scorpio_reader, + AbstractRemapper &horiz_interp, const_view_1d &input); +#endif + // ------------------------------------------------------------------------------------------- + // ------------------------------------------------------------------------------------------- + + 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..ba97affafd5 --- /dev/null +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -0,0 +1,169 @@ +#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); + + // 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("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(); + const auto nondim = ekat::units::Units::nondimensional(); + + 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, nondim, 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 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 + +// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------- +#if 0 +template +void marineOrganicsFunctions::update_marine_organics_data_from_file( + std::shared_ptr &scorpio_reader, + AbstractRemapper &horiz_interp, const_view_1d &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 + input = horiz_interp.get_tgt_field(0).get_view(); + 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 +#endif +// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------- + +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, 1, 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 index 9be7c0f817b..223fb78cfc1 100644 --- a/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp @@ -50,7 +50,7 @@ struct soilErodibilityFunctions { }; // struct soilErodilityFunctions -} // namespace soil_erodibilty +} // namespace soil_erodibility } // namespace scream #endif // SOIL_ERODIBILITY_HPP diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp index 2f6a5da6888..a685134419f 100644 --- a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp @@ -15,11 +15,10 @@ 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); + const int ncols_data = scorpio::get_dimlen(data_file, dim_name1); scorpio::release_file(data_file); @@ -44,7 +43,8 @@ soilErodibilityFunctions::create_horiz_remapper( 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."); + " but remap file is missing from soil erodibility " + "parameter list."); remapper = std::make_shared(horiz_interp_tgt_grid, map_file); @@ -73,7 +73,8 @@ soilErodibilityFunctions::create_horiz_remapper( // ------------------------------------------------------------------------------------------- template -std::shared_ptr soilErodibilityFunctions::create_data_reader( +std::shared_ptr +soilErodibilityFunctions::create_data_reader( const std::shared_ptr &horiz_remapper, const std::string &data_file) { std::vector io_fields; @@ -95,27 +96,33 @@ void soilErodibilityFunctions::update_soil_erodibility_data_from_file( // 1. Read from file start_timer( - "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::read_data"); + "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"); + "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) + // 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"); + "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"); + "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"); + "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"); + "EAMxx::soilErodibility::update_soil_erodibility_data_from_file::get_" + "field"); stop_timer("EAMxx::soilErodibility::update_soil_erodibility_data_from_file"); @@ -137,7 +144,8 @@ void soilErodibilityFunctions::init_soil_erodibility_file_read( grid, data_file, mapping_file, field_name, dim_name1); // Create reader (an AtmosphereInput object) - soilErodibilityDataReader = create_data_reader(soilErodibilityHorizInterp, data_file); + soilErodibilityDataReader = + create_data_reader(soilErodibilityHorizInterp, data_file); } // init_soil_erodibility_file_read } // namespace soil_erodibility } // namespace scream diff --git a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt index 8191338fb66..4b7350a1bb9 100644 --- a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt +++ b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt @@ -37,6 +37,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 b8b97e11185..ce103765b70 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -24,6 +24,7 @@ atmosphere_processes: 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 # FIXME: just set everything to a constant scalar for testing online_emis_IC_ncl_a1: 0.0 online_emis_IC_ncl_a2: 0.0 From 2f0d6c8c84d913556b9277dab038e154c1d79bbb Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 20:10:28 -0700 Subject: [PATCH 18/41] EAMxx:Reading and interpolation of mariane organic file works, not tested yet --- ...and_online_emissions_process_interface.cpp | 31 +++- .../physics/mam/readfiles/marine_organics.hpp | 68 +++++--- .../mam/readfiles/marine_organics_impl.hpp | 150 ++++++++++++++++-- 3 files changed, 217 insertions(+), 32 deletions(-) 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 a7514c7ee5d..cdabc48a7d9 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 @@ -260,10 +260,10 @@ void MAMSrfOnlineEmiss::set_grids( 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"); // /compyfs/inputdata/atm/cam/chem/trop_mam/marine_BGC/monthly_macromolecules_0.1deg_bilinear_latlon_year01_merge_date.nc @@ -282,6 +282,7 @@ void MAMSrfOnlineEmiss::set_grids( // output morg_horizInterp_, morg_data_start_, morg_data_end_, morg_data_out_, morg_dataReader_); + printf("END SIZE:%i,%i\n", morg_data_end_.data.ncols, ncol_); } // set_grid ends @@ -391,6 +392,14 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { serod_dataReader_, *serod_horizInterp_, soil_erodibility_); // output + //-------------------------------------------------------------------- + // Update marine orgaincs from file + //-------------------------------------------------------------------- + + marineOrganicsFunc::update_marine_organics_data_from_file( + morg_dataReader_, timestamp(), curr_month, *morg_horizInterp_, + morg_data_end_); // output + //-------------------------------------------------------------------- // Initialize online emissions from file //-------------------------------------------------------------------- @@ -421,10 +430,28 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // ALSO this code should in the preprocessor struct now Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); + // Gather time and state information for interpolation + const auto ts = timestamp() + dt; + //-------------------------------------------------------------------- // Online emissions from dust and sea salt //-------------------------------------------------------------------- + // --- Interpolate mariane 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 marineOrganics routine to get interpolated aerosol forcings. + marineOrganicsFunc::marineOrganics_main(morg_timeState_, morg_data_start_, + morg_data_end_, morg_data_out_); + // FIXME: Remove the following vars as they are used only for validation const const_view_2d z_mid2 = get_field_in("z_mid").get_view(); // FIXME: Remove ^^^^^^ @@ -486,7 +513,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Interpolate srf emiss data read in from emissions files //-------------------------------------------------------------------- // Gather time and state information for interpolation - auto ts = timestamp() + dt; + // auto ts = timestamp() + dt; for(srf_emiss_ &ispec_srf : srf_emiss_species_) { // Update TimeState, note the addition of dt diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp index 0c472fdb44c..92136d315f5 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp @@ -11,9 +11,12 @@ template struct marineOrganicsFunctions { using Device = DeviceType; - using KT = KokkosTypes; - using view_2d = typename KT::template view_2d; + 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. @@ -31,12 +34,12 @@ struct marineOrganicsFunctions { struct marineOrganicsData { marineOrganicsData() = default; - marineOrganicsData(const int ncol_, const int nsectors_) - : ncols(ncol_), nsectors(nsectors_) { + 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) { + void init(const int &ncol, const int &nsector, const bool allocate) { ncols = ncol; nsectors = nsector; if(allocate) emiss_sectors = view_2d("morgAllSectors", nsectors, ncols); @@ -51,12 +54,12 @@ struct marineOrganicsFunctions { // ------------------------------------------------------------------------------------------- struct marineOrganicsInput { marineOrganicsInput() = default; - marineOrganicsInput(const int ncols_, const int nsectors_) { - init(ncols_, nsectors_); + marineOrganicsInput(const int &ncols_, const int &nfields_) { + init(ncols_, nfields_); } - void init(const int ncols_, const int nsectors_) { - data.init(ncols_, nsectors_, true); + void init(const int &ncols_, const int &nfields_) { + data.init(ncols_, nfields_, true); } marineOrganicsData data; // All marineOrganics fields }; // marineOrganicsInput @@ -68,7 +71,6 @@ struct marineOrganicsFunctions { // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- - static std::shared_ptr create_horiz_remapper( const std::shared_ptr &model_grid, const std::string &marineOrganics_data_file, const std::string &map_file, @@ -76,30 +78,60 @@ struct marineOrganicsFunctions { // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- - static std::shared_ptr create_data_reader( const std::shared_ptr &horiz_remapper, const std::string &data_file); // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- -#if 0 static void update_marine_organics_data_from_file( std::shared_ptr &scorpio_reader, - AbstractRemapper &horiz_interp, const_view_1d &input); -#endif + 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 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_, + marineOrganicsInput &morg_data_start_, + marineOrganicsInput &morg_data_end_, marineOrganicsData &morg_data_out_, std::shared_ptr &marineOrganicsDataReader); }; // struct marineOrganicsFunctions diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp index ba97affafd5..8f532d5342a 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -22,9 +22,8 @@ marineOrganicsFunctions::create_horiz_remapper( 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) + // 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); @@ -93,11 +92,11 @@ marineOrganicsFunctions::create_data_reader( // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- -#if 0 template void marineOrganicsFunctions::update_marine_organics_data_from_file( - std::shared_ptr &scorpio_reader, - AbstractRemapper &horiz_interp, const_view_1d &input) { + 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 @@ -125,7 +124,9 @@ void marineOrganicsFunctions::update_marine_organics_data_from_file( "field"); // Recall, the fields are registered in the order: // Read the field from the file +#if 0 input = horiz_interp.get_tgt_field(0).get_view(); +#endif stop_timer( "EAMxx::marineOrganics::update_marine_organics_data_from_file::get_" "field"); @@ -133,20 +134,145 @@ void marineOrganicsFunctions::update_marine_organics_data_from_file( stop_timer("EAMxx::marineOrganics::update_marine_organics_data_from_file"); } // END update_marine_organics_data_from_file -#endif + +// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------- +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 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_, + marineOrganicsInput &data_start_, marineOrganicsInput &data_end_, + marineOrganicsData &data_out_, std::shared_ptr &marineOrganicsDataReader) { // Init horizontal remap @@ -156,7 +282,7 @@ void marineOrganicsFunctions::init_marine_organics_file_read( // 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, 1, true); + data_out_.init(ncol, field_name.size(), true); // Create reader (an AtmosphereInput object) marineOrganicsDataReader = From 82e843b479ea22cae32755fb25c61aa1ce43c2ac Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 20:42:03 -0700 Subject: [PATCH 19/41] EAMxx: Connects marine organics emiss with MAM4xx codes --- ...am_srf_and_online_emissions_process_interface.cpp | 12 +++++++++--- externals/mam4xx | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) 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 cdabc48a7d9..4b3a7470d1f 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 @@ -268,7 +268,7 @@ void MAMSrfOnlineEmiss::set_grids( m_params.get("marine_organics_file"); // /compyfs/inputdata/atm/cam/chem/trop_mam/marine_BGC/monthly_macromolecules_0.1deg_bilinear_latlon_year01_merge_date.nc - // Field to be read from file + // Field to be read from file (order matters as they are read in same order) const std::vector marine_org_fld_name = { "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; @@ -282,7 +282,6 @@ void MAMSrfOnlineEmiss::set_grids( // output morg_horizInterp_, morg_data_start_, morg_data_end_, morg_data_out_, morg_dataReader_); - printf("END SIZE:%i,%i\n", morg_data_end_.data.ncols, ncol_); } // set_grid ends @@ -452,6 +451,11 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { marineOrganicsFunc::marineOrganics_main(morg_timeState_, morg_data_start_, morg_data_end_, morg_data_out_); + // Marine organics emission data read from the file + 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); + // FIXME: Remove the following vars as they are used only for validation const const_view_2d z_mid2 = get_field_in("z_mid").get_view(); // FIXME: Remove ^^^^^^ @@ -501,7 +505,9 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { 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), fluxes_col); + soil_erodibility(icol), mpoly(icol), mprot(icol), mlip(icol), + // out + fluxes_col); }); // NOTE: mam4::aero_model_emissions calculates mass and number emission fluxes diff --git a/externals/mam4xx b/externals/mam4xx index 4f6259f61ae..7d6a52bec71 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 4f6259f61aeda3814df542e541a7a48de35e255e +Subproject commit 7d6a52bec71003de7da14433db1577042ff77218 From 5d8341f6402620be7b01347e7e91756ea17ce254 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 21:18:57 -0700 Subject: [PATCH 20/41] EAMxx:Fixes units for marine organic emissions --- ...and_online_emissions_process_interface.cpp | 41 ++++++------------- ...and_online_emissions_process_interface.hpp | 2 - .../mam/readfiles/marine_organics_impl.hpp | 6 ++- 3 files changed, 16 insertions(+), 33 deletions(-) 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 4b3a7470d1f..a67ddf61604 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 @@ -50,9 +50,6 @@ void MAMSrfOnlineEmiss::set_grids( // For components of dust flux const FieldLayout vector4d = grid_->get_2d_vector_layout(4); - // FIXME: The following variables are used for validation ONLY!!! REMOVE - // THEM!!! - add_field("z_mid", scalar3d_m, kg / m2 / s, grid_name); // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process // -------------------------------------------------------------------------- @@ -79,8 +76,7 @@ void MAMSrfOnlineEmiss::set_grids( // Temperature[K] at midpoints add_field("T_mid", scalar3d_m, K, grid_name); - // Vertical pressure velocity [Pa/s] at midpoints (Require only for building - // DS) + // Vertical pressure velocity [Pa/s] at midpoints add_field("omega", scalar3d_m, Pa / s, grid_name); // Total pressure [Pa] at midpoints @@ -266,9 +262,9 @@ void MAMSrfOnlineEmiss::set_grids( // ------------------------------------------------------------- const std::string marine_organics_data_file = m_params.get("marine_organics_file"); - // /compyfs/inputdata/atm/cam/chem/trop_mam/marine_BGC/monthly_macromolecules_0.1deg_bilinear_latlon_year01_merge_date.nc - // Field to be read from file (order matters as they are read in same order) + // Field 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"}; @@ -394,17 +390,11 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //-------------------------------------------------------------------- // 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 - //-------------------------------------------------------------------- - // Initialize online emissions from file - //-------------------------------------------------------------------- - online_emissions.online_emis_data.init(ncol_); - online_emissions.init_from_input_file(m_params); - //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- @@ -436,7 +426,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Online emissions from dust and sea salt //-------------------------------------------------------------------- - // --- Interpolate mariane organics data -- + // --- Interpolate marine organics data -- // Update TimeState, note the addition of dt morg_timeState_.t_now = ts.frac_of_year_in_days(); @@ -447,22 +437,15 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // output morg_timeState_, morg_data_start_, morg_data_end_); - // Call the main marineOrganics routine to get interpolated aerosol forcings. + // 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 + // 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); - // FIXME: Remove the following vars as they are used only for validation - const const_view_2d z_mid2 = get_field_in("z_mid").get_view(); - // FIXME: Remove ^^^^^^ - - // dust fluxes [kg/m^2/s]: Four flux values for each column - const const_view_2d dstflx = get_field_in("dstflx").get_view(); - // Ocean fraction [unitless] const const_view_1d ocnfrac = get_field_in("ocnfrac").get_view(); @@ -478,6 +461,9 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { 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(); + // Constituent fluxes [kg/m^2/s] auto constituent_fluxes = this->constituent_fluxes_; @@ -490,11 +476,10 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // TODO: check that units are consistent with srf emissions! // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? - const int surf_lev = nlev_ - 1; + const int surf_lev = nlev_ - 1; // surface level Kokkos::parallel_for( - // "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - "online_emis_fluxes", 1, KOKKOS_LAMBDA(int icol) { + "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { // input const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); @@ -518,8 +503,6 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { //-------------------------------------------------------------------- // Interpolate srf emiss data read in from emissions files //-------------------------------------------------------------------- - // Gather time and state information for interpolation - // auto ts = timestamp() + dt; for(srf_emiss_ &ispec_srf : srf_emiss_species_) { // Update TimeState, note the addition of dt 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 fb162ee94e4..16cf6dcb74a 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 @@ -172,8 +172,6 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // A vector for carrying emissions for all the species std::vector srf_emiss_species_; - onlineEmiss online_emissions; - // For reading marine organics file std::shared_ptr morg_horizInterp_; std::shared_ptr morg_dataReader_; diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp index 8f532d5342a..100b432cb74 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -54,7 +54,9 @@ marineOrganicsFunctions::create_horiz_remapper( 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(); + using namespace ekat::units; + using namespace ekat::prefixes; + Units umolC(micro * mol, "umol C"); std::vector fields_vector; @@ -62,7 +64,7 @@ marineOrganicsFunctions::create_horiz_remapper( 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, nondim, tgt_grid->name())); + 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); From c681f075a5c5677417c4b7986a3722c1cee6caf9 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 21:58:08 -0700 Subject: [PATCH 21/41] EAMxx: Adds logic to zero out constituent fluxes, cleanup --- ...and_online_emissions_process_interface.cpp | 39 ++++--------------- ...and_online_emissions_process_interface.hpp | 21 +++++++--- externals/mam4xx | 2 +- 3 files changed, 25 insertions(+), 37 deletions(-) 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 a67ddf61604..1d931435d06 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 @@ -413,12 +413,6 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { Kokkos::parallel_for("preprocess", scan_policy, preprocess_); Kokkos::fence(); - // Zero-out output - // FIXME: Find out if we do it in the fortran code - // if we do, this should be a "Computed" field, - // ALSO this code should in the preprocessor struct now - Kokkos::deep_copy(preprocess_.constituent_fluxes_pre_, 0); - // Gather time and state information for interpolation const auto ts = timestamp() + dt; @@ -468,25 +462,25 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { auto constituent_fluxes = this->constituent_fluxes_; // Soil edodibility [fraction] - auto soil_erodibility = this->soil_erodibility_; + const const_view_1d soil_erodibility = this->soil_erodibility_; // Vertical layer height at midpoints const const_view_2d z_mid = dry_atm_.z_mid; - // TODO: check that units are consistent with srf emissions! - // copy current values to online-emissions-local version // TODO: potentially combine with below parfor(icol) loop? const int surf_lev = nlev_ - 1; // surface level Kokkos::parallel_for( "online_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - // input + // Input const const_view_1d dstflx_icol = ekat::subview(dstflx, icol); - // output + // Output view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); - // comput online emissions + // Comput 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, @@ -494,11 +488,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // out fluxes_col); }); - - // 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 - // Kokkos::deep_copy(constituent_fluxes_, online_data.cfluxes); - // Kokkos::fence(); + Kokkos::fence(); //-------------------------------------------------------------------- // Interpolate srf emiss data read in from emissions files @@ -539,17 +529,4 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { } // for loop for species } // run_impl ends // ============================================================================= -} // namespace scream - -// NOTE: calling aero_model_emissions() this way hides the fact that -// some hard-coded data is initialized within mam4::aero_model_emissions -// some of these values definitely need to be read from here column-wise -// the structs.{values} holding the above are: -// SeasaltEmissionsData.{mpoly, mprot, mlip} -// DustEmissionsData.dust_dmt_vwr -// OnlineEmissionsData.{dust_flux_in, surface_temp, u_wind, -// v_wind, z_bottom, ocean_frac} -// NOTE: fortran mam4 gets dust_flux_in and ocean_frac from cam_in, -// which is a chunk-wise variable at this point -// (see: physpkg.F90:{1605 & 1327}) otherwise grabs the end/bottom -// col-value once inside aero_model_emissions() \ No newline at end of file +} // namespace scream \ No newline at end of file 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 16cf6dcb74a..063efad22d4 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 @@ -103,7 +103,7 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { struct Preprocess { Preprocess() = default; // on host: initializes preprocess functor with necessary state data - void initialize(const int ncol, const int nlev, + void initialize(const int &ncol, const int &nlev, const mam_coupling::WetAtmosphere &wet_atm, const mam_coupling::DryAtmosphere &dry_atm, const view_2d &constituent_fluxes) { @@ -116,14 +116,25 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { KOKKOS_INLINE_FUNCTION void operator()( const Kokkos::TeamPolicy::member_type &team) const { - const int i = team.league_rank(); // column index + const int icol = team.league_rank(); // column index - compute_dry_mixing_ratios(team, wet_atm_pre_, dry_atm_pre_, i); + 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_, i); - compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, i); + compute_vertical_layer_heights(team, dry_atm_pre_, icol); + compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, icol); + + view_1d flux_col = ekat::subview(constituent_fluxes_pre_, icol); + + // Zero out constituent fluxes only for gasses and aerosols + const int pcnst = mam4::aero_model::pcnst; + const int gas_start_ind = mam4::utils::gasses_start_ind(); + + // FIXME: Is there a better way to zero out a select indices? + for(int ispc = gas_start_ind; ispc < pcnst; ++ispc) { + flux_col(ispc) = 0; + } } // Preprocess operator() // local variables for preprocess struct diff --git a/externals/mam4xx b/externals/mam4xx index 7d6a52bec71..419e04b4943 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 7d6a52bec71003de7da14433db1577042ff77218 +Subproject commit 419e04b4943ac4a69d867f6087ca43652a0689a2 From a150d9d53ad5c389ffe4db178d99bd6265e67278 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 22:18:26 -0700 Subject: [PATCH 22/41] EAMxx: Reverts input yaml and CMake, changes cons fluxes to updated --- ...and_online_emissions_process_interface.cpp | 5 ++- .../mam/emissions/CMakeLists.txt | 3 +- .../single-process/mam/emissions/input.yaml | 36 ++----------------- 3 files changed, 6 insertions(+), 38 deletions(-) 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 1d931435d06..f849595ff34 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,4 +1,3 @@ - #include // For reading soil erodibility file @@ -124,8 +123,8 @@ void MAMSrfOnlineEmiss::set_grids( // Constituent fluxes of species in [kg/m2/s] // FIXME: confirm if it is Updated or Computed - add_field("constituent_fluxes", vector2d_pcnst, kg / m2 / s, - grid_name); + 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", ""); diff --git a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt index 4b7350a1bb9..65f377b4db1 100644 --- a/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt +++ b/components/eamxx/tests/single-process/mam/emissions/CMakeLists.txt @@ -13,8 +13,7 @@ CreateADUnitTest(${TEST_BASE_NAME} # Set AD configurable options set (ATM_TIME_STEP 1800) -#SetVarDependingOnTestSize(NUM_STEPS 2 5 48) # 1h 2.5h 24h -SetVarDependingOnTestSize(NUM_STEPS 1 1 1) +SetVarDependingOnTestSize(NUM_STEPS 2 5 48) # 1h 2.5h 24h set (RUN_T0 2021-10-12-45000) ## Copy (and configure) yaml files needed by tests diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index ce103765b70..64a07b84405 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -25,34 +25,6 @@ atmosphere_processes: 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 - # FIXME: just set everything to a constant scalar for testing - online_emis_IC_ncl_a1: 0.0 - online_emis_IC_ncl_a2: 0.0 - online_emis_IC_ncl_a3: 0.0 - online_emis_IC_mom_a1: 0.16838301005552275E-013 - online_emis_IC_mom_a2: 0.26554873160224250E-016 - online_emis_IC_mom_a4: 0.0 - online_emis_IC_num_a1: 0.0 - online_emis_IC_num_a2: 0.0 - online_emis_IC_num_a3: 0.0 - online_emis_IC_num_a4: 0.0 - online_emis_IC_dst_a1: 0.0 - online_emis_IC_dst_a3: 0.18720550166902007E+003 - -# NOTE: these are the single-call results from the mam validation test -# (20) ncl_a1: 2.659900637e-13 -# (25) ncl_a2: 0 -# (29) ncl_a3: 2.391492482e-11 -# (21) mom_a1: 1.683830101e-14 -# (26) mom_a2: 2.655487316e-17 -# (38) mom_a4: 0 -# (22) num_a1: 2.594942753e-15 -# (27) num_a2: 4.092359179e-18 -# (35) num_a3: 0 -# (39) num_a4: 0 -# (19) dst_a1: 0 -# (28) dst_a3: 403.2611563 - grids_manager: Type: Mesh Free geo_data_source: IC_FILE @@ -65,12 +37,10 @@ grids_manager: initial_conditions: # The name of the file containing the initial conditions for this test. - #Filename: ${SCREAM_DATA_DIR}/init/${EAMxx_tests_IC_FILE_MAM4xx_72lev} - Filename: /qfs/people/sing201/eagles/refactor_mam/mam4xx_scream/4src/screami_unit_tests_mam4xx_ne2np4L72_LAT_m5p57727434969464_LON_83p9312243922771_ONLINE_EMISS_1st.nc + 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 pbl_height : 1.0 dstflx : [ 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000] @@ -78,7 +48,7 @@ initial_conditions: 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"] From f0a33da27edd95cfde8d670cfe73521d93f041ca Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 22:33:32 -0700 Subject: [PATCH 23/41] EAMxx: Some fixes after rebase --- .../eamxx_mam_microphysics_process_interface.cpp | 2 +- .../eamxx/src/physics/mam/srf_emission_impl.hpp | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 41713b97735..5fb4dccb2c8 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -76,7 +76,7 @@ void MAMMicrophysics::set_grids( constexpr auto n_unit = 1 / kg; // units of number mixing ratios of tracers ======= config_.amicphys.nucleation = {}; - // config_.amicphys.nucleation.dens_so4a_host = 1770.0; + //config_.amicphys.nucleation.dens_so4a_host = 1770.0; // config_.amicphys.nucleation.mw_so4a_host = 115.0; // config_.amicphys.nucleation.newnuc_method_user_choice = 2; // config_.amicphys.nucleation.pbl_nuc_wang2008_user_choice = 1; diff --git a/components/eamxx/src/physics/mam/srf_emission_impl.hpp b/components/eamxx/src/physics/mam/srf_emission_impl.hpp index b090e3746cb..4e7f7c14424 100644 --- a/components/eamxx/src/physics/mam/srf_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/srf_emission_impl.hpp @@ -182,10 +182,7 @@ void srfEmissFunctions::update_srfEmiss_data_from_file( const int time_index, // zero-based AbstractRemapper &srfEmiss_horiz_interp, srfEmissInput &srfEmiss_input) { using namespace ShortFieldTagsNames; - // NOTE: these are currently unused - // using ESU = ekat::ExeSpaceUtils; - // using Member = typename KokkosTypes::MemberType; - + start_timer("EAMxx::srfEmiss::update_srfEmiss_data_from_file"); // 1. Read from file @@ -205,14 +202,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 - // NOTE: these are currently unused - // const auto &layout = srfEmiss_horiz_interp.get_tgt_field(0) - // .get_header() - // .get_identifier() - // .get_layout(); - - const int ncols = layout.dim(COL); - // Read fields from the file for(int i = 0; i < srfEmiss_horiz_interp.get_num_fields(); ++i) { auto sector = From 80a57f053ecf36f4737d4425c08f512de573f2e4 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 30 Oct 2024 23:02:18 -0700 Subject: [PATCH 24/41] EAMxx:Adds loop to update file read for marine organics, cleanup --- .../cime_config/namelist_defaults_scream.xml | 2 - ...and_online_emissions_process_interface.cpp | 5 +- ...and_online_emissions_process_interface.hpp | 4 +- .../eamxx/src/physics/mam/online_emission.hpp | 88 ------------------- .../mam/readfiles/marine_organics_impl.hpp | 17 ++-- .../src/physics/mam/srf_emission_impl.hpp | 3 - 6 files changed, 16 insertions(+), 103 deletions(-) delete mode 100644 components/eamxx/src/physics/mam/online_emission.hpp diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 8b3a1131d2a..b57a5746253 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -612,7 +612,6 @@ be lost if SCREAM_HACK_XML is not enabled. ${DIN_LOC_ROOT}/atm/cam/topo/USGS-gtopo30_CA_ne32_x32_v1_pg2_16xdel2.nc - ${CASE}.scream 0.0 0.0 @@ -698,7 +697,6 @@ be lost if SCREAM_HACK_XML is not enabled. - ./${CASE}.scream default ${REST_N} 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 f849595ff34..190dc1da27d 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 @@ -247,7 +247,7 @@ void MAMSrfOnlineEmiss::set_grids( // Field to be read from file const std::string soil_erod_fld_name = "mbl_bsn_fct_geo"; - // Dimensions of the filed + // Dimensions of the field const std::string soil_erod_dname = "ncol"; // initialize the file read @@ -267,7 +267,7 @@ void MAMSrfOnlineEmiss::set_grids( const std::vector marine_org_fld_name = { "TRUEPOLYC", "TRUEPROTC", "TRUELIPC"}; - // Dimensions of the filed + // Dimensions of the field const std::string marine_org_dname = "ncol"; // initialize the file read @@ -526,6 +526,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { constituent_fluxes(icol, species_index) = fluxes_in_mks_units(icol); }); } // for loop for species + Kokkos::fence(); } // run_impl ends // ============================================================================= } // namespace scream \ No newline at end of file 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 063efad22d4..b1cba682bb6 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 @@ -6,7 +6,6 @@ // For MAM4 aerosol configuration #include -#include #include // For reading marine organics file @@ -62,9 +61,8 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { const_view_1d soil_erodibility_; public: + // For reading surface emissions and marine organics file using srfEmissFunc = mam_coupling::srfEmissFunctions; - using onlineEmiss = mam_coupling::onlineEmissions; - // For reading marine organics file using marineOrganicsFunc = marine_organics::marineOrganicsFunctions; diff --git a/components/eamxx/src/physics/mam/online_emission.hpp b/components/eamxx/src/physics/mam/online_emission.hpp deleted file mode 100644 index c8318dcc05c..00000000000 --- a/components/eamxx/src/physics/mam/online_emission.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#ifndef ONLINE_EMISSION_HPP -#define ONLINE_EMISSION_HPP - -#include "share/util/scream_timing.hpp" - -namespace scream::mam_coupling { -template struct onlineEmissions { - using Device = DeviceType; - using KT = KokkosTypes; - using MemberType = typename KT::MemberType; - static constexpr int pcnst = mam4::aero_model::pcnst; - - struct onlineEmissData { - // Basic spatial dimensions of the data - int ncols; - view_2d flux_data; - // local copy of main fluxes array - view_2d cfluxes; - // FIXME: read this from elsewhere? input? - const std::vector spec_names = { - "ncl_a1", "ncl_a2", "ncl_a3", "mom_a1", "mom_a2", "mom_a4", - "num_a1", "num_a2", "num_a3", "num_a4", "dst_a1", "dst_a3"}; - // FIXME: change this when/if the above is dynamically-determined - int nspec = spec_names.size(); - const std::string root_IC_str = "online_emis_IC_"; - - onlineEmissData() = default; - onlineEmissData(const int ncol_, const int nspec_) - : ncols(ncol_), nspec(nspec_) { - init(ncols, nspec, true); - } - - onlineEmissData(const int ncol_) : ncols(ncol_) { - init(ncols, nspec, true); - } - - // overloads of init() in case npsec is not hard-coded, or if you want to - // control allocation via bool flag - void init(const int ncol_, const int nspec_, const bool allocate_) { - ncols = ncol_; - nspec = nspec_; - if (allocate_) { - flux_data = view_2d("onlineEmissData", nspec, ncols); - cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); - } - } // onlineEmissData init - void init(const int ncol_, const bool allocate_) { - ncols = ncol_; - if (allocate_) { - flux_data = view_2d("onlineEmissData", nspec, ncols); - cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); - } - } // onlineEmissData init - void init(const int ncol_) { - ncols = ncol_; - flux_data = view_2d("onlineEmissData", nspec, ncols); - cfluxes = view_2d("onlineEmisLocalCflux", ncols, pcnst); - } // onlineEmissData init - }; // onlineEmissData - - onlineEmissData online_emis_data; - - // --------------------------------------------------------------------------- - // Online emissions routines - // --------------------------------------------------------------------------- - void init_from_input_file(const ekat::ParameterList ¶ms) { - const int nspec = online_emis_data.nspec; - const int ncols = online_emis_data.ncols; - using ExeSpace = typename KT::ExeSpace; - using ESU = ekat::ExeSpaceUtils; - const auto policy = ESU::get_default_team_policy(ncols, nspec); - // Read from input file - // FIXME: currently reading a single placeholder scalar--should be - // ncols-sized array when we know what the input data looks like - for (int ispec = 0; ispec < nspec; ++ispec) { - Real init_cond_val = - params.get(online_emis_data.root_IC_str + online_emis_data.spec_names[ispec]); - // TODO: is this overkill?--i.e., would a mirror/deep_copy make more sense? - Kokkos::parallel_for( - policy, KOKKOS_LAMBDA(const MemberType &team) { - const int jcol = team.league_rank(); // column index - online_emis_data.flux_data(ispec, jcol) = init_cond_val; - }); - } - } // end init_from_input_file() -}; // struct onlineEmissions -} // namespace scream::mam_coupling -#endif // ONLINE_EMISSION_HPP diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp index 100b432cb74..6eda3a47e72 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -85,8 +85,8 @@ marineOrganicsFunctions::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)); + 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); @@ -126,9 +126,16 @@ void marineOrganicsFunctions::update_marine_organics_data_from_file( "field"); // Recall, the fields are registered in the order: // Read the field from the file -#if 0 - input = horiz_interp.get_tgt_field(0).get_view(); -#endif + + 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"); diff --git a/components/eamxx/src/physics/mam/srf_emission_impl.hpp b/components/eamxx/src/physics/mam/srf_emission_impl.hpp index 4e7f7c14424..fa037dc281d 100644 --- a/components/eamxx/src/physics/mam/srf_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/srf_emission_impl.hpp @@ -102,9 +102,6 @@ void srfEmissFunctions::perform_time_interpolation( // NOTE: we *assume* data_beg and data_end have the *same* hybrid v coords. // IF this ever ceases to be the case, you can interp those too. - 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; From 15f44fd3101cd3a67a634af34e498fc1736362e7 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Thu, 31 Oct 2024 16:43:23 -0600 Subject: [PATCH 25/41] fixup! EAMxx: Some fixes after rebase small change to kickoff AT --- components/eamxx/tests/single-process/mam/emissions/input.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index 64a07b84405..a4564cb3949 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -41,7 +41,7 @@ initial_conditions: topography_filename: ${TOPO_DATA_DIR}/${EAMxx_tests_TOPO_FILE} phis : 1.0 - #we should get the following variables from other processes + # we should get the following variables from other processes pbl_height : 1.0 dstflx : [ 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000] ocnfrac: 0.10000000000000000E+001 From bdd475f60e4fbd7703159f2e1217f43391f6623b Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Fri, 1 Nov 2024 06:59:01 -0700 Subject: [PATCH 26/41] EAMxx:Fixes file path and specify vector components for dust flux --- components/eamxx/cime_config/namelist_defaults_scream.xml | 7 ++++++- components/eamxx/src/mct_coupling/scream_cpl_indices.F90 | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index b57a5746253..9cc917a8c88 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -376,12 +376,17 @@ be lost if SCREAM_HACK_XML is not enabled. ======= - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne30pg2_c20241028.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/dst_ne30pg2_c20241028.nc +<<<<<<< HEAD ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne4pg2_c20241028.nc >>>>>>> EAMxx:Adds soil erodibility file read and sent to online emission read +======= + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/dst_ne4pg2_c20241028.nc + +>>>>>>> EAMxx:Fixes file path and specify vector components for dust flux ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne30pg2_c20241030.nc diff --git a/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 b/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 index 15df178d29e..9462750297f 100644 --- a/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 +++ b/components/eamxx/src/mct_coupling/scream_cpl_indices.F90 @@ -125,12 +125,16 @@ subroutine scream_set_cpl_indices (x2a, a2x) 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 From 4e120c747c038559b90f9424e895f6795c7c4cfd Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 11:06:50 -0800 Subject: [PATCH 27/41] EAMxx: MAM4xx submodule pointing to main that include GPU fixes --- ...mxx_mam_microphysics_process_interface.cpp | 102 ++++++++++++++++++ externals/mam4xx | 2 +- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 5fb4dccb2c8..875d63432f8 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -611,6 +611,7 @@ void MAMMicrophysics::run_impl(const double dt) { // allocation perspective auto o3_col_dens = buffer_.scratch[8]; +<<<<<<< HEAD /* Gather time and state information for interpolation */ const auto ts = timestamp() + dt; @@ -628,6 +629,107 @@ void MAMMicrophysics::run_impl(const double dt) { dry_atm_.p_mid, dry_atm_.z_iface, // in cnst_offline_); // out Kokkos::fence(); +======= + const_view_1d &col_latitudes = col_latitudes_; + mam_coupling::DryAtmosphere &dry_atm = dry_atm_; + mam_coupling::AerosolState &dry_aero = dry_aero_; + mam4::mo_photo::PhotoTableData &photo_table = photo_table_; + const int nlev = nlev_; + const Config &config = config_; + // FIXME: read relevant linoz climatology data from file(s) based on time + + // FIXME: read relevant chlorine loading data from file based on time + + // loop over atmosphere columns and compute aerosol microphyscs + auto some_step = step_; + + Kokkos::parallel_for(policy, KOKKOS_LAMBDA(const ThreadTeam& team) { + const int icol = team.league_rank(); // column index + + Real col_lat = col_latitudes(icol); // column latitude (degrees?) + + // fetch column-specific atmosphere state data + auto atm = mam_coupling::atmosphere_for_column(dry_atm, icol); + auto z_iface = ekat::subview(dry_atm.z_iface, icol); + Real phis = dry_atm.phis(icol); + + // set surface state data + haero::Surface sfc{}; + + // fetch column-specific subviews into aerosol prognostics + mam4::Prognostics progs = mam_coupling::interstitial_aerosols_for_column(dry_aero, icol); + + // set up diagnostics + mam4::Diagnostics diags(nlev); + + // calculate o3 column densities (first component of col_dens in Fortran code) + auto o3_col_dens_i = ekat::subview(o3_col_dens, icol); + impl::compute_o3_column_density(team, atm, progs, o3_col_dens_i); + + // set up photolysis work arrays for this column. + mam4::mo_photo::PhotoTableWorkArrays photo_work_arrays; + // FIXME: set views here + + // ... look up photolysis rates from our table + // NOTE: the table interpolation operates on an entire column of data, so we + // NOTE: must do it before dispatching to individual vertical levels + Real zenith_angle = 0.0; // FIXME: need to get this from EAMxx [radians] + Real surf_albedo = 0.0; // FIXME: surface albedo + Real esfact = 0.0; // FIXME: earth-sun distance factor + mam4::ColumnView lwc; // FIXME: liquid water cloud content: where do we get this? + //mam4::mo_photo::table_photo(photo_rates, atm.pressure, atm.hydrostatic_dp, + // atm.temperature, o3_col_dens_i, zenith_angle, surf_albedo, lwc, + // atm.cloud_fraction, esfact, photo_table, photo_work_arrays); + + // compute external forcings at time t(n+1) [molecules/cm^3/s] + constexpr int extcnt = mam4::gas_chemistry::extcnt; + view_2d extfrc; // FIXME: where to allocate? (nlev, extcnt) + mam4::mo_setext::Forcing forcings[extcnt]; // FIXME: forcings seem to require file data + mam4::mo_setext::extfrc_set(forcings, extfrc); + + // compute aerosol microphysics on each vertical level within this column + Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev), [&](const int k) { + + constexpr int num_modes = mam4::AeroConfig::num_modes(); + constexpr int gas_pcnst = mam_coupling::gas_pcnst(); + constexpr int nqtendbb = mam_coupling::nqtendbb(); + + // extract atm state variables (input) + Real temp = atm.temperature(k); + Real pmid = atm.pressure(k); + Real pdel = atm.hydrostatic_dp(k); + Real zm = atm.height(k); + Real zi = z_iface(k); + Real pblh = atm.planetary_boundary_layer_height; + Real qv = atm.vapor_mixing_ratio(k); + Real cldfrac = atm.cloud_fraction(k); + + // extract aerosol state variables into "working arrays" (mass mixing ratios) + // (in EAM, this is done in the gas_phase_chemdr subroutine defined within + // mozart/mo_gas_phase_chemdr.F90) + Real q[gas_pcnst] = {}; + Real qqcw[gas_pcnst] = {}; + mam_coupling::transfer_prognostics_to_work_arrays(progs, k, q, qqcw); + + // convert mass mixing ratios to volume mixing ratios (VMR), equivalent + // to tracer mixing ratios (TMR)) + Real vmr[gas_pcnst], vmrcw[gas_pcnst]; + mam_coupling::convert_work_arrays_to_vmr(q, qqcw, vmr, vmrcw); + + // aerosol/gas species tendencies (output) + Real vmr_tendbb[gas_pcnst][nqtendbb] = {}; + Real vmrcw_tendbb[gas_pcnst][nqtendbb] = {}; + + // create work array copies to retain "pre-chemistry" values + Real vmr_pregaschem[gas_pcnst] = {}; + Real vmr_precldchem[gas_pcnst] = {}; + Real vmrcw_precldchem[gas_pcnst] = {}; + for (int i = 0; i < gas_pcnst; ++i) { + vmr_pregaschem[i] = vmr[i]; + vmr_precldchem[i] = vmr[i]; + vmrcw_precldchem[i] = vmrcw[i]; + } +>>>>>>> EAMxx: MAM4xx submodule pointing to main that include GPU fixes scream::mam_coupling::advance_tracer_data( LinozDataReader_, // in diff --git a/externals/mam4xx b/externals/mam4xx index 419e04b4943..59990d402f4 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 419e04b4943ac4a69d867f6087ca43652a0689a2 +Subproject commit 59990d402f4115ce3a36a8c00eb4d059e703a583 From 7b8f7fdd2871018dabeeae8a53dd76f4333905f9 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 13:26:15 -0800 Subject: [PATCH 28/41] EAMxx: Some codes re-arranged after rebase --- .../cime_config/namelist_defaults_scream.xml | 9 -- ...mxx_mam_microphysics_process_interface.cpp | 113 ------------------ 2 files changed, 122 deletions(-) diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 9cc917a8c88..fd62cdc01e3 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -371,22 +371,13 @@ be lost if SCREAM_HACK_XML is not enabled. ${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 -<<<<<<< HEAD -======= - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/dst_ne30pg2_c20241028.nc -<<<<<<< HEAD ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne4pg2_c20241028.nc ->>>>>>> EAMxx:Adds soil erodibility file read and sent to online emission read -======= - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/dst_ne4pg2_c20241028.nc - ->>>>>>> EAMxx:Fixes file path and specify vector components for dust flux ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne30pg2_c20241030.nc diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 875d63432f8..57eaf927548 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -70,20 +70,9 @@ void MAMMicrophysics::set_grids( const FieldLayout scalar3d_mid = grid_->get_3d_scalar_layout(true); const FieldLayout scalar3d_int = grid_->get_3d_scalar_layout(false); -<<<<<<< HEAD using namespace ekat::units; 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 -======= - config_.amicphys.nucleation = {}; - //config_.amicphys.nucleation.dens_so4a_host = 1770.0; - // config_.amicphys.nucleation.mw_so4a_host = 115.0; - // config_.amicphys.nucleation.newnuc_method_user_choice = 2; - // config_.amicphys.nucleation.pbl_nuc_wang2008_user_choice = 1; - // config_.amicphys.nucleation.adjust_factor_pbl_ratenucl = 1.0; - // config_.amicphys.nucleation.accom_coef_h2so4 = 1.0; - config_.amicphys.nucleation.newnuc_adjust_factor_dnaitdt = 1.0; ->>>>>>> comment out error-inducing code in mam4_amicphys.cpp and small reorg change in online_emission.hpp // -------------------------------------------------------------------------- // These variables are "Required" or pure inputs for the process @@ -611,7 +600,6 @@ void MAMMicrophysics::run_impl(const double dt) { // allocation perspective auto o3_col_dens = buffer_.scratch[8]; -<<<<<<< HEAD /* Gather time and state information for interpolation */ const auto ts = timestamp() + dt; @@ -629,107 +617,6 @@ void MAMMicrophysics::run_impl(const double dt) { dry_atm_.p_mid, dry_atm_.z_iface, // in cnst_offline_); // out Kokkos::fence(); -======= - const_view_1d &col_latitudes = col_latitudes_; - mam_coupling::DryAtmosphere &dry_atm = dry_atm_; - mam_coupling::AerosolState &dry_aero = dry_aero_; - mam4::mo_photo::PhotoTableData &photo_table = photo_table_; - const int nlev = nlev_; - const Config &config = config_; - // FIXME: read relevant linoz climatology data from file(s) based on time - - // FIXME: read relevant chlorine loading data from file based on time - - // loop over atmosphere columns and compute aerosol microphyscs - auto some_step = step_; - - Kokkos::parallel_for(policy, KOKKOS_LAMBDA(const ThreadTeam& team) { - const int icol = team.league_rank(); // column index - - Real col_lat = col_latitudes(icol); // column latitude (degrees?) - - // fetch column-specific atmosphere state data - auto atm = mam_coupling::atmosphere_for_column(dry_atm, icol); - auto z_iface = ekat::subview(dry_atm.z_iface, icol); - Real phis = dry_atm.phis(icol); - - // set surface state data - haero::Surface sfc{}; - - // fetch column-specific subviews into aerosol prognostics - mam4::Prognostics progs = mam_coupling::interstitial_aerosols_for_column(dry_aero, icol); - - // set up diagnostics - mam4::Diagnostics diags(nlev); - - // calculate o3 column densities (first component of col_dens in Fortran code) - auto o3_col_dens_i = ekat::subview(o3_col_dens, icol); - impl::compute_o3_column_density(team, atm, progs, o3_col_dens_i); - - // set up photolysis work arrays for this column. - mam4::mo_photo::PhotoTableWorkArrays photo_work_arrays; - // FIXME: set views here - - // ... look up photolysis rates from our table - // NOTE: the table interpolation operates on an entire column of data, so we - // NOTE: must do it before dispatching to individual vertical levels - Real zenith_angle = 0.0; // FIXME: need to get this from EAMxx [radians] - Real surf_albedo = 0.0; // FIXME: surface albedo - Real esfact = 0.0; // FIXME: earth-sun distance factor - mam4::ColumnView lwc; // FIXME: liquid water cloud content: where do we get this? - //mam4::mo_photo::table_photo(photo_rates, atm.pressure, atm.hydrostatic_dp, - // atm.temperature, o3_col_dens_i, zenith_angle, surf_albedo, lwc, - // atm.cloud_fraction, esfact, photo_table, photo_work_arrays); - - // compute external forcings at time t(n+1) [molecules/cm^3/s] - constexpr int extcnt = mam4::gas_chemistry::extcnt; - view_2d extfrc; // FIXME: where to allocate? (nlev, extcnt) - mam4::mo_setext::Forcing forcings[extcnt]; // FIXME: forcings seem to require file data - mam4::mo_setext::extfrc_set(forcings, extfrc); - - // compute aerosol microphysics on each vertical level within this column - Kokkos::parallel_for(Kokkos::TeamThreadRange(team, nlev), [&](const int k) { - - constexpr int num_modes = mam4::AeroConfig::num_modes(); - constexpr int gas_pcnst = mam_coupling::gas_pcnst(); - constexpr int nqtendbb = mam_coupling::nqtendbb(); - - // extract atm state variables (input) - Real temp = atm.temperature(k); - Real pmid = atm.pressure(k); - Real pdel = atm.hydrostatic_dp(k); - Real zm = atm.height(k); - Real zi = z_iface(k); - Real pblh = atm.planetary_boundary_layer_height; - Real qv = atm.vapor_mixing_ratio(k); - Real cldfrac = atm.cloud_fraction(k); - - // extract aerosol state variables into "working arrays" (mass mixing ratios) - // (in EAM, this is done in the gas_phase_chemdr subroutine defined within - // mozart/mo_gas_phase_chemdr.F90) - Real q[gas_pcnst] = {}; - Real qqcw[gas_pcnst] = {}; - mam_coupling::transfer_prognostics_to_work_arrays(progs, k, q, qqcw); - - // convert mass mixing ratios to volume mixing ratios (VMR), equivalent - // to tracer mixing ratios (TMR)) - Real vmr[gas_pcnst], vmrcw[gas_pcnst]; - mam_coupling::convert_work_arrays_to_vmr(q, qqcw, vmr, vmrcw); - - // aerosol/gas species tendencies (output) - Real vmr_tendbb[gas_pcnst][nqtendbb] = {}; - Real vmrcw_tendbb[gas_pcnst][nqtendbb] = {}; - - // create work array copies to retain "pre-chemistry" values - Real vmr_pregaschem[gas_pcnst] = {}; - Real vmr_precldchem[gas_pcnst] = {}; - Real vmrcw_precldchem[gas_pcnst] = {}; - for (int i = 0; i < gas_pcnst; ++i) { - vmr_pregaschem[i] = vmr[i]; - vmr_precldchem[i] = vmr[i]; - vmrcw_precldchem[i] = vmrcw[i]; - } ->>>>>>> EAMxx: MAM4xx submodule pointing to main that include GPU fixes scream::mam_coupling::advance_tracer_data( LinozDataReader_, // in From 4195b419bee625fb962646433b58ca3c4747f252 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 13:29:00 -0800 Subject: [PATCH 29/41] EAMxx:Removes accidently added file --- .../src/physics/mam/impl/mam4_amicphys.cpp | 1819 ----------------- 1 file changed, 1819 deletions(-) delete mode 100644 components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp diff --git a/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp b/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp deleted file mode 100644 index e241b5ec428..00000000000 --- a/components/eamxx/src/physics/mam/impl/mam4_amicphys.cpp +++ /dev/null @@ -1,1819 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace scream::impl { - -#define MAX_FILENAME_LEN 256 - -using namespace mam4; - -// number of constituents in gas chemistry "work arrays" -KOKKOS_INLINE_FUNCTION -constexpr int gas_pcnst() { - constexpr int gas_pcnst_ = mam4::gas_chemistry::gas_pcnst; - return gas_pcnst_; -} - -// number of aerosol/gas species tendencies -KOKKOS_INLINE_FUNCTION -constexpr int nqtendbb() { return 4; } - -// MAM4 aerosol microphysics configuration data -struct AmicPhysConfig { - // these switches activate various aerosol microphysics processes - bool do_cond; // condensation (a.k.a gas-aerosol exchange) - bool do_rename; // mode "renaming" - bool do_newnuc; // gas -> aerosol nucleation - bool do_coag; // aerosol coagulation - - // configurations for specific aerosol microphysics - mam4::GasAerExchProcess::ProcessConfig condensation; - mam4::NucleationProcess::ProcessConfig nucleation; - - // controls treatment of h2so4 condensation in mam_gasaerexch_1subarea - // 1 = sequential calc. of gas-chem prod then condensation loss - // 2 = simultaneous calc. of gas-chem prod and condensation loss - int gaexch_h2so4_uptake_optaa; - - // controls how nucleation interprets h2so4 concentrations - int newnuc_h2so4_conc_optaa; -}; - -namespace { - -KOKKOS_INLINE_FUNCTION constexpr int nqtendaa() { return 5; } -KOKKOS_INLINE_FUNCTION constexpr int nqqcwtendaa() { return 1; } -KOKKOS_INLINE_FUNCTION constexpr int nqqcwtendbb() { return 1; } -KOKKOS_INLINE_FUNCTION constexpr int iqtend_cond() { return 0; } -KOKKOS_INLINE_FUNCTION constexpr int iqtend_rnam() { return 1; } -KOKKOS_INLINE_FUNCTION constexpr int iqtend_nnuc() { return 2; } -KOKKOS_INLINE_FUNCTION constexpr int iqtend_coag() { return 3; } -KOKKOS_INLINE_FUNCTION constexpr int iqtend_cond_only() { return 4; } -KOKKOS_INLINE_FUNCTION constexpr int iqqcwtend_rnam() { return 0; } -KOKKOS_INLINE_FUNCTION constexpr int maxsubarea() { return 2; } - -// conversion factors -KOKKOS_INLINE_FUNCTION Real fcvt_gas(int gas_id) { - static const Real fcvt_gas_[AeroConfig::num_gas_ids()] = {1, 1, 1}; - return fcvt_gas_[gas_id]; -} -KOKKOS_INLINE_FUNCTION Real fcvt_aer(int aero_id) { - static const Real fcvt_aer_[AeroConfig::num_aerosol_ids()] = {1, 1, 1, 1, 1, 1, 1}; - return fcvt_aer_[aero_id]; -} - -// leave number mix-ratios unchanged (#/kmol-air) -KOKKOS_INLINE_FUNCTION Real fcvt_num() { return 1.0; } -// factor for converting aerosol water mix-ratios from (kg/kg) to (mol/mol) -KOKKOS_INLINE_FUNCTION Real fcvt_wtr() { return 1.0; } - -KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_nul() { return 0; } -KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_gas() { return 1; } -KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_aer() { return 2; } -KOKKOS_INLINE_FUNCTION constexpr int lmapcc_val_num() { return 3; } -KOKKOS_INLINE_FUNCTION int lmapcc_all(int index) { - static const int lmapcc_all_[gas_pcnst()] = { - lmapcc_val_nul(), lmapcc_val_gas(), lmapcc_val_nul(), lmapcc_val_nul(), - lmapcc_val_gas(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_aer(), lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_aer(), lmapcc_val_num(), lmapcc_val_aer(), lmapcc_val_aer(), - lmapcc_val_aer(), lmapcc_val_num()}; - return lmapcc_all_[index]; -} - -// Where lmapcc_val_num are defined in lmapcc_all -KOKKOS_INLINE_FUNCTION int numptr_amode(int mode) { - static const int numptr_amode_[AeroConfig::num_modes()] = {12, 17, 25, 29}; - return numptr_amode_[mode]; -} - -// Where lmapcc_val_gas are defined in lmapcc_all -KOKKOS_INLINE_FUNCTION int lmap_gas(int mode) { - static const int lmap_gas_[AeroConfig::num_modes()] = {4, 1}; - return lmap_gas_[mode]; -} -// Where lmapcc_val_aer are defined in lmapcc_all -KOKKOS_INLINE_FUNCTION int lmassptr_amode(int aero_id, int mode) { - static const int lmassptr_amode_[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] = { - {5, 13, 18, 26}, {6, 14, 19, 27}, {7, 15, 20, 28}, {8, 16, 21, -6}, - {9, -6, 22, -6}, {10, -6, 23, -6}, {11, -6, 24, -6}}; - return lmassptr_amode_[aero_id][mode]; -} - -KOKKOS_INLINE_FUNCTION -void subarea_partition_factors( - const Real - q_int_cell_avg, // in grid cell mean interstitial aerosol mixing ratio - const Real - q_cbn_cell_avg, // in grid cell mean cloud-borne aerosol mixing ratio - const Real fcldy, // in cloudy fraction of the grid cell - const Real fclea, // in clear fraction of the grid cell - Real &part_fac_q_int_clea, // out - Real &part_fac_q_int_cldy) // out -{ - // Calculate mixing ratios of each subarea - - // cloud-borne, cloudy subarea - const Real tmp_q_cbn_cldy = q_cbn_cell_avg / fcldy; - // interstitial, cloudy subarea - const Real tmp_q_int_cldy = - haero::max(0.0, ((q_int_cell_avg + q_cbn_cell_avg) - tmp_q_cbn_cldy)); - // interstitial, clear subarea - const Real tmp_q_int_clea = (q_int_cell_avg - fcldy * tmp_q_int_cldy) / fclea; - - // Calculate the corresponding paritioning factors for interstitial aerosols - // using the above-derived subarea mixing ratios plus the constraint that - // the cloud fraction weighted average of subarea mean need to match grid box - // mean. - - // *** question *** - // use same part_fac_q_int_clea/cldy for everything ? - // use one for number and one for all masses (based on total mass) ? - // use separate ones for everything ? - // maybe one for number and one for all masses is best, - // because number and mass have different activation fractions - // *** question *** - - Real tmp_aa = haero::max(1.e-35, tmp_q_int_clea * fclea) / - haero::max(1.e-35, q_int_cell_avg); - tmp_aa = haero::max(0.0, haero::min(1.0, tmp_aa)); - - part_fac_q_int_clea = tmp_aa / fclea; - part_fac_q_int_cldy = (1.0 - tmp_aa) / fcldy; -} - -KOKKOS_INLINE_FUNCTION -void construct_subareas_1gridcell( - const Real cld, // in - const Real relhumgcm, // in - const Real q_pregaschem[gas_pcnst()], // in q TMRs before - // gas-phase chemistry - const Real q_precldchem[gas_pcnst()], // in q TMRs before - // cloud chemistry - const Real qqcw_precldchem[gas_pcnst()], // in qqcw TMRs before - // cloud chemistry - const Real q[gas_pcnst()], // in current tracer mixing ratios (TMRs) - // *** MUST BE #/kmol-air for number - // *** MUST BE mol/mol-air for mass - const Real qqcw[gas_pcnst()], // in like q but for - // cloud-borner tracers - int &nsubarea, // out - int &ncldy_subarea, // out - int &jclea, // out - int &jcldy, // out - bool iscldy_subarea[maxsubarea()], // out - Real afracsub[maxsubarea()], // out - Real relhumsub[maxsubarea()], // out - Real qsub1[gas_pcnst()][maxsubarea()], // out interstitial - Real qsub2[gas_pcnst()][maxsubarea()], // out interstitial - Real qsub3[gas_pcnst()][maxsubarea()], // out interstitial - Real qqcwsub1[gas_pcnst()][maxsubarea()], // out cloud-borne - Real qqcwsub2[gas_pcnst()][maxsubarea()], // out cloud-borne - Real qqcwsub3[gas_pcnst()][maxsubarea()], // outcloud-borne - Real qaerwatsub3[AeroConfig::num_modes()] - [maxsubarea()], // out aerosol water mixing ratios (mol/mol) - Real qaerwat[AeroConfig::num_modes()] // in aerosol water mixing ratio - // (kg/kg, NOT mol/mol) -) { - static constexpr int num_modes = AeroConfig::num_modes(); - // cloud chemistry is only on when cld(i,k) >= 1.0e-5_wp - // it may be that the macrophysics has a higher threshold that this - const Real fcld_locutoff = 1.0e-5; - const Real fcld_hicutoff = 0.999; - - // qgcmN and qqcwgcmN (N=1:4) are grid-cell mean tracer mixing ratios (TMRs, - // mol/mol or #/kmol) - // N=1 - before gas-phase chemistry - // N=2 - before cloud chemistry - // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) - // qgcm1, qgcm2, qgcm3 - // qqcwgcm2, qqcwgcm3 - // qaerwatgcm3 ! aerosol water mixing ratios (mol/mol) - - // -------------------------------------------------------------------------------------- - // Determine the number of sub-areas, their fractional areas, and relative - // humidities - // -------------------------------------------------------------------------------------- - // if cloud fraction ~= 0, the grid-cell has a single clear sub-area - // (nsubarea = 1) if cloud fraction ~= 1, the grid-cell has a single cloudy - // sub-area (nsubarea = 1) otherwise, the grid-cell has a - // clear and a cloudy sub-area (nsubarea = 2) - - Real zfcldy = 0; - nsubarea = 0; - ncldy_subarea = 0; - jclea = 0; - jcldy = 0; - - if (cld < fcld_locutoff) { - nsubarea = 1; - jclea = 1; - } else if (cld > fcld_hicutoff) { - zfcldy = 1.0; - nsubarea = 1; - ncldy_subarea = 1; - jcldy = 1; - } else { - zfcldy = cld; - nsubarea = 2; - ncldy_subarea = 1; - jclea = 1; - jcldy = 2; - } - - const Real zfclea = 1.0 - zfcldy; - for (int i = 0; i < maxsubarea(); ++i) - iscldy_subarea[i] = false; - if (jcldy > 0) - iscldy_subarea[jcldy - 1] = true; - for (int i = 0; i < maxsubarea(); ++i) - afracsub[i] = 0.0; - if (jclea > 0) - afracsub[jclea - 1] = zfclea; - if (jcldy > 0) - afracsub[jcldy - 1] = zfcldy; - - // cldy_rh_sameas_clear is just to match mam_refactor. Compiler should - // optimize away. - const int cldy_rh_sameas_clear = 0; - if (ncldy_subarea <= 0) { - for (int i = 0; i < maxsubarea(); ++i) - relhumsub[i] = relhumgcm; - } else if (cldy_rh_sameas_clear > 0) { - for (int i = 0; i < maxsubarea(); ++i) - relhumsub[i] = relhumgcm; - } else { - if (jcldy > 0) { - relhumsub[jcldy - 1] = 1.0; - if (jclea > 0) { - const Real tmpa = - (relhumgcm - afracsub[jcldy - 1]) / afracsub[jclea - 1]; - relhumsub[jclea - 1] = haero::max(0.0, haero::min(1.0, tmpa)); - } - } - } - - // ---------------------------------------------------------------------------- - // Copy grid cell mean mixing ratios. - // These values, together with cloud fraction and a few assumptions, are used - // in the remainder of the subroutine to calculate the sub-area mean mixing - // ratios. - // ---------------------------------------------------------------------------- - // Interstitial aerosols - Real qgcm1[gas_pcnst()], qgcm2[gas_pcnst()], qgcm3[gas_pcnst()]; - for (int i = 0; i < gas_pcnst(); ++i) { - qgcm1[i] = haero::max(0.0, q_pregaschem[i]); - qgcm2[i] = haero::max(0.0, q_precldchem[i]); - qgcm3[i] = haero::max(0.0, q[i]); - } - - // Cloud-borne aerosols - Real qqcwgcm2[gas_pcnst()], qqcwgcm3[gas_pcnst()]; - for (int i = 0; i < gas_pcnst(); ++i) { - qqcwgcm2[i] = haero::max(0.0, qqcw_precldchem[i]); - qqcwgcm3[i] = haero::max(0.0, qqcw[i]); - } - - // aerosol water - Real qaerwatgcm3[num_modes] = {}; - for (int i = 0; i < num_modes; ++i) { - qaerwatgcm3[i] = haero::max(0.0, qaerwat[i]); - } - - // ---------------------------------------------------------------------------- - // Initialize the subarea mean mixing ratios - // ---------------------------------------------------------------------------- - { - const int n = haero::min(maxsubarea(), nsubarea + 1); - for (int i = 0; i < n; ++i) { - for (int j = 0; j < gas_pcnst(); ++j) { - qsub1[j][i] = 0.0; - qsub2[j][i] = 0.0; - qsub3[j][i] = 0.0; - qqcwsub1[j][i] = 0.0; - qqcwsub2[j][i] = 0.0; - qqcwsub3[j][i] = 0.0; - } - for (int j = 0; j < num_modes; ++j) { - qaerwatsub3[j][i] = 0.0; - } - } - } - - // ************************************************************************************************* - // Calculate initial (i.e., before cond/rnam/nnuc/coag) tracer mixing - // ratios within the sub-areas - // - for all-clear or all-cloudy cases, the sub-area TMRs are equal to the - // grid-cell means - // - for partly cloudy case, they are different. This is primarily - // because the - // interstitial aerosol mixing ratios are assumed lower in the cloudy - // sub-area than in the clear sub-area, because much of the aerosol is - // activated in the cloudy sub-area. - // ************************************************************************************************* - // Category I: partly cloudy case - // ************************************************************************************************* - if ((jclea > 0) && (jcldy > 0) && (jclea + jcldy == 3) && (nsubarea == 2)) { - - // --------------------------------------------------------------------- - // Set GAS mixing ratios in sub-areas (for the condensing gases only!!) - // --------------------------------------------------------------------- - for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { - if (lmapcc_all(lmz) == lmapcc_val_gas()) { - - // assume gas in both sub-areas before gas-chem and cloud-chem equal - // grid-cell mean - for (int i = 0; i < nsubarea; ++i) { - qsub1[lmz][i] = qgcm1[lmz]; - qsub2[lmz][i] = qgcm2[lmz]; - } - // assume gas in clear sub-area after cloud-chem equals before - // cloud-chem value - qsub3[lmz][jclea - 1] = qsub2[lmz][jclea - 1]; - // gas in cloud sub-area then determined by grid-cell mean and clear - // values - qsub3[lmz][jcldy - 1] = - (qgcm3[lmz] - zfclea * qsub3[lmz][jclea - 1]) / zfcldy; - - // check that this does not produce a negative value - if (qsub3[lmz][jcldy - 1] < 0.0) { - qsub3[lmz][jcldy - 1] = 0.0; - qsub3[lmz][jclea - 1] = qgcm3[lmz] / zfclea; - } - } - } - // --------------------------------------------------------------------- - // Set CLOUD-BORNE AEROSOL mixing ratios in sub-areas. - // This is straightforward, as the same partitioning factors (0 or 1/f) - // are applied to all mass and number mixing ratios in all modes. - // --------------------------------------------------------------------- - // loop thru log-normal modes - for (int n = 0; n < num_modes; ++n) { - // number - then mass of individual species - of a mode - for (int l2 = -1; l2 < num_species_mode(n); ++l2) { - int lc; - if (l2 == -1) - lc = numptr_amode(n); - else - lc = lmassptr_amode(l2, n); - qqcwsub2[lc][jclea - 1] = 0.0; - qqcwsub2[lc][jcldy - 1] = qqcwgcm2[lc] / zfcldy; - qqcwsub3[lc][jclea - 1] = 0.0; - qqcwsub3[lc][jcldy - 1] = qqcwgcm3[lc] / zfcldy; - } - } - - // --------------------------------------------------------------------- - // Set INTERSTITIAL AEROSOL mixing ratios in sub-areas. - // --------------------------------------------------------------------- - for (int n = 0; n < num_modes; ++n) { - // ------------------------------------- - // Aerosol number - // ------------------------------------- - // grid cell mean, interstitial - Real tmp_q_cellavg_int = qgcm2[numptr_amode(n)]; - // grid cell mean, cloud-borne - Real tmp_q_cellavg_cbn = qqcwgcm2[numptr_amode(n)]; - - Real nmbr_part_fac_clea = 0; - Real nmbr_part_fac_cldy = 0; - subarea_partition_factors(tmp_q_cellavg_int, tmp_q_cellavg_cbn, zfcldy, - zfclea, nmbr_part_fac_clea, nmbr_part_fac_cldy); - - // Apply the partitioning factors to calculate sub-area mean number - // mixing ratios - - const int la = numptr_amode(n); - - qsub2[la][jclea - 1] = qgcm2[la] * nmbr_part_fac_clea; - qsub2[la][jcldy - 1] = qgcm2[la] * nmbr_part_fac_cldy; - qsub3[la][jclea - 1] = qgcm3[la] * nmbr_part_fac_clea; - qsub3[la][jcldy - 1] = qgcm3[la] * nmbr_part_fac_cldy; - - //------------------------------------- - // Aerosol mass - //------------------------------------- - // For aerosol mass, we use the total grid cell mean - // interstitial/cloud-borne mass mixing ratios to come up with the same - // partitioning for all species in the mode. - - // Compute the total mixing ratios by summing up the individual species - - tmp_q_cellavg_int = 0.0; // grid cell mean, interstitial - tmp_q_cellavg_cbn = 0.0; // grid cell mean, cloud-borne - - for (int l2 = 0; l2 < num_species_mode(n); ++l2) { - tmp_q_cellavg_int += qgcm2[lmassptr_amode(l2, n)]; - tmp_q_cellavg_cbn += qqcwgcm2[lmassptr_amode(l2, n)]; - } - Real mass_part_fac_clea = 0; - Real mass_part_fac_cldy = 0; - // Calculate the partitioning factors - subarea_partition_factors(tmp_q_cellavg_int, tmp_q_cellavg_cbn, zfcldy, - zfclea, mass_part_fac_clea, mass_part_fac_cldy); - - // Apply the partitioning factors to calculate sub-area mean mass mixing - // ratios - - for (int l2 = 0; l2 < num_species_mode(n); ++l2) { - const int la = lmassptr_amode(l2, n); - - qsub2[la][jclea - 1] = qgcm2[la] * mass_part_fac_clea; - qsub2[la][jcldy - 1] = qgcm2[la] * mass_part_fac_cldy; - qsub3[la][jclea - 1] = qgcm3[la] * mass_part_fac_clea; - qsub3[la][jcldy - 1] = qgcm3[la] * mass_part_fac_cldy; - } - } - - // ************************************************************************************************* - // Category II: all clear, or cld < 1e-5 - // In this case, zfclea=1 and zfcldy=0 - // ************************************************************************************************* - } else if ((jclea == 1) && (jcldy == 0) && (nsubarea == 1)) { - // - // put all the gases and interstitial aerosols in the clear sub-area - // and set mix-ratios = 0 in cloudy sub-area - // for cloud-borne aerosol, do nothing - // because the grid-cell-mean cloud-borne aerosol will be left - // unchanged (i.e., this routine only changes qqcw when cld >= 1e-5) - // - - for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { - if (0 < lmapcc_all(lmz)) { - qsub1[lmz][jclea - 1] = qgcm1[lmz]; - qsub2[lmz][jclea - 1] = qgcm2[lmz]; - qsub3[lmz][jclea - 1] = qgcm3[lmz]; - qqcwsub2[lmz][jclea - 1] = qqcwgcm2[lmz]; - qqcwsub3[lmz][jclea - 1] = qqcwgcm3[lmz]; - } - } - // ************************************************************************************************* - // Category III: all cloudy, or cld > 0.999 - // in this case, zfcldy= and zfclea=0 - // ************************************************************************************************* - } else if ((jclea == 0) && (jcldy == 1) && (nsubarea == 1)) { - // - // put all the gases and interstitial aerosols in the cloudy sub-area - // and set mix-ratios = 0 in clear sub-area - // - for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { - if (0 < lmapcc_all(lmz)) { - qsub1[lmz][jcldy - 1] = qgcm1[lmz]; - qsub2[lmz][jcldy - 1] = qgcm2[lmz]; - qsub3[lmz][jcldy - 1] = qgcm3[lmz]; - qqcwsub2[lmz][jcldy - 1] = qqcwgcm2[lmz]; - qqcwsub3[lmz][jcldy - 1] = qqcwgcm3[lmz]; - } - } - // ************************************************************************************************* - } else { // this should not happen - EKAT_KERNEL_REQUIRE_MSG(false, "*** modal_aero_amicphys - bad jclea, jcldy, nsubarea!"); - } - // ************************************************************************************************* - - // ------------------------------------------------------------------------------------ - // aerosol water -- how to treat this in sub-areas needs more work/thinking - // currently modal_aero_water_uptake calculates qaerwat using - // the grid-cell mean interstital-aerosol mix-rats and the clear-area rh - for (int jsub = 0; jsub < nsubarea; ++jsub) - for (int i = 0; i < num_modes; ++i) - qaerwatsub3[i][jsub] = qaerwatgcm3[i]; - - // ------------------------------------------------------------------------------------ - if (nsubarea == 1) { - // the j=1 subarea is used for some diagnostics - // but is not used in actual calculations - const int j = 1; - for (int i = 0; i < gas_pcnst(); ++i) { - qsub1[i][j] = 0.0; - qsub2[i][j] = 0.0; - qsub3[i][j] = 0.0; - qqcwsub2[i][j] = 0.0; - qqcwsub3[i][j] = 0.0; - } - } -} - -KOKKOS_INLINE_FUNCTION -void mam_amicphys_1subarea_clear( - const AmicPhysConfig& config, const int nstep, const Real deltat, const int jsub, - const int nsubarea, const bool iscldy_subarea, const Real afracsub, - const Real temp, const Real pmid, const Real pdel, const Real zmid, - const Real pblh, const Real relhum, Real dgn_a[AeroConfig::num_modes()], - Real dgn_awet[AeroConfig::num_modes()], - Real wetdens[AeroConfig::num_modes()], - const Real qgas1[AeroConfig::num_gas_ids()], - const Real qgas3[AeroConfig::num_gas_ids()], - Real qgas4[AeroConfig::num_gas_ids()], - Real qgas_delaa[AeroConfig::num_gas_ids()][nqtendaa()], - const Real qnum3[AeroConfig::num_modes()], - Real qnum4[AeroConfig::num_modes()], - Real qnum_delaa[AeroConfig::num_modes()][nqtendaa()], - const Real qaer3[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], - Real qaer4[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], - Real qaer_delaa[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] - [nqtendaa()], - const Real qwtr3[AeroConfig::num_modes()], - Real qwtr4[AeroConfig::num_modes()]) { - static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); - static constexpr int num_modes = AeroConfig::num_modes(); - static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); - - static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); - // Turn off nh3 for now. This is a future enhancement. - static constexpr int igas_nh3 = -999888777; // Same as mam_refactor - static constexpr int iaer_so4 = static_cast(AeroId::SO4); - static constexpr int iaer_pom = static_cast(AeroId::POM); - - const AeroId gas_to_aer[num_gas_ids] = {AeroId::SOA, AeroId::SO4, - AeroId::None}; - - const bool l_gas_condense_to_mode[num_gas_ids][num_modes] = { - {true, true, true, true}, - {true, true, true, true}, - {false, false, false, false}}; - enum { NA, ANAL, IMPL }; - const int eqn_and_numerics_category[num_gas_ids] = {IMPL, ANAL, ANAL}; - - // air molar density (kmol/m3) - // const Real r_universal = Constants::r_gas; // [mJ/(K mol)] - const Real r_universal = 8.314467591; // [mJ/(mol)] as in mam_refactor - const Real aircon = pmid / (1000 * r_universal * temp); - const Real alnsg_aer[num_modes] = {0.58778666490211906, 0.47000362924573563, - 0.58778666490211906, 0.47000362924573563}; - const Real uptk_rate_factor[num_gas_ids] = {0.81, 1.0, 1.0}; - // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) - // qgas3, qaer3, qnum3 are the current incoming TMRs - // qgas4, qaer4, qnum4 are the updated outgoing TMRs - // - // this routine calculates changes involving - // gas-aerosol exchange (condensation/evaporation) - // growth from smaller to larger modes (renaming) due to condensation - // new particle nucleation - // coagulation - // transfer of particles from hydrophobic modes to hydrophilic modes - // (aging) - // due to condensation and coagulation - // - // qXXXN (X=gas,aer,wat,num; N=1:4) are sub-area mixing ratios - // XXX=gas - gas species - // XXX=aer - aerosol mass species (excluding water) - // XXX=wat - aerosol water - // XXX=num - aerosol number - // N=1 - before gas-phase chemistry - // N=2 - before cloud chemistry - // N=3 - current incoming values (before gas-aerosol exchange, newnuc, - // coag) N=4 - updated outgoing values (after gas-aerosol exchange, - // newnuc, coag) - // - // qXXX_delaa are TMR changes (not tendencies) - // for different processes, which are used to produce history output - // for a clear sub-area, the processes are condensation/evaporation (and - // associated aging), renaming, coagulation, and nucleation - - Real qgas_cur[num_gas_ids]; - for (int i = 0; i < num_gas_ids; ++i) - qgas_cur[i] = qgas3[i]; - Real qaer_cur[num_aerosol_ids][num_modes]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_cur[i][j] = qaer3[i][j]; - - Real qnum_cur[num_modes]; - for (int j = 0; j < num_modes; ++j) - qnum_cur[j] = qnum3[j]; - Real qwtr_cur[num_modes]; - for (int j = 0; j < num_modes; ++j) - qwtr_cur[j] = qwtr3[j]; - - // qgas_netprod_otrproc = gas net production rate from other processes - // such as gas-phase chemistry and emissions (mol/mol/s) - // this allows the condensation (gasaerexch) routine to apply production and - // condensation loss - // together, which is more accurate numerically - // NOTE - must be >= zero, as numerical method can fail when it is negative - // NOTE - currently only the values for h2so4 and nh3 should be non-zero - Real qgas_netprod_otrproc[num_gas_ids] = {}; - if (config.do_cond && config.gaexch_h2so4_uptake_optaa == 2) { - for (int igas = 0; igas < num_gas_ids; ++igas) { - if (igas == igas_h2so4 || igas == igas_nh3) { - // if config.gaexch_h2so4_uptake_optaa == 2, then - // if qgas increases from pre-gaschem to post-cldchem, - // start from the pre-gaschem mix-ratio and add in the production - // during the integration - // if it decreases, - // start from post-cldchem mix-ratio - // *** currently just do this for h2so4 and nh3 - qgas_netprod_otrproc[igas] = (qgas3[igas] - qgas1[igas]) / deltat; - if (qgas_netprod_otrproc[igas] >= 0.0) - qgas_cur[igas] = qgas1[igas]; - else - qgas_netprod_otrproc[igas] = 0.0; - } - } - } - Real qgas_del_cond[num_gas_ids] = {}; - Real qgas_del_nnuc[num_gas_ids] = {}; - Real qgas_del_cond_only[num_gas_ids] = {}; - Real qaer_del_cond[num_aerosol_ids][num_modes] = {}; - Real qaer_del_rnam[num_aerosol_ids][num_modes] = {}; - Real qaer_del_nnuc[num_aerosol_ids][num_modes] = {}; - Real qaer_del_coag[num_aerosol_ids][num_modes] = {}; - Real qaer_delsub_coag_in[num_aerosol_ids][AeroConfig::max_agepair()] = {}; - Real qaer_delsub_cond[num_aerosol_ids][num_modes] = {}; - Real qaer_delsub_coag[num_aerosol_ids][num_modes] = {}; - Real qaer_del_cond_only[num_aerosol_ids][num_modes] = {}; - Real qnum_del_cond[num_modes] = {}; - Real qnum_del_rnam[num_modes] = {}; - Real qnum_del_nnuc[num_modes] = {}; - Real qnum_del_coag[num_modes] = {}; - Real qnum_delsub_cond[num_modes] = {}; - Real qnum_delsub_coag[num_modes] = {}; - Real qnum_del_cond_only[num_modes] = {}; - Real dnclusterdt = 0.0; - - const int ntsubstep = 1; - Real dtsubstep = deltat; - if (ntsubstep > 1) - dtsubstep = deltat / ntsubstep; - Real del_h2so4_gasprod = - haero::max(qgas3[igas_h2so4] - qgas1[igas_h2so4], 0.0) / ntsubstep; - - // loop over multiple time sub-steps - for (int jtsubstep = 1; jtsubstep <= ntsubstep; ++jtsubstep) { - // gas-aerosol exchange - Real uptkrate_h2so4 = 0.0; - Real del_h2so4_aeruptk = 0.0; - Real qaer_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; - Real qgas_avg[num_gas_ids] = {}; - Real qnum_sv1[num_modes] = {}; - Real qaer_sv1[num_aerosol_ids][num_modes] = {}; - Real qgas_sv1[num_gas_ids] = {}; - - if (config.do_cond) { - - const bool l_calc_gas_uptake_coeff = jtsubstep == 1; - Real uptkaer[num_gas_ids][num_modes] = {}; - - for (int i = 0; i < num_gas_ids; ++i) - qgas_sv1[i] = qgas_cur[i]; - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_sv1[j][i] = qaer_cur[j][i]; - - // time sub-step - const Real dtsub_soa_fixed = -1.0; - // Integration order - const int nghq = 2; - const int ntot_soamode = 4; - int niter_out = 0; - Real g0_soa_out = 0; - // gasaerexch::mam_gasaerexch_1subarea( - // nghq, igas_h2so4, igas_nh3, ntot_soamode, gas_to_aer, iaer_so4, - // iaer_pom, l_calc_gas_uptake_coeff, l_gas_condense_to_mode, - // eqn_and_numerics_category, dtsubstep, dtsub_soa_fixed, temp, pmid, - // aircon, num_gas_ids, qgas_cur, qgas_avg, qgas_netprod_otrproc, - // qaer_cur, qnum_cur, dgn_awet, alnsg_aer, uptk_rate_factor, uptkaer, - // uptkrate_h2so4, niter_out, g0_soa_out); - - if (config.newnuc_h2so4_conc_optaa == 11) - qgas_avg[igas_h2so4] = - 0.5 * (qgas_sv1[igas_h2so4] + qgas_cur[igas_h2so4]); - else if (config.newnuc_h2so4_conc_optaa == 12) - qgas_avg[igas_h2so4] = qgas_cur[igas_h2so4]; - - for (int i = 0; i < num_gas_ids; ++i) - qgas_del_cond[i] += - (qgas_cur[i] - (qgas_sv1[i] + qgas_netprod_otrproc[i] * dtsubstep)); - - for (int i = 0; i < num_modes; ++i) - qnum_delsub_cond[i] = qnum_cur[i] - qnum_sv1[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_delsub_cond[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; - - // qaer_del_grow4rnam = change in qaer_del_cond during latest condensation - // calculations - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_delsub_grow4rnam[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; - for (int i = 0; i < num_gas_ids; ++i) - qgas_del_cond_only[i] = qgas_del_cond[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_del_cond_only[i][j] = qaer_delsub_cond[i][j]; - for (int i = 0; i < num_modes; ++i) - qnum_del_cond_only[i] = qnum_delsub_cond[i]; - del_h2so4_aeruptk = - qgas_cur[igas_h2so4] - - (qgas_sv1[igas_h2so4] + qgas_netprod_otrproc[igas_h2so4] * dtsubstep); - } else { - for (int i = 0; i < num_gas_ids; ++i) - qgas_avg[i] = qgas_cur[i]; - } - - // renaming after "continuous growth" - if (config.do_rename) { - constexpr int nmodes = AeroConfig::num_modes(); - constexpr int naerosol_species = AeroConfig::num_aerosol_ids(); - const Real smallest_dryvol_value = 1.0e-25; // BAD_CONSTANT - const int dest_mode_of_mode[nmodes] = {-1, 0, -1, -1}; - - Real qnumcw_cur[num_modes] = {}; - Real qaercw_cur[num_aerosol_ids][num_modes] = {}; - Real qaercw_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; - Real mean_std_dev[nmodes]; - Real fmode_dist_tail_fac[nmodes]; - Real v2n_lo_rlx[nmodes]; - Real v2n_hi_rlx[nmodes]; - Real ln_diameter_tail_fac[nmodes]; - int num_pairs = 0; - Real diameter_cutoff[nmodes]; - Real ln_dia_cutoff[nmodes]; - Real diameter_threshold[nmodes]; - Real mass_2_vol[naerosol_species] = {0.15, - 6.4971751412429377e-002, - 0.15, - 7.0588235294117650e-003, - 3.0789473684210526e-002, - 5.1923076923076926e-002, - 156.20986883198000}; - - rename::find_renaming_pairs(dest_mode_of_mode, // in - mean_std_dev, // out - fmode_dist_tail_fac, // out - v2n_lo_rlx, // out - v2n_hi_rlx, // out - ln_diameter_tail_fac, // out - num_pairs, // out - diameter_cutoff, // out - ln_dia_cutoff, diameter_threshold); - - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_sv1[j][i] = qaer_cur[j][i]; - Real dgnum_amode[nmodes]; - for (int m = 0; m < nmodes; ++m) { - dgnum_amode[m] = modes(m).nom_diameter; - } - - { - Real qmol_i_cur[num_modes][num_aerosol_ids]; - Real qmol_i_del[num_modes][num_aerosol_ids]; - Real qmol_c_cur[num_modes][num_aerosol_ids]; - Real qmol_c_del[num_modes][num_aerosol_ids]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) { - qmol_i_cur[i][j] = qaer_cur[j][i]; - qmol_i_del[i][j] = qaer_delsub_grow4rnam[j][i]; - qmol_c_cur[i][j] = qaercw_cur[j][i]; - qmol_c_del[i][j] = qaercw_delsub_grow4rnam[j][i]; - } - Rename rename; - rename.mam_rename_1subarea_( - iscldy_subarea, smallest_dryvol_value, dest_mode_of_mode, - mean_std_dev, fmode_dist_tail_fac, v2n_lo_rlx, v2n_hi_rlx, - ln_diameter_tail_fac, num_pairs, diameter_cutoff, ln_dia_cutoff, - diameter_threshold, mass_2_vol, dgnum_amode, qnum_cur, qmol_i_cur, - qmol_i_del, qnumcw_cur, qmol_c_cur, qmol_c_del); - - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) { - qaer_cur[j][i] = qmol_i_cur[i][j]; - qaer_delsub_grow4rnam[j][i] = qmol_i_del[i][j]; - qaercw_cur[j][i] = qmol_c_cur[i][j]; - qaercw_delsub_grow4rnam[j][i] = qmol_c_del[i][j]; - } - } - - for (int i = 0; i < num_modes; ++i) - qnum_del_rnam[i] += qnum_cur[i] - qnum_sv1[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_del_rnam[i][j] += qaer_cur[i][j] - qaer_sv1[i][j]; - } - - // new particle formation (nucleation) - if (config.do_newnuc) { - for (int i = 0; i < num_gas_ids; ++i) - qgas_sv1[i] = qgas_cur[i]; - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - Real qaer_cur_tmp[num_modes][num_aerosol_ids]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) { - qaer_sv1[j][i] = qaer_cur[j][i]; - qaer_cur_tmp[i][j] = qaer_cur[j][i]; - } - Real dnclusterdt_substep = 0; - Real dndt_ait = 0; - Real dmdt_ait = 0; - Real dso4dt_ait = 0; - Real dnh4dt_ait = 0; - Nucleation nucleation; - Nucleation::Config config; - // config.dens_so4a_host = 1770; - config.mw_nh4a_host = 115; - config.mw_so4a_host = 115; - // config.accom_coef_h2so4 = 0.65; - AeroConfig aero_config; - nucleation.init(aero_config, config); - // new version after switching from box model version of nucleation - // compute_tendencies_( - // // Real deltat, - // // Real temp_in, - // // Real press_in, - // // Real zm_in, - // // Real pblh_in, - // // Real relhum, - // // Real uptkrate_h2so4, - // const int nsize, // missing? - // const Real dp_lo_mode, - // const Real dp_hi_mode, - // // const Real qgas_cur[num_gases], - // // const Real qgas_avg[num_gases], - // int &isize_nuc, - // // Real &qnuma_del, - // // Real &qso4a_del, - // // Real &qnh4a_del, - // // Real &qh2so4_del, - // Real &qnh3_del, - // // Real &dnclusterdt - // ) - // old, box-model-based version - // nucleation.compute_tendencies_( - // dtsubstep, temp, pmid, aircon, zmid, pblh, relhum, uptkrate_h2so4, - // del_h2so4_gasprod, del_h2so4_aeruptk, qgas_cur, qgas_avg, qnum_cur, - // qaer_cur_tmp, qwtr_cur, dndt_ait, dmdt_ait, dso4dt_ait, dnh4dt_ait, - // dnclusterdt_substep); - // nucleation.compute_tendencies_( - // dtsubstep, // deltat - // temp, // temp_in - // pmid, // press_in - // aircon, // ? - // zmid, // zm_in - // pblh, // pblh_in - // relhum, // relhum - // uptkrate_h2so4, // uptkrate_h2so4 - // // nsize? - // // dp_lo_mode? - // // dp_hi_mode? - // del_h2so4_aeruptk, // uptkrate_h2so4? - // qgas_cur, // qgas_cur[num_gases]? - // qgas_avg, // qgas_avg[num_gases]? - // // isize_nuc? - // qnum_cur, // qnuma_del? - // qaer_cur_tmp, // ? - // qwtr_cur, // ? - // dndt_ait, // - // dmdt_ait, // - // dso4dt_ait, // qso4a_del - // dnh4dt_ait, // qnh4a_del - // del_h2so4_gasprod, // qh2so4_del? - // // qnh3_del? - // dnclusterdt_substep // dnclusterdt - // ); - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_cur[j][i] = qaer_cur_tmp[i][j]; - - //! Apply the tendencies to the prognostics. - const int nait = static_cast(ModeIndex::Aitken); - qnum_cur[nait] += dndt_ait * dtsubstep; - - if (dso4dt_ait > 0.0) { - static constexpr int iaer_so4 = static_cast(AeroId::SO4); - static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); - - Real delta_q = dso4dt_ait * dtsubstep; - qaer_cur[iaer_so4][nait] += delta_q; - delta_q = haero::min(delta_q, qgas_cur[igas_h2so4]); - qgas_cur[igas_h2so4] -= delta_q; - } - - if (igas_nh3 > 0 && dnh4dt_ait > 0.0) { - static constexpr int iaer_nh4 = - -9999999; // static_cast(AeroId::NH4); - - Real delta_q = dnh4dt_ait * dtsubstep; - qaer_cur[iaer_nh4][nait] += delta_q; - delta_q = haero::min(delta_q, qgas_cur[igas_nh3]); - qgas_cur[igas_nh3] -= delta_q; - } - for (int i = 0; i < num_gas_ids; ++i) - qgas_del_nnuc[i] += (qgas_cur[i] - qgas_sv1[i]); - for (int i = 0; i < num_modes; ++i) - qnum_del_nnuc[i] += (qnum_cur[i] - qnum_sv1[i]); - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_del_nnuc[j][i] += (qaer_cur[j][i] - qaer_sv1[j][i]); - - dnclusterdt = dnclusterdt + dnclusterdt_substep * (dtsubstep / deltat); - } - - // coagulation part - if (config.do_coag) { - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_sv1[j][i] = qaer_cur[j][i]; - // coagulation::mam_coag_1subarea(dtsubstep, temp, pmid, aircon, dgn_a, - // dgn_awet, wetdens, qnum_cur, qaer_cur, - // qaer_delsub_coag_in); - for (int i = 0; i < num_modes; ++i) - qnum_delsub_coag[i] = qnum_cur[i] - qnum_sv1[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_delsub_coag[j][i] = qaer_cur[j][i] - qaer_sv1[j][i]; - } - - // primary carbon aging - - aging::mam_pcarbon_aging_1subarea( - dgn_a, qnum_cur, qnum_delsub_cond, qnum_delsub_coag, qaer_cur, - qaer_delsub_cond, qaer_delsub_coag, qaer_delsub_coag_in); - - // accumulate sub-step q-dels - if (config.do_coag) { - for (int i = 0; i < num_modes; ++i) - qnum_del_coag[i] += qnum_delsub_coag[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_del_coag[j][i] += qaer_delsub_coag[j][i]; - } - if (config.do_cond) { - for (int i = 0; i < num_modes; ++i) - qnum_del_cond[i] += qnum_delsub_cond[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_del_cond[j][i] += qaer_delsub_cond[j][i]; - } - } - - // final mix ratios - for (int i = 0; i < num_gas_ids; ++i) - qgas4[i] = qgas_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer4[j][i] = qaer_cur[j][i]; - for (int i = 0; i < num_modes; ++i) - qnum4[i] = qnum_cur[i]; - for (int i = 0; i < num_modes; ++i) - qwtr4[i] = qwtr_cur[i]; - - // final mix ratio changes - for (int i = 0; i < num_gas_ids; ++i) { - qgas_delaa[i][iqtend_cond()] = qgas_del_cond[i]; - qgas_delaa[i][iqtend_rnam()] = 0.0; - qgas_delaa[i][iqtend_nnuc()] = qgas_del_nnuc[i]; - qgas_delaa[i][iqtend_coag()] = 0.0; - qgas_delaa[i][iqtend_cond_only()] = qgas_del_cond_only[i]; - } - for (int i = 0; i < num_modes; ++i) { - qnum_delaa[i][iqtend_cond()] = qnum_del_cond[i]; - qnum_delaa[i][iqtend_rnam()] = qnum_del_rnam[i]; - qnum_delaa[i][iqtend_nnuc()] = qnum_del_nnuc[i]; - qnum_delaa[i][iqtend_coag()] = qnum_del_coag[i]; - qnum_delaa[i][iqtend_cond_only()] = qnum_del_cond_only[i]; - } - for (int j = 0; j < num_aerosol_ids; ++j) { - for (int i = 0; i < num_modes; ++i) { - qaer_delaa[j][i][iqtend_cond()] = qaer_del_cond[j][i]; - qaer_delaa[j][i][iqtend_rnam()] = qaer_del_rnam[j][i]; - qaer_delaa[j][i][iqtend_nnuc()] = qaer_del_nnuc[j][i]; - qaer_delaa[j][i][iqtend_coag()] = qaer_del_coag[j][i]; - qaer_delaa[j][i][iqtend_cond_only()] = qaer_del_cond_only[j][i]; - } - } -} - -KOKKOS_INLINE_FUNCTION -void mam_amicphys_1subarea_cloudy( - const AmicPhysConfig& config, const int nstep, const Real deltat, const int jsub, - const int nsubarea, const bool iscldy_subarea, const Real afracsub, - const Real temp, const Real pmid, const Real pdel, const Real zmid, - const Real pblh, const Real relhum, Real dgn_a[AeroConfig::num_modes()], - Real dgn_awet[AeroConfig::num_modes()], - Real wetdens[AeroConfig::num_modes()], - const Real qgas1[AeroConfig::num_gas_ids()], - const Real qgas3[AeroConfig::num_gas_ids()], - Real qgas4[AeroConfig::num_gas_ids()], - Real qgas_delaa[AeroConfig::num_gas_ids()][nqtendaa()], - const Real qnum3[AeroConfig::num_modes()], - Real qnum4[AeroConfig::num_modes()], - Real qnum_delaa[AeroConfig::num_modes()][nqtendaa()], - const Real qaer2[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], - const Real qaer3[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], - Real qaer4[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()], - Real qaer_delaa[AeroConfig::num_aerosol_ids()][AeroConfig::num_modes()] - [nqtendaa()], - const Real qwtr3[AeroConfig::num_modes()], - Real qwtr4[AeroConfig::num_modes()], - const Real qnumcw3[AeroConfig::num_modes()], - Real qnumcw4[AeroConfig::num_modes()], - Real qnumcw_delaa[AeroConfig::num_modes()][nqqcwtendaa()], - const Real qaercw2[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], - const Real qaercw3[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], - Real qaercw4[AeroConfig::num_gas_ids()][AeroConfig::num_modes()], - Real qaercw_delaa[AeroConfig::num_gas_ids()][AeroConfig::num_modes()] - [nqqcwtendaa()]) { - - // - // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) - // qgas3, qaer3, qaercw3, qnum3, qnumcw3 are the current incoming TMRs - // qgas4, qaer4, qaercw4, qnum4, qnumcw4 are the updated outgoing TMRs - // - // when config.do_cond = false, this routine only calculates changes involving - // growth from smaller to larger modes (renaming) following cloud chemistry - // so gas TMRs are not changed - // when config.do_cond = true, this routine also calculates changes involving - // gas-aerosol exchange (condensation/evaporation) - // transfer of particles from hydrophobic modes to hydrophilic modes - // (aging) - // due to condensation - // currently this routine does not do - // new particle nucleation - because h2so4 gas conc. should be very low in - // cloudy air coagulation - because cloud-borne aerosol would need to be - // included - // - - // qXXXN (X=gas,aer,wat,num; N=1:4) are sub-area mixing ratios - // XXX=gas - gas species - // XXX=aer - aerosol mass species (excluding water) - // XXX=wat - aerosol water - // XXX=num - aerosol number - // N=1 - before gas-phase chemistry - // N=2 - before cloud chemistry - // N=3 - current incoming values (before gas-aerosol exchange, newnuc, - // coag) N=4 - updated outgoing values (after gas-aerosol exchange, - // newnuc, coag) - // - // qXXX_delaa are TMR changes (not tendencies) - // for different processes, which are used to produce history output - // for a clear sub-area, the processes are condensation/evaporation (and - // associated aging), - // renaming, coagulation, and nucleation - - // qxxx_del_yyyy are mix-ratio changes over full time step (deltat) - // qxxx_delsub_yyyy are mix-ratio changes over time sub-step (dtsubstep) - - static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); - static constexpr int num_modes = AeroConfig::num_modes(); - static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); - - static constexpr int igas_h2so4 = static_cast(GasId::H2SO4); - // Turn off nh3 for now. This is a future enhancement. - static constexpr int igas_nh3 = -999888777; // Same as mam_refactor - static constexpr int iaer_so4 = static_cast(AeroId::SO4); - static constexpr int iaer_pom = static_cast(AeroId::POM); - - const AeroId gas_to_aer[num_gas_ids] = {AeroId::SOA, AeroId::SO4, - AeroId::None}; - const bool l_gas_condense_to_mode[num_gas_ids][num_modes] = { - {true, true, true, true}, - {true, true, true, true}, - {false, false, false, false}}; - enum { NA, ANAL, IMPL }; - const int eqn_and_numerics_category[num_gas_ids] = {IMPL, ANAL, ANAL}; - // air molar density (kmol/m3) - // In order to try to match the results in mam_refactor - // set r_universal as [mJ/(mol)] as in mam_refactor. - // const Real r_universal = Constants::r_gas; // [mJ/(K mol)] - const Real r_universal = 8.314467591; // [mJ/(mol)] as in mam_refactor - const Real aircon = pmid / (1000 * r_universal * temp); - const Real alnsg_aer[num_modes] = {0.58778666490211906, 0.47000362924573563, - 0.58778666490211906, 0.47000362924573563}; - const Real uptk_rate_factor[num_gas_ids] = {0.81, 1.0, 1.0}; - - Real qgas_cur[num_gas_ids]; - for (int i = 0; i < num_gas_ids; ++i) - qgas_cur[i] = qgas3[i]; - Real qaer_cur[num_aerosol_ids][num_modes]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_cur[i][j] = qaer3[i][j]; - - Real qnum_cur[num_modes]; - for (int j = 0; j < num_modes; ++j) - qnum_cur[j] = qnum3[j]; - Real qwtr_cur[num_modes]; - for (int j = 0; j < num_modes; ++j) - qwtr_cur[j] = qwtr3[j]; - - Real qnumcw_cur[num_modes]; - for (int j = 0; j < num_modes; ++j) - qnumcw_cur[j] = qnumcw3[j]; - - Real qaercw_cur[num_gas_ids][num_modes]; - for (int i = 0; i < num_gas_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw_cur[i][j] = qaercw3[i][j]; - - Real qgas_netprod_otrproc[num_gas_ids] = {}; - if (config.do_cond && config.gaexch_h2so4_uptake_optaa == 2) { - for (int igas = 0; igas < num_gas_ids; ++igas) { - if (igas == igas_h2so4 || igas == igas_nh3) { - // if gaexch_h2so4_uptake_optaa == 2, then - // if qgas increases from pre-gaschem to post-cldchem, - // start from the pre-gaschem mix-ratio and add in the production - // during the integration - // if it decreases, - // start from post-cldchem mix-ratio - // *** currently just do this for h2so4 and nh3 - qgas_netprod_otrproc[igas] = (qgas3[igas] - qgas1[igas]) / deltat; - if (qgas_netprod_otrproc[igas] >= 0.0) - qgas_cur[igas] = qgas1[igas]; - else - qgas_netprod_otrproc[igas] = 0.0; - } - } - } - Real qgas_del_cond[num_gas_ids] = {}; - Real qgas_del_nnuc[num_gas_ids] = {}; - Real qgas_del_cond_only[num_gas_ids] = {}; - Real qaer_del_cond[num_aerosol_ids][num_modes] = {}; - Real qaer_del_rnam[num_aerosol_ids][num_modes] = {}; - Real qaer_del_nnuc[num_aerosol_ids][num_modes] = {}; - Real qaer_del_coag[num_aerosol_ids][num_modes] = {}; - Real qaer_delsub_cond[num_aerosol_ids][num_modes] = {}; - Real qaer_delsub_coag[num_aerosol_ids][num_modes] = {}; - Real qaer_del_cond_only[num_aerosol_ids][num_modes] = {}; - Real qaercw_del_rnam[num_aerosol_ids][num_modes] = {}; - Real qnum_del_cond[num_modes] = {}; - Real qnum_del_rnam[num_modes] = {}; - Real qnum_del_nnuc[num_modes] = {}; - Real qnum_del_coag[num_modes] = {}; - Real qnum_delsub_cond[num_modes] = {}; - Real qnum_delsub_coag[num_modes] = {}; - Real qnum_del_cond_only[num_modes] = {}; - Real qnumcw_del_rnam[num_modes] = {}; - Real qaer_delsub_coag_in[num_aerosol_ids][AeroConfig::max_agepair()] = {}; - - const int ntsubstep = 1; - Real dtsubstep = deltat; - if (ntsubstep > 1) - dtsubstep = deltat / ntsubstep; - - // loop over multiple time sub-steps - - for (int jtsubstep = 1; jtsubstep <= ntsubstep; ++jtsubstep) { - // gas-aerosol exchange - Real uptkrate_h2so4 = 0.0; - Real qgas_avg[num_gas_ids] = {}; - Real qgas_sv1[num_gas_ids] = {}; - Real qnum_sv1[num_modes] = {}; - Real qaer_sv1[num_aerosol_ids][num_modes] = {}; - Real qaer_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; - - if (config.do_cond) { - - const bool l_calc_gas_uptake_coeff = jtsubstep == 1; - Real uptkaer[num_gas_ids][num_modes] = {}; - - for (int i = 0; i < num_gas_ids; ++i) - qgas_sv1[i] = qgas_cur[i]; - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_sv1[j][i] = qaer_cur[j][i]; - - const int nghq = 2; - const int ntot_soamode = 4; - int niter_out = 0; - Real g0_soa_out = 0; - // time sub-step - const Real dtsub_soa_fixed = -1.0; - // gasaerexch::mam_gasaerexch_1subarea( - // nghq, igas_h2so4, igas_nh3, ntot_soamode, gas_to_aer, iaer_so4, - // iaer_pom, l_calc_gas_uptake_coeff, l_gas_condense_to_mode, - // eqn_and_numerics_category, dtsubstep, dtsub_soa_fixed, temp, pmid, - // aircon, num_gas_ids, qgas_cur, qgas_avg, qgas_netprod_otrproc, - // qaer_cur, qnum_cur, dgn_awet, alnsg_aer, uptk_rate_factor, uptkaer, - // uptkrate_h2so4, niter_out, g0_soa_out); - - if (config.newnuc_h2so4_conc_optaa == 11) - qgas_avg[igas_h2so4] = - 0.5 * (qgas_sv1[igas_h2so4] + qgas_cur[igas_h2so4]); - else if (config.newnuc_h2so4_conc_optaa == 12) - qgas_avg[igas_h2so4] = qgas_cur[igas_h2so4]; - - for (int i = 0; i < num_gas_ids; ++i) - qgas_del_cond[i] += - (qgas_cur[i] - (qgas_sv1[i] + qgas_netprod_otrproc[i] * dtsubstep)); - - for (int i = 0; i < num_modes; ++i) - qnum_delsub_cond[i] = qnum_cur[i] - qnum_sv1[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_delsub_cond[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; - - // qaer_del_grow4rnam = change in qaer_del_cond during latest condensation - // calculations - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_delsub_grow4rnam[i][j] = qaer_cur[i][j] - qaer_sv1[i][j]; - for (int i = 0; i < num_gas_ids; ++i) - qgas_del_cond_only[i] = qgas_del_cond[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_del_cond_only[i][j] = qaer_delsub_cond[i][j]; - for (int i = 0; i < num_modes; ++i) - qnum_del_cond_only[i] = qnum_delsub_cond[i]; - - } else { - for (int i = 0; i < num_gas_ids; ++i) - qgas_avg[i] = qgas_cur[i]; - } - // renaming after "continuous growth" - if (config.do_rename) { - constexpr int nmodes = AeroConfig::num_modes(); - constexpr int naerosol_species = AeroConfig::num_aerosol_ids(); - const Real smallest_dryvol_value = 1.0e-25; // BAD_CONSTANT - const int dest_mode_of_mode[nmodes] = {-1, 0, -1, -1}; - - Real qnumcw_cur[num_modes] = {}; - Real qaercw_cur[num_aerosol_ids][num_modes] = {}; - Real qaercw_delsub_grow4rnam[num_aerosol_ids][num_modes] = {}; - Real mean_std_dev[nmodes]; - Real fmode_dist_tail_fac[nmodes]; - Real v2n_lo_rlx[nmodes]; - Real v2n_hi_rlx[nmodes]; - Real ln_diameter_tail_fac[nmodes]; - int num_pairs = 0; - Real diameter_cutoff[nmodes]; - Real ln_dia_cutoff[nmodes]; - Real diameter_threshold[nmodes]; - Real mass_2_vol[naerosol_species] = {0.15, - 6.4971751412429377e-002, - 0.15, - 7.0588235294117650e-003, - 3.0789473684210526e-002, - 5.1923076923076926e-002, - 156.20986883198000}; - - rename::find_renaming_pairs(dest_mode_of_mode, // in - mean_std_dev, // out - fmode_dist_tail_fac, // out - v2n_lo_rlx, // out - v2n_hi_rlx, // out - ln_diameter_tail_fac, // out - num_pairs, // out - diameter_cutoff, // out - ln_dia_cutoff, diameter_threshold); - - for (int i = 0; i < num_modes; ++i) - qnum_sv1[i] = qnum_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_sv1[j][i] = qaer_cur[j][i]; - Real dgnum_amode[nmodes]; - for (int m = 0; m < nmodes; ++m) { - dgnum_amode[m] = modes(m).nom_diameter; - } - - // qaercw_delsub_grow4rnam = change in qaercw from cloud chemistry - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw_delsub_grow4rnam[i][j] = - (qaercw3[i][j] - qaercw2[i][j]) / ntsubstep; - Real qnumcw_sv1[num_modes]; - for (int i = 0; i < num_modes; ++i) - qnumcw_sv1[i] = qnumcw_cur[i]; - Real qaercw_sv1[num_aerosol_ids][num_modes]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw_sv1[i][j] = qaercw_cur[i][j]; - - { - Real qmol_i_cur[num_modes][num_aerosol_ids]; - Real qmol_i_del[num_modes][num_aerosol_ids]; - Real qmol_c_cur[num_modes][num_aerosol_ids]; - Real qmol_c_del[num_modes][num_aerosol_ids]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) { - qmol_i_cur[i][j] = qaer_cur[j][i]; - qmol_i_del[i][j] = qaer_delsub_grow4rnam[j][i]; - qmol_c_cur[i][j] = qaercw_cur[j][i]; - qmol_c_del[i][j] = qaercw_delsub_grow4rnam[j][i]; - } - - Rename rename; - rename.mam_rename_1subarea_( - iscldy_subarea, smallest_dryvol_value, dest_mode_of_mode, - mean_std_dev, fmode_dist_tail_fac, v2n_lo_rlx, v2n_hi_rlx, - ln_diameter_tail_fac, num_pairs, diameter_cutoff, ln_dia_cutoff, - diameter_threshold, mass_2_vol, dgnum_amode, qnum_cur, qmol_i_cur, - qmol_i_del, qnumcw_cur, qmol_c_cur, qmol_c_del); - - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) { - qaer_cur[j][i] = qmol_i_cur[i][j]; - qaer_delsub_grow4rnam[j][i] = qmol_i_del[i][j]; - qaercw_cur[j][i] = qmol_c_cur[i][j]; - qaercw_delsub_grow4rnam[j][i] = qmol_c_del[i][j]; - } - } - for (int i = 0; i < num_modes; ++i) - qnum_del_rnam[i] += qnum_cur[i] - qnum_sv1[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaer_del_rnam[i][j] += qaer_cur[i][j] - qaer_sv1[i][j]; - for (int i = 0; i < num_modes; ++i) - qnumcw_del_rnam[i] += qnumcw_cur[i] - qnumcw_sv1[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw_del_rnam[i][j] += qaercw_cur[i][j] - qaercw_sv1[i][j]; - } - - // primary carbon aging - if (config.do_cond) { - aging::mam_pcarbon_aging_1subarea( - dgn_a, qnum_cur, qnum_delsub_cond, qnum_delsub_coag, qaer_cur, - qaer_delsub_cond, qaer_delsub_coag, qaer_delsub_coag_in); - } - // accumulate sub-step q-dels - if (config.do_cond) { - for (int i = 0; i < num_modes; ++i) - qnum_del_cond[i] += qnum_delsub_cond[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer_del_cond[j][i] += qaer_delsub_cond[j][i]; - } - } - // final mix ratios - for (int i = 0; i < num_gas_ids; ++i) - qgas4[i] = qgas_cur[i]; - for (int j = 0; j < num_aerosol_ids; ++j) - for (int i = 0; i < num_modes; ++i) - qaer4[j][i] = qaer_cur[j][i]; - for (int i = 0; i < num_modes; ++i) - qnum4[i] = qnum_cur[i]; - for (int i = 0; i < num_modes; ++i) - qwtr4[i] = qwtr_cur[i]; - for (int i = 0; i < num_modes; ++i) - qnumcw4[i] = qnumcw_cur[i]; - for (int i = 0; i < num_gas_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw4[i][j] = qaercw_cur[i][j]; - - // final mix ratio changes - for (int i = 0; i < num_gas_ids; ++i) { - qgas_delaa[i][iqtend_cond()] = qgas_del_cond[i]; - qgas_delaa[i][iqtend_rnam()] = 0.0; - qgas_delaa[i][iqtend_nnuc()] = qgas_del_nnuc[i]; - qgas_delaa[i][iqtend_coag()] = 0.0; - qgas_delaa[i][iqtend_cond_only()] = qgas_del_cond_only[i]; - } - for (int i = 0; i < num_modes; ++i) { - qnum_delaa[i][iqtend_cond()] = qnum_del_cond[i]; - qnum_delaa[i][iqtend_rnam()] = qnum_del_rnam[i]; - qnum_delaa[i][iqtend_nnuc()] = qnum_del_nnuc[i]; - qnum_delaa[i][iqtend_coag()] = qnum_del_coag[i]; - qnum_delaa[i][iqtend_cond_only()] = qnum_del_cond_only[i]; - } - for (int j = 0; j < num_aerosol_ids; ++j) { - for (int i = 0; i < num_modes; ++i) { - qaer_delaa[j][i][iqtend_cond()] = qaer_del_cond[j][i]; - qaer_delaa[j][i][iqtend_rnam()] = qaer_del_rnam[j][i]; - qaer_delaa[j][i][iqtend_nnuc()] = qaer_del_nnuc[j][i]; - qaer_delaa[j][i][iqtend_coag()] = qaer_del_coag[j][i]; - qaer_delaa[j][i][iqtend_cond_only()] = qaer_del_cond_only[j][i]; - } - } - for (int i = 0; i < num_modes; ++i) - qnumcw_delaa[i][iqqcwtend_rnam()] = qnumcw_del_rnam[i]; - for (int i = 0; i < num_aerosol_ids; ++i) - for (int j = 0; j < num_modes; ++j) - qaercw_delaa[i][j][iqqcwtend_rnam()] = qaercw_del_rnam[i][j]; -} - -KOKKOS_INLINE_FUNCTION -void mam_amicphys_1gridcell( - const AmicPhysConfig& config, const int nstep, const Real deltat, const int nsubarea, - const int ncldy_subarea, const bool iscldy_subarea[maxsubarea()], - const Real afracsub[maxsubarea()], const Real temp, const Real pmid, - const Real pdel, const Real zmid, const Real pblh, - const Real relhumsub[maxsubarea()], Real dgn_a[AeroConfig::num_modes()], - Real dgn_awet[AeroConfig::num_modes()], - Real wetdens[AeroConfig::num_modes()], - const Real qsub1[AeroConfig::num_gas_ids()][maxsubarea()], - const Real qsub2[AeroConfig::num_gas_ids()][maxsubarea()], - const Real qqcwsub2[AeroConfig::num_gas_ids()][maxsubarea()], - const Real qsub3[AeroConfig::num_gas_ids()][maxsubarea()], - const Real qqcwsub3[AeroConfig::num_gas_ids()][maxsubarea()], - Real qaerwatsub3[AeroConfig::num_modes()][maxsubarea()], - Real qsub4[AeroConfig::num_gas_ids()][maxsubarea()], - Real qqcwsub4[AeroConfig::num_gas_ids()][maxsubarea()], - Real qaerwatsub4[AeroConfig::num_modes()][maxsubarea()], - Real qsub_tendaa[AeroConfig::num_gas_ids()][nqtendaa()][maxsubarea()], - Real qqcwsub_tendaa[AeroConfig::num_gas_ids()][nqqcwtendaa()][maxsubarea()]) { - - // - // calculates changes to gas and aerosol sub-area TMRs (tracer mixing ratios) - // qsub3 and qqcwsub3 are the incoming current TMRs - // qsub4 and qqcwsub4 are the outgoing updated TMRs - // - // qsubN and qqcwsubN (N=1:4) are tracer mixing ratios (TMRs, mol/mol or - // #/kmol) in sub-areas - // currently there are just clear and cloudy sub-areas - // the N=1:4 have same meanings as for qgcmN - // N=1 - before gas-phase chemistry - // N=2 - before cloud chemistry - // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) - // N=4 - outgoing values (after gas-aerosol exchange, newnuc, coag) - // qsub_tendaa and qqcwsub_tendaa are TMR tendencies - // for different processes, which are used to produce history output - // the processes are condensation/evaporation (and associated aging), - // renaming, coagulation, and nucleation - - static constexpr int num_gas_ids = AeroConfig::num_gas_ids(); - static constexpr int num_modes = AeroConfig::num_modes(); - static constexpr int num_aerosol_ids = AeroConfig::num_aerosol_ids(); - - // the q--4 values will be equal to q--3 values unless they get changed - for (int i = 0; i < num_gas_ids; ++i) - for (int j = 0; j < maxsubarea(); ++j) { - qsub4[i][j] = qsub3[i][j]; - qqcwsub4[i][j] = qqcwsub3[i][j]; - } - for (int i = 0; i < num_modes; ++i) - for (int j = 0; j < maxsubarea(); ++j) - qaerwatsub4[i][j] = qaerwatsub3[i][j]; - for (int i = 0; i < num_gas_ids; ++i) - for (int j = 0; j < nqtendaa(); ++j) - for (int k = 0; k < maxsubarea(); ++k) - qsub_tendaa[i][j][k] = 0; - for (int i = 0; i < num_gas_ids; ++i) - for (int j = 0; j < nqqcwtendaa(); ++j) - for (int k = 0; k < maxsubarea(); ++k) - qqcwsub_tendaa[i][j][k] = 0.0; - - for (int jsub = 0; jsub < nsubarea; ++jsub) { - AmicPhysConfig sub_config = config; - if (iscldy_subarea[jsub]) { - sub_config.do_cond = config.do_cond; - sub_config.do_rename = config.do_rename; - sub_config.do_newnuc = false; - sub_config.do_coag = false; - } - const bool do_map_gas_sub = sub_config.do_cond || sub_config.do_newnuc; - - // map incoming sub-area mix-ratios to gas/aer/num arrays - Real qgas1[num_gas_ids] = {}; - Real qgas3[num_gas_ids] = {}; - Real qgas4[num_gas_ids] = {}; - if (do_map_gas_sub) { - // for cldy subarea, only do gases if doing gaexch - for (int igas = 0; igas < 2; ++igas) { - const int l = lmap_gas(igas); - qgas1[igas] = qsub1[l][jsub] * fcvt_gas(igas); - qgas3[igas] = qsub3[l][jsub] * fcvt_gas(igas); - qgas4[igas] = qgas3[igas]; - } - } - Real qaer2[num_aerosol_ids][num_modes] = {}; - Real qaer3[num_aerosol_ids][num_modes] = {}; - Real qnum3[num_modes] = {}; - Real qaer4[num_aerosol_ids][num_modes] = {}; - Real qnum4[num_modes] = {}; - Real qwtr3[num_modes] = {}; - Real qwtr4[num_modes] = {}; - for (int n = 0; n < num_modes; ++n) { - qnum3[n] = qsub3[n][jsub] * fcvt_num(); - qnum4[n] = qnum3[n]; - for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { - qaer2[iaer][n] = qsub2[iaer][jsub] * fcvt_aer(iaer); - qaer3[iaer][n] = qsub3[iaer][jsub] * fcvt_aer(iaer); - qaer4[iaer][n] = qaer3[iaer][n]; - } - qwtr3[n] = qaerwatsub3[n][jsub] * fcvt_wtr(); - qwtr4[n] = qwtr3[n]; - } - Real qaercw2[num_aerosol_ids][num_modes] = {}; - Real qaercw3[num_aerosol_ids][num_modes] = {}; - Real qnumcw3[num_modes] = {}; - Real qaercw4[num_aerosol_ids][num_modes] = {}; - Real qnumcw4[num_modes] = {}; - if (iscldy_subarea[jsub]) { - // only do cloud-borne for cloudy - for (int n = 0; n < num_modes; ++n) { - qnumcw3[n] = qqcwsub3[n][jsub] * fcvt_num(); - qnumcw4[n] = qnumcw3[n]; - for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { - qaercw2[iaer][n] = qqcwsub2[n][jsub] * fcvt_aer(iaer); - qaercw3[iaer][n] = qqcwsub3[n][jsub] * fcvt_aer(iaer); - qaercw4[iaer][n] = qaercw3[iaer][n]; - } - } - } - - Real qgas_delaa[num_gas_ids][nqtendaa()] = {}; - Real qnum_delaa[num_modes][nqtendaa()] = {}; - Real qnumcw_delaa[num_modes][nqqcwtendaa()] = {}; - Real qaer_delaa[num_aerosol_ids][num_modes][nqtendaa()] = {}; - Real qaercw_delaa[num_aerosol_ids][num_modes][nqqcwtendaa()] = {}; - - if (iscldy_subarea[jsub]) { - mam_amicphys_1subarea_cloudy(sub_config, nstep, deltat, - jsub, nsubarea, iscldy_subarea[jsub], afracsub[jsub], temp, pmid, - pdel, zmid, pblh, relhumsub[jsub], dgn_a, dgn_awet, wetdens, qgas1, - qgas3, qgas4, qgas_delaa, qnum3, qnum4, qnum_delaa, qaer2, qaer3, - qaer4, qaer_delaa, qwtr3, qwtr4, qnumcw3, qnumcw4, qnumcw_delaa, - qaercw2, qaercw3, qaercw4, qaercw_delaa); - } else { - mam_amicphys_1subarea_clear(sub_config, nstep, deltat, - jsub, nsubarea, iscldy_subarea[jsub], afracsub[jsub], temp, pmid, - pdel, zmid, pblh, relhumsub[jsub], dgn_a, dgn_awet, wetdens, qgas1, - qgas3, qgas4, qgas_delaa, qnum3, qnum4, qnum_delaa, qaer3, qaer4, - qaer_delaa, qwtr3, qwtr4); - // map gas/aer/num arrays (mix-ratio and del=change) back to sub-area - // arrays - - if (do_map_gas_sub) { - for (int igas = 0; igas < 2; ++igas) { - const int l = lmap_gas(igas); - qsub4[l][jsub] = qgas4[igas] / fcvt_gas(igas); - for (int i = 0; i < nqtendaa(); ++i) - qsub_tendaa[l][i][jsub] = - qgas_delaa[igas][i] / (fcvt_gas(igas) * deltat); - } - } - for (int n = 0; n < num_modes; ++n) { - qsub4[n][jsub] = qnum4[n] / fcvt_num(); - for (int i = 0; i < nqtendaa(); ++i) - qsub_tendaa[n][i][jsub] = qnum_delaa[n][i] / (fcvt_num() * deltat); - for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { - qsub4[iaer][jsub] = qaer4[iaer][n] / fcvt_aer(iaer); - for (int i = 0; i < nqtendaa(); ++i) - qsub_tendaa[iaer][i][jsub] = - qaer_delaa[iaer][n][i] / (fcvt_aer(iaer) * deltat); - } - qaerwatsub4[n][jsub] = qwtr4[n] / fcvt_wtr(); - - if (iscldy_subarea[jsub]) { - qqcwsub4[n][jsub] = qnumcw4[n] / fcvt_num(); - for (int i = 0; i < nqqcwtendaa(); ++i) - qqcwsub_tendaa[n][i][jsub] = - qnumcw_delaa[n][i] / (fcvt_num() * deltat); - for (int iaer = 0; iaer < num_aerosol_ids; ++iaer) { - qqcwsub4[iaer][jsub] = qaercw4[iaer][n] / fcvt_aer(iaer); - for (int i = 0; i < nqqcwtendaa(); ++i) - qqcwsub_tendaa[iaer][i][jsub] = - qaercw_delaa[iaer][n][i] / (fcvt_aer(iaer) * deltat); - } - } - } - } - } -} - -} // anonymous namespace - -KOKKOS_INLINE_FUNCTION -void modal_aero_amicphys_intr( - const AmicPhysConfig& config, const int nstep, const Real deltat, const Real t, const Real pmid, const Real pdel, - const Real zm, const Real pblh, const Real qv, const Real cld, - Real q[gas_pcnst()], Real qqcw[gas_pcnst()], const Real q_pregaschem[gas_pcnst()], - const Real q_precldchem[gas_pcnst()], const Real qqcw_precldchem[gas_pcnst()], - Real q_tendbb[gas_pcnst()][nqtendbb()], Real qqcw_tendbb[gas_pcnst()][nqtendbb()], - Real dgncur_a[AeroConfig::num_modes()], - Real dgncur_awet[AeroConfig::num_modes()], - Real wetdens_host[AeroConfig::num_modes()], - Real qaerwat[AeroConfig::num_modes()]) { - - /* - nstep ! model time-step number - nqtendbb ! dimension for q_tendbb - nqqcwtendbb ! dimension f - deltat ! - q(ncol,pver,pcnstxx) ! current tracer mixing ratios (TMRs) - these values are updated (so out /= in) - *** MUST BE #/kmol-air for number - *** MUST BE mol/mol-air for mass - *** NOTE ncol dimension - qqcw(ncol,pver,pcnstxx) - like q but for cloud-borner tracers - these values are updated - q_pregaschem(ncol,pver,pcnstxx) ! q TMRs before gas-phase - chemistry q_precldchem(ncol,pver,pcnstxx) ! q TMRs before cloud - chemistry qqcw_precldchem(ncol,pver,pcnstxx) ! qqcw TMRs before cloud - chemistry q_tendbb(ncol,pver,pcnstxx,nqtendbb()) ! TMR tendencies for - box-model diagnostic output qqcw_tendbb(ncol,pver,pcnstx t(pcols,pver) ! - temperature at model levels (K) pmid(pcols,pver) ! pressure at model - level centers (Pa) pdel(pcols,pver) ! pressure thickness of levels - (Pa) zm(pcols,pver) ! altitude (above ground) at level centers (m) - pblh(pcols) ! planetary boundary layer depth (m) - qv(pcols,pver) ! specific humidity (kg/kg) - cld(ncol,pver) ! cloud fraction (-) *** NOTE ncol dimension - dgncur_a(pcols,pver,ntot_amode) - dgncur_awet(pcols,pver,ntot_amode) - ! dry & wet geo. mean dia. (m) of - number distrib. wetdens_host(pcols,pver,ntot_amode) ! interstitial - aerosol wet density (kg/m3) - - qaerwat(pcols,pver,ntot_amode aerosol water mixing ratio (kg/kg, - NOT mol/mol) - - */ - - // !DESCRIPTION: - // calculates changes to gas and aerosol TMRs (tracer mixing ratios) from - // gas-aerosol exchange (condensation/evaporation) - // growth from smaller to larger modes (renaming) due to both - // condensation and cloud chemistry - // new particle nucleation - // coagulation - // transfer of particles from hydrophobic modes to hydrophilic modes - // (aging) - // due to condensation and coagulation - // - // the incoming mixing ratios (q and qqcw) are updated before output - // - // !REVISION HISTORY: - // RCE 07.04.13: Adapted from earlier version of CAM5 modal aerosol - // routines - // for these processes - // - - static constexpr int num_modes = AeroConfig::num_modes(); - - // qgcmN and qqcwgcmN (N=1:4) are grid-cell mean tracer mixing ratios - // (TMRs, mol/mol or #/kmol) - // N=1 - before gas-phase chemistry - // N=2 - before cloud chemistry - // N=3 - incoming values (before gas-aerosol exchange, newnuc, coag) - // N=4 - outgoing values (after gas-aerosol exchange, newnuc, coag) - - // qsubN and qqcwsubN (N=1:4) are TMRs in sub-areas - // currently there are just clear and cloudy sub-areas - // the N=1:4 have same meanings as for qgcmN - - // q_coltendaa and qqcw_coltendaa are column-integrated tendencies - // for different processes, which are output to history - // the processes are condensation/evaporation (and associated aging), - // renaming, coagulation, and nucleation - - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqtendbb(); ++j) - q_tendbb[i][j] = 0.0, qqcw_tendbb[i][j] = 0.0; - - // get saturation mixing ratio - // call qsat( t(1:ncol,1:pver), pmid(1:ncol,1:pvnner), & - // ev_sat(1:ncol,1:pver), qv_sat(1:ncol,1:pver) ) - const Real epsqs = haero::Constants::weight_ratio_h2o_air; - // Saturation vapor pressure - const Real ev_sat = conversions::vapor_saturation_pressure_magnus(t, pmid); - // Saturation specific humidity - const Real qv_sat = epsqs * ev_sat / (pmid - (1 - epsqs) * ev_sat); - - const Real relhumgcm = haero::max(0.0, haero::min(1.0, qv / qv_sat)); - - // Set up cloudy/clear subareas inside a grid cell - int nsubarea, ncldy_subarea, jclea, jcldy; - bool iscldy_subarea[maxsubarea()]; - Real afracsub[maxsubarea()]; - Real relhumsub[maxsubarea()]; - Real qsub1[gas_pcnst()][maxsubarea()]; - Real qsub2[gas_pcnst()][maxsubarea()]; - Real qsub3[gas_pcnst()][maxsubarea()]; - Real qqcwsub1[gas_pcnst()][maxsubarea()]; - Real qqcwsub2[gas_pcnst()][maxsubarea()]; - Real qqcwsub3[gas_pcnst()][maxsubarea()]; - // aerosol water mixing ratios (mol/mol) - Real qaerwatsub3[AeroConfig::num_modes()][maxsubarea()]; - construct_subareas_1gridcell(cld, relhumgcm, // in - q_pregaschem, q_precldchem, // in - qqcw_precldchem, // in - q, qqcw, // in - nsubarea, ncldy_subarea, jclea, jcldy, // out - iscldy_subarea, afracsub, relhumsub, // out - qsub1, qsub2, qsub3, // out - qqcwsub1, qqcwsub2, qqcwsub3, qaerwatsub3, // out - qaerwat // in - ); - - // Initialize the "after-amicphys" values - Real qsub4[gas_pcnst()][maxsubarea()] = {}; - Real qqcwsub4[gas_pcnst()][maxsubarea()] = {}; - Real qaerwatsub4[AeroConfig::num_modes()][maxsubarea()] = {}; - - // - // start integration - // - Real dgn_a[num_modes], dgn_awet[num_modes], wetdens[num_modes]; - for (int n = 0; n < num_modes; ++n) { - dgn_a[n] = dgncur_a[n]; - dgn_awet[n] = dgncur_awet[n]; - wetdens[n] = haero::max(1000.0, wetdens_host[n]); - } - Real qsub_tendaa[gas_pcnst()][nqtendaa()][maxsubarea()] = {}; - Real qqcwsub_tendaa[gas_pcnst()][nqqcwtendaa()][maxsubarea()] = {}; - mam_amicphys_1gridcell(config, nstep, deltat, - nsubarea, ncldy_subarea, iscldy_subarea, afracsub, t, - pmid, pdel, zm, pblh, relhumsub, dgn_a, dgn_awet, - wetdens, qsub1, qsub2, qqcwsub2, qsub3, qqcwsub3, - qaerwatsub3, qsub4, qqcwsub4, qaerwatsub4, qsub_tendaa, - qqcwsub_tendaa); - - // - // form new grid-mean mix-ratios - Real qgcm4[gas_pcnst()]; - Real qgcm_tendaa[gas_pcnst()][nqtendaa()]; - Real qaerwatgcm4[num_modes]; - if (nsubarea == 1) { - for (int i = 0; i < gas_pcnst(); ++i) - qgcm4[i] = qsub4[i][0]; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqtendaa(); ++j) - qgcm_tendaa[i][j] = qsub_tendaa[i][j][0]; - for (int i = 0; i < num_modes; ++i) - qaerwatgcm4[i] = qaerwatsub4[i][0]; - } else { - for (int i = 0; i < gas_pcnst(); ++i) - qgcm4[i] = 0.0; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqtendaa(); ++j) - qgcm_tendaa[i][j] = 0.0; - for (int n = 0; n < nsubarea; ++n) { - for (int i = 0; i < gas_pcnst(); ++i) - qgcm4[i] += qsub4[i][n] * afracsub[n]; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqtendaa(); ++j) - qgcm_tendaa[i][j] = - qgcm_tendaa[i][j] + qsub_tendaa[i][j][n] * afracsub[n]; - } - for (int i = 0; i < num_modes; ++i) - // for aerosol water use the clear sub-area value - qaerwatgcm4[i] = qaerwatsub4[i][jclea - 1]; - } - Real qqcwgcm4[gas_pcnst()]; - Real qqcwgcm_tendaa[gas_pcnst()][nqqcwtendaa()]; - if (ncldy_subarea <= 0) { - for (int i = 0; i < gas_pcnst(); ++i) - qqcwgcm4[i] = haero::max(0.0, qqcw[i]); - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqqcwtendaa(); ++j) - qqcwgcm_tendaa[i][j] = 0.0; - } else if (nsubarea == 1) { - for (int i = 0; i < gas_pcnst(); ++i) - qqcwgcm4[i] = qqcwsub4[i][0]; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqqcwtendaa(); ++j) - qqcwgcm_tendaa[i][j] = qqcwsub_tendaa[i][j][0]; - } else { - for (int i = 0; i < gas_pcnst(); ++i) - qqcwgcm4[i] = 0.0; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqqcwtendaa(); ++j) - qqcwgcm_tendaa[i][j] = 0.0; - for (int n = 0; n < nsubarea; ++n) { - if (iscldy_subarea[n]) { - for (int i = 0; i < gas_pcnst(); ++i) - qqcwgcm4[i] += qqcwsub4[i][n] * afracsub[n]; - for (int i = 0; i < gas_pcnst(); ++i) - for (int j = 0; j < nqqcwtendaa(); ++j) - qqcwgcm_tendaa[i][j] += qqcwsub_tendaa[i][j][n] * afracsub[n]; - } - } - } - - for (int lmz = 0; lmz < gas_pcnst(); ++lmz) { - if (lmapcc_all(lmz) > 0) { - // HW, to ensure non-negative - q[lmz] = haero::max(qgcm4[lmz], 0.0); - if (lmapcc_all(lmz) >= lmapcc_val_aer()) { - // HW, to ensure non-negative - qqcw[lmz] = haero::max(qqcwgcm4[lmz], 0.0); - } - } - } - for (int i = 0; i < gas_pcnst(); ++i) { - if (iqtend_cond() < nqtendbb()) - q_tendbb[i][iqtend_cond()] = qgcm_tendaa[i][iqtend_cond()]; - if (iqtend_rnam() < nqtendbb()) - q_tendbb[i][iqtend_rnam()] = qgcm_tendaa[i][iqtend_rnam()]; - if (iqtend_nnuc() < nqtendbb()) - q_tendbb[i][iqtend_nnuc()] = qgcm_tendaa[i][iqtend_nnuc()]; - if (iqtend_coag() < nqtendbb()) - q_tendbb[i][iqtend_coag()] = qgcm_tendaa[i][iqtend_coag()]; - if (iqqcwtend_rnam() < nqqcwtendbb()) - qqcw_tendbb[i][iqqcwtend_rnam()] = qqcwgcm_tendaa[i][iqqcwtend_rnam()]; - } - for (int i = 0; i < num_modes; ++i) - qaerwat[i] = qaerwatgcm4[i]; -} - -} // namespace scream::impl From e715b8b471a5173c1efc3668f54b6056a2884e47 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Mon, 4 Nov 2024 17:33:17 -0700 Subject: [PATCH 30/41] update mam4xx submodule to fix gpu compilation error --- externals/mam4xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/mam4xx b/externals/mam4xx index 59990d402f4..d1c70ab972b 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit 59990d402f4115ce3a36a8c00eb4d059e703a583 +Subproject commit d1c70ab972bc00269939fd3296d93a719f273105 From cc63e5d67ed616b06a58514a44f3326322438bfe Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 20:24:40 -0800 Subject: [PATCH 31/41] EAMxx: Fixes a gpu bug requiring a particular format for dstflx --- components/eamxx/tests/single-process/mam/emissions/input.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/eamxx/tests/single-process/mam/emissions/input.yaml b/components/eamxx/tests/single-process/mam/emissions/input.yaml index a4564cb3949..0819562d959 100644 --- a/components/eamxx/tests/single-process/mam/emissions/input.yaml +++ b/components/eamxx/tests/single-process/mam/emissions/input.yaml @@ -43,7 +43,7 @@ initial_conditions: # we should get the following variables from other processes pbl_height : 1.0 - dstflx : [ 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000, 0.00000000000000000E+000] + dstflx : 0.0 ocnfrac: 0.10000000000000000E+001 sst: 0.30178553874977507E+003 cldfrac_tot: 0.138584624960092 From 40a6190f5557002f1429c36a4249663fc378a1a7 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 20:39:44 -0800 Subject: [PATCH 32/41] EAMxx: Clang format --- ...mxx_mam_microphysics_process_interface.cpp | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 57eaf927548..328afc89690 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -2,9 +2,13 @@ // impl namespace for some driver level functions for microphysics +<<<<<<< HEAD #include "readfiles/photo_table_utils.cpp" #include "readfiles/find_season_index_utils.hpp" +======= +>>>>>>> EAMxx: Clang format #include "physics/rrtmgp/shr_orb_mod_c2f.hpp" +#include "readfiles/photo_table_utils.cpp" namespace scream { @@ -36,7 +40,7 @@ MAMMicrophysics::MAMMicrophysics(const ekat::Comm &comm, config_.linoz.o3_lbl = m_params.get("mam4_o3_lbl"); config_.linoz.o3_tau = m_params.get("mam4_o3_tau"); config_.linoz.o3_sfc = m_params.get("mam4_o3_sfc"); - config_.linoz.psc_T = m_params.get("mam4_psc_T"); + config_.linoz.psc_T = m_params.get("mam4_psc_T"); } AtmosphereProcessType MAMMicrophysics::type() const { @@ -81,19 +85,20 @@ void MAMMicrophysics::set_grids( // ----------- Atmospheric quantities ------------- // Specific humidity [kg/kg](Require only for building DS) - add_tracer("qv", grid_, kg/kg); // specific humidity + add_tracer("qv", grid_, kg / kg); // specific humidity // Cloud liquid mass mixing ratio [kg/kg](Require only for building DS) - add_tracer("qc", grid_, kg/kg); // cloud liquid wet mixing ratio + add_tracer("qc", grid_, kg / kg); // cloud liquid wet mixing ratio // Cloud ice mass mixing ratio [kg/kg](Require only for building DS) - add_tracer("qi", grid_, kg/kg); // ice wet mixing ratio + add_tracer("qi", grid_, kg / kg); // ice wet mixing ratio // Cloud liquid number mixing ratio [1/kg](Require only for building DS) - add_tracer("nc", grid_, n_unit); // cloud liquid wet number mixing ratio + add_tracer("nc", grid_, + n_unit); // cloud liquid wet number mixing ratio // Cloud ice number mixing ratio [1/kg](Require only for building DS) - add_tracer("ni", grid_, n_unit); // ice number mixing ratio + add_tracer("ni", grid_, n_unit); // ice number mixing ratio // Temperature[K] at midpoints add_field("T_mid", scalar3d_mid, K, grid_name); @@ -161,7 +166,7 @@ void MAMMicrophysics::set_grids( mam_coupling::int_aero_mmr_field_name(m, a); if(strlen(int_mmr_field_name) > 0) { - add_tracer(int_mmr_field_name, grid_, kg/kg); + add_tracer(int_mmr_field_name, grid_, kg / kg); } } // for loop species } // for loop nmodes interstitial @@ -183,7 +188,7 @@ void MAMMicrophysics::set_grids( // aerosol-related gases: mass mixing ratios for(int g = 0; g < mam_coupling::num_aero_gases(); ++g) { const char *gas_mmr_field_name = mam_coupling::gas_mmr_field_name(g); - add_tracer(gas_mmr_field_name, grid_, kg/kg); + add_tracer(gas_mmr_field_name, grid_, kg / kg); } // Creating a Linoz reader and setting Linoz parameters involves reading data @@ -715,10 +720,17 @@ void MAMMicrophysics::run_impl(const double dt) { const auto zenith_angle = acos_cosine_zenith_; constexpr int gas_pcnst = mam_coupling::gas_pcnst(); +<<<<<<< HEAD const auto& elevated_emis_output = elevated_emis_output_; const auto& extfrc = extfrc_; const auto& forcings = forcings_; constexpr int extcnt = mam4::gas_chemistry::extcnt; +======= + const auto &vert_emis_output = vert_emis_output_; + const auto &extfrc = extfrc_; + const auto &forcings = forcings_; + constexpr int extcnt = mam4::gas_chemistry::extcnt; +>>>>>>> EAMxx: Clang format const int offset_aerosol = mam4::utils::gasses_start_ind(); Real adv_mass_kg_per_moles[gas_pcnst]; @@ -809,9 +821,9 @@ void MAMMicrophysics::run_impl(const double dt) { linoz_o3col_clim_icol, linoz_PmL_clim_icol, linoz_dPmL_dO3_icol, linoz_dPmL_dT_icol, linoz_dPmL_dO3col_icol, linoz_cariolle_pscs_icol, eccf, adv_mass_kg_per_moles, clsmap_4, - permute_4, offset_aerosol, - config.linoz.o3_sfc, config.linoz.o3_tau, config.linoz.o3_lbl, - dry_diameter_icol, wet_diameter_icol, wetdens_icol); + permute_4, offset_aerosol, config.linoz.o3_sfc, config.linoz.o3_tau, + config.linoz.o3_lbl, dry_diameter_icol, wet_diameter_icol, + wetdens_icol); }); // parallel_for for the column loop Kokkos::fence(); From f3a29b109af2891bb212d233cca88e3880d1bdf2 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 20:50:43 -0800 Subject: [PATCH 33/41] EAMxx: Fixed some comments paths and other minor cleanup --- .../eamxx/cime_config/namelist_defaults_scream.xml | 2 +- ...m_srf_and_online_emissions_process_interface.cpp | 10 +++++----- ...m_srf_and_online_emissions_process_interface.hpp | 1 + .../src/physics/mam/readfiles/marine_organics.hpp | 10 ---------- .../physics/mam/readfiles/marine_organics_impl.hpp | 8 -------- .../src/physics/mam/readfiles/soil_erodibility.hpp | 13 ------------- .../physics/mam/readfiles/soil_erodibility_impl.hpp | 6 ------ 7 files changed, 7 insertions(+), 43 deletions(-) diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index fd62cdc01e3..9c11c76aea1 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -376,7 +376,7 @@ be lost if SCREAM_HACK_XML is not enabled. ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/dst_ne30pg2_c20241028.nc - ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/dst_ne4pg2_c20241028.nc + ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne4pg2/dst_ne4pg2_c20241028.nc ${DIN_LOC_ROOT}/atm/scream/mam4xx/emissions/ne30pg2/monthly_macromolecules_0.1deg_bilinear_year01_merge_ne30pg2_c20241030.nc 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 190dc1da27d..a3efea7d32d 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 @@ -15,7 +15,7 @@ using soilErodibilityFunc = MAMSrfOnlineEmiss::MAMSrfOnlineEmiss(const ekat::Comm &comm, const ekat::ParameterList ¶ms) : AtmosphereProcess(comm, params) { - // FIXME: Do we want to read dust emiss factor hfrom namelist?? + // 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. */ @@ -238,7 +238,7 @@ void MAMSrfOnlineEmiss::set_grids( } // srf emissions file read init // ------------------------------------------------------------- - // setup to enable reading soil erodibility file + // Setup to enable reading soil erodibility file // ------------------------------------------------------------- const std::string soil_erodibility_data_file = @@ -257,12 +257,12 @@ void MAMSrfOnlineEmiss::set_grids( serod_dataReader_); // output // ------------------------------------------------------------- - // setup to enable reading marine organics file + // Setup to enable reading marine organics file // ------------------------------------------------------------- const std::string marine_organics_data_file = m_params.get("marine_organics_file"); - // Field to be read from file (order matters as they are read in the same + // 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"}; @@ -477,7 +477,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Output view_1d fluxes_col = ekat::subview(constituent_fluxes, icol); - // Comput online emissions + // 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( 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 b1cba682bb6..ae00098b079 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 @@ -130,6 +130,7 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { const int gas_start_ind = mam4::utils::gasses_start_ind(); // FIXME: Is there a better way to zero out a select indices? + // We should probably do it in run_impl directly for(int ispc = gas_start_ind; ispc < pcnst; ++ispc) { flux_col(ispc) = 0; } diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp index 92136d315f5..a04dff129f4 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics.hpp @@ -15,7 +15,6 @@ struct marineOrganicsFunctions { using MemberType = typename KT::MemberType; using view_2d = typename KT::template view_2d; - // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- struct marineOrganicsTimeState { marineOrganicsTimeState() = default; @@ -50,7 +49,6 @@ struct marineOrganicsFunctions { view_2d emiss_sectors; }; // marineOrganicsData - // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- struct marineOrganicsInput { marineOrganicsInput() = default; @@ -69,20 +67,17 @@ struct marineOrganicsFunctions { // 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, @@ -91,7 +86,6 @@ struct marineOrganicsFunctions { AbstractRemapper &horiz_interp, marineOrganicsInput &marineOrganics_input); - // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- static void update_marine_organics_timestate( std::shared_ptr &scorpio_reader, @@ -99,21 +93,18 @@ struct marineOrganicsFunctions { 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 @@ -121,7 +112,6 @@ struct marineOrganicsFunctions { const ScalarX &x1, const ScalarT &t); - // ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- static void init_marine_organics_file_read( const int &ncol, const std::vector &field_name, diff --git a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp index 6eda3a47e72..389445fa024 100644 --- a/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/marine_organics_impl.hpp @@ -77,8 +77,6 @@ marineOrganicsFunctions::create_horiz_remapper( } // create_horiz_remapper // ------------------------------------------------------------------------------------------- -// ------------------------------------------------------------------------------------------- - template std::shared_ptr marineOrganicsFunctions::create_data_reader( @@ -92,7 +90,6 @@ marineOrganicsFunctions::create_data_reader( return std::make_shared(data_file, io_grid, io_fields, true); } // create_data_reader -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template void marineOrganicsFunctions::update_marine_organics_data_from_file( @@ -144,7 +141,6 @@ void marineOrganicsFunctions::update_marine_organics_data_from_file( } // END update_marine_organics_data_from_file -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template void marineOrganicsFunctions::update_marine_organics_timestate( @@ -180,7 +176,6 @@ void marineOrganicsFunctions::update_marine_organics_timestate( } // END updata_marine_organics_timestate -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template template @@ -189,7 +184,6 @@ KOKKOS_INLINE_FUNCTION ScalarX marineOrganicsFunctions::linear_interp( return (1 - t) * x0 + t * x1; } // linear_interp -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template void marineOrganicsFunctions::perform_time_interpolation( @@ -239,7 +233,6 @@ void marineOrganicsFunctions::perform_time_interpolation( } // perform_time_interpolation -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template void marineOrganicsFunctions::marineOrganics_main( @@ -270,7 +263,6 @@ void marineOrganicsFunctions::marineOrganics_main( perform_time_interpolation(time_state, data_beg, data_end, data_out); } // marineOrganics_main -// ------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------- template void marineOrganicsFunctions::init_marine_organics_file_read( diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp index 223fb78cfc1..8b47c81d907 100644 --- a/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility.hpp @@ -14,32 +14,19 @@ struct soilErodibilityFunctions { using KT = KokkosTypes; using const_view_1d = typename KT::template view_1d; - // ------------------------------------------------------------------------------------------- - // ------------------------------------------------------------------------------------------- - - // Soil erodibility routines 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, diff --git a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp index a685134419f..af0c4d73c17 100644 --- a/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp +++ b/components/eamxx/src/physics/mam/readfiles/soil_erodibility_impl.hpp @@ -70,8 +70,6 @@ soilErodibilityFunctions::create_horiz_remapper( } // create_horiz_remapper // ------------------------------------------------------------------------------------------- -// ------------------------------------------------------------------------------------------- - template std::shared_ptr soilErodibilityFunctions::create_data_reader( @@ -86,8 +84,6 @@ soilErodibilityFunctions::create_data_reader( } // create_data_reader // ------------------------------------------------------------------------------------------- -// ------------------------------------------------------------------------------------------- - template void soilErodibilityFunctions::update_soil_erodibility_data_from_file( std::shared_ptr &scorpio_reader, @@ -129,8 +125,6 @@ void soilErodibilityFunctions::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, From 8d4ca7677e6cde97fccf948a1685e42744b68188 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Mon, 4 Nov 2024 22:33:36 -0800 Subject: [PATCH 34/41] EAMxx: Moves online emiss and constituent init to a new function hpp file --- ...mam_srf_and_online_emissions_functions.hpp | 76 +++++++++++++++++++ ...and_online_emissions_process_interface.cpp | 45 +++++------ ...and_online_emissions_process_interface.hpp | 25 ++---- 3 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp 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..c1414d2077f --- /dev/null +++ b/components/eamxx/src/physics/mam/eamxx_mam_srf_and_online_emissions_functions.hpp @@ -0,0 +1,76 @@ +#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; }); + }); + Kokkos::fence(); +} // 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); + }); + Kokkos::fence(); + +} // 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 a3efea7d32d..d410bb01d6e 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,5 +1,8 @@ #include +// For surface and online emission functions +#include + // For reading soil erodibility file #include @@ -397,7 +400,7 @@ void MAMSrfOnlineEmiss::initialize_impl(const RunType run_type) { //----------------------------------------------------------------- // Setup preprocessing and post processing //----------------------------------------------------------------- - preprocess_.initialize(ncol_, nlev_, wet_atm_, dry_atm_, constituent_fluxes_); + preprocess_.initialize(ncol_, nlev_, wet_atm_, dry_atm_); } // end initialize_impl() @@ -412,6 +415,13 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { 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 + // Gather time and state information for interpolation const auto ts = timestamp() + dt; @@ -419,6 +429,8 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Online emissions from dust and sea salt //-------------------------------------------------------------------- + // compute_online_dust_nacl_emiss(); + // --- Interpolate marine organics data -- // Update TimeState, note the addition of dt @@ -457,38 +469,17 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Dust fluxes [kg/m^2/s]: Four flux values for each column const const_view_2d dstflx = get_field_in("dstflx").get_view(); - // Constituent fluxes [kg/m^2/s] - auto constituent_fluxes = this->constituent_fluxes_; - // 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; - // TODO: potentially combine with below parfor(icol) loop? - 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); - }); - Kokkos::fence(); - + compute_online_dust_nacl_emiss(ncol_, nlev_, ocnfrac, sst, u_wind, v_wind, + dstflx, mpoly, mprot, mlip, soil_erodibility, + z_mid, + // output + constituent_fluxes); //-------------------------------------------------------------------- // Interpolate srf emiss data read in from emissions files //-------------------------------------------------------------------- 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 ae00098b079..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 @@ -103,13 +103,11 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // on host: initializes preprocess functor with necessary state data void initialize(const int &ncol, const int &nlev, const mam_coupling::WetAtmosphere &wet_atm, - const mam_coupling::DryAtmosphere &dry_atm, - const view_2d &constituent_fluxes) { - ncol_pre_ = ncol; - nlev_pre_ = nlev; - wet_atm_pre_ = wet_atm; - dry_atm_pre_ = dry_atm; - constituent_fluxes_pre_ = constituent_fluxes; + 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()( @@ -122,18 +120,6 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // for atmosphere compute_vertical_layer_heights(team, dry_atm_pre_, icol); compute_updraft_velocities(team, wet_atm_pre_, dry_atm_pre_, icol); - - view_1d flux_col = ekat::subview(constituent_fluxes_pre_, icol); - - // Zero out constituent fluxes only for gasses and aerosols - const int pcnst = mam4::aero_model::pcnst; - const int gas_start_ind = mam4::utils::gasses_start_ind(); - - // FIXME: Is there a better way to zero out a select indices? - // We should probably do it in run_impl directly - for(int ispc = gas_start_ind; ispc < pcnst; ++ispc) { - flux_col(ispc) = 0; - } } // Preprocess operator() // local variables for preprocess struct @@ -143,7 +129,6 @@ class MAMSrfOnlineEmiss final : public scream::AtmosphereProcess { // local atmospheric and aerosol state data mam_coupling::WetAtmosphere wet_atm_pre_; mam_coupling::DryAtmosphere dry_atm_pre_; - view_2d constituent_fluxes_pre_; }; // MAMSrfOnlineEmiss::Preprocess private: // preprocessing scratch pad From a20712c19505fbcf1025d5d6ce1d29952c25a772 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Tue, 5 Nov 2024 11:57:27 -0700 Subject: [PATCH 35/41] fix cuda compile warnings --- ...amxx_mam_srf_and_online_emissions_process_interface.cpp | 7 ++++--- components/eamxx/src/physics/mam/srf_emission_impl.hpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) 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 d410bb01d6e..58a1c341fa5 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 @@ -509,15 +509,16 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { auto fluxes_in_mks_units = this->fluxes_in_mks_units_; 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( "srf_emis_fluxes", ncol_, KOKKOS_LAMBDA(int icol) { - fluxes_in_mks_units(icol) = - ispec_srf.data_out_.emiss_sectors(0, icol) * mfactor; + 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_impl ends // ============================================================================= -} // namespace scream \ No newline at end of file +} // namespace scream diff --git a/components/eamxx/src/physics/mam/srf_emission_impl.hpp b/components/eamxx/src/physics/mam/srf_emission_impl.hpp index fa037dc281d..b8ebfdbe501 100644 --- a/components/eamxx/src/physics/mam/srf_emission_impl.hpp +++ b/components/eamxx/src/physics/mam/srf_emission_impl.hpp @@ -179,7 +179,7 @@ void srfEmissFunctions::update_srfEmiss_data_from_file( const int time_index, // zero-based AbstractRemapper &srfEmiss_horiz_interp, srfEmissInput &srfEmiss_input) { using namespace ShortFieldTagsNames; - + start_timer("EAMxx::srfEmiss::update_srfEmiss_data_from_file"); // 1. Read from file From a40341e657253fd3b810a3c2eb3b6f3682e2f249 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Wed, 13 Nov 2024 12:54:49 -0800 Subject: [PATCH 36/41] EAMxx:Inits constituent fluxes to zero --- components/eamxx/cime_config/namelist_defaults_scream.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index 9c11c76aea1..d23169173ce 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -638,6 +638,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 From df4f13c1942429b87f049e5326ac5b01d24ec58f Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Fri, 15 Nov 2024 13:01:30 -0700 Subject: [PATCH 37/41] Revert "EAMxx: Clang format" This reverts commit 22086f144e9a8ef9264c383f5487b39d8de37319. --- ...mxx_mam_microphysics_process_interface.cpp | 109 +++++++----------- 1 file changed, 44 insertions(+), 65 deletions(-) diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 328afc89690..35f27e1db00 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -2,11 +2,7 @@ // impl namespace for some driver level functions for microphysics -<<<<<<< HEAD #include "readfiles/photo_table_utils.cpp" -#include "readfiles/find_season_index_utils.hpp" -======= ->>>>>>> EAMxx: Clang format #include "physics/rrtmgp/shr_orb_mod_c2f.hpp" #include "readfiles/photo_table_utils.cpp" @@ -211,7 +207,7 @@ void MAMMicrophysics::set_grids( LinozHorizInterp_, linoz_file_name_); // linoz reader - const auto io_grid_linoz = LinozHorizInterp_->get_tgt_grid(); + const auto io_grid_linoz = LinozHorizInterp_->get_src_grid(); const int num_cols_io_linoz = io_grid_linoz->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io_linoz = @@ -239,7 +235,7 @@ void MAMMicrophysics::set_grids( TracerHorizInterp_, oxid_file_name_); const int nvars = int(var_names.size()); - const auto io_grid = TracerHorizInterp_->get_tgt_grid(); + const auto io_grid = TracerHorizInterp_->get_src_grid(); const int num_cols_io = io_grid->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io = @@ -264,88 +260,78 @@ void MAMMicrophysics::set_grids( "num_a1", "num_a2", "num_a4", "soag"}; for(const auto &var_name : extfrc_lst_) { - std::string item_name = "mam4_" + var_name + "_elevated_emiss_file_name"; + std::string item_name = "mam4_" + var_name + "_verti_emiss_file_name"; const auto file_name = m_params.get(item_name); - elevated_emis_file_name_[var_name] = file_name; + vert_emis_file_name_[var_name] = file_name; } - elevated_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; - elevated_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; - elevated_emis_var_names_["so4_a2"] = {"contvolc"}; - elevated_emis_var_names_["pom_a4"] = {"BB"}; - elevated_emis_var_names_["bc_a4"] = {"BB"}; - elevated_emis_var_names_["num_a1"] = { + vert_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; + vert_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; + vert_emis_var_names_["so4_a2"] = {"contvolc"}; + vert_emis_var_names_["pom_a4"] = {"BB"}; + vert_emis_var_names_["bc_a4"] = {"BB"}; + vert_emis_var_names_["num_a1"] = { "num_a1_SO4_ELEV_BB", "num_a1_SO4_ELEV_ENE", "num_a1_SO4_ELEV_IND", "num_a1_SO4_ELEV_contvolc"}; - elevated_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"}; + vert_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"}; // num_a4 // FIXME: why the sectors in this files are num_a1; // I guess this should be num_a4? Is this a bug in the orginal nc files? - elevated_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB", + vert_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB", "num_a1_POM_ELEV_BB"}; - elevated_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"}; + vert_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"}; - int elevated_emiss_cyclical_ymd = m_params.get("elevated_emiss_ymd"); + int verti_emiss_cyclical_ymd = m_params.get("verti_emiss_ymd"); for(const auto &var_name : extfrc_lst_) { - const auto file_name = elevated_emis_file_name_[var_name]; - const auto var_names = elevated_emis_var_names_[var_name]; + const auto file_name = vert_emis_file_name_[var_name]; + const auto var_names = vert_emis_var_names_[var_name]; scream::mam_coupling::TracerData data_tracer; scream::mam_coupling::setup_tracer_data(data_tracer, file_name, - elevated_emiss_cyclical_ymd); + verti_emiss_cyclical_ymd); auto hor_rem = scream::mam_coupling::create_horiz_remapper( grid_, file_name, extfrc_map_file, var_names, data_tracer); - auto file_reader = - scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name, - data_tracer.file_type); - ElevatedEmissionsHorizInterp_.push_back(hor_rem); - ElevatedEmissionsDataReader_.push_back(file_reader); - elevated_emis_data_.push_back(data_tracer); - } // var_name elevated emissions + scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name); + VertEmissionsHorizInterp_.push_back(hor_rem); + VertEmissionsDataReader_.push_back(file_reader); + vert_emis_data_.push_back(data_tracer); + } // var_name vert emissions int i = 0; int offset_emis_ver = 0; for(const auto &var_name : extfrc_lst_) { - const auto file_name = elevated_emis_file_name_[var_name]; - const auto var_names = elevated_emis_var_names_[var_name]; + const auto file_name = vert_emis_file_name_[var_name]; + const auto var_names = vert_emis_var_names_[var_name]; const int nvars = static_cast(var_names.size()); forcings_[i].nsectors = nvars; // I am assuming the order of species in extfrc_lst_. // Indexing in mam4xx is fortran. forcings_[i].frc_ndx = i + 1; - const auto io_grid_emis = ElevatedEmissionsHorizInterp_[i]->get_tgt_grid(); + const auto io_grid_emis = VertEmissionsHorizInterp_[i]->get_src_grid(); const int num_cols_io_emis = io_grid_emis->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io_emis = io_grid_emis ->get_num_vertical_levels(); // Number of levels per column - elevated_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars); - elevated_emis_data_[i].allocate_temporal_views(); - forcings_[i].file_alt_data = elevated_emis_data_[i].has_altitude_; + vert_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars); + vert_emis_data_[i].allocate_temporal_views(); + forcings_[i].file_alt_data = vert_emis_data_[i].has_altitude_; for(int isp = 0; isp < nvars; ++isp) { forcings_[i].offset = offset_emis_ver; - elevated_emis_output_[isp + offset_emis_ver] = - view_2d("elevated_emis_output_", ncol_, nlev_); + vert_emis_output_[isp + offset_emis_ver] = + view_2d("vert_emis_output_", ncol_, nlev_); } offset_emis_ver += nvars; ++i; } // end i EKAT_REQUIRE_MSG( - offset_emis_ver <= int(mam_coupling::MAX_NUM_ELEVATED_EMISSIONS_FIELDS), + offset_emis_ver <= int(mam_coupling::MAX_NUM_VERT_EMISSION_FIELDS), "Error! Number of fields is bigger than " - "MAX_NUM_ELEVATED_EMISSIONS_FIELDS. Increase the " - "MAX_NUM_ELEVATED_EMISSIONS_FIELDS in tracer_reader_utils.hpp \n"); + "MAX_NUM_VERT_EMISSION_FIELDS. Increase the " + "MAX_NUM_VERT_EMISSION_FIELDS in tracer_reader_utils.hpp \n"); } // Tracer external forcing data - - { - const std::string season_wes_file = m_params.get("mam4_season_wes_file"); - const auto& clat = col_latitudes_; - mam_coupling::find_season_index_reader(season_wes_file, - clat, - index_season_lai_); - } } // set_grids // ================================================================ @@ -534,8 +520,8 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { for(int i = 0; i < static_cast(extfrc_lst_.size()); ++i) { scream::mam_coupling::update_tracer_data_from_file( - ElevatedEmissionsDataReader_[i], curr_month, *ElevatedEmissionsHorizInterp_[i], - elevated_emis_data_[i]); + VertEmissionsDataReader_[i], curr_month, *VertEmissionsHorizInterp_[i], + vert_emis_data_[i]); } invariants_ = view_3d("invarians", ncol_, nlev_, mam4::gas_chemistry::nfs); @@ -632,20 +618,20 @@ void MAMMicrophysics::run_impl(const double dt) { linoz_output); // out Kokkos::fence(); - elevated_emiss_time_state_.t_now = ts.frac_of_year_in_days(); + vert_emiss_time_state_.t_now = ts.frac_of_year_in_days(); int i = 0; for(const auto &var_name : extfrc_lst_) { - const auto file_name = elevated_emis_file_name_[var_name]; - const auto var_names = elevated_emis_var_names_[var_name]; + const auto file_name = vert_emis_file_name_[var_name]; + const auto var_names = vert_emis_var_names_[var_name]; const int nsectors = int(var_names.size()); - view_2d elevated_emis_output[nsectors]; + view_2d vert_emis_output[nsectors]; for(int isp = 0; isp < nsectors; ++isp) { - elevated_emis_output[isp] = elevated_emis_output_[isp + forcings_[i].offset]; + vert_emis_output[isp] = vert_emis_output_[isp + forcings_[i].offset]; } scream::mam_coupling::advance_tracer_data( - ElevatedEmissionsDataReader_[i], *ElevatedEmissionsHorizInterp_[i], ts, - elevated_emiss_time_state_, elevated_emis_data_[i], dry_atm_.p_mid, - dry_atm_.z_iface, elevated_emis_output); + VertEmissionsDataReader_[i], *VertEmissionsHorizInterp_[i], ts, + vert_emiss_time_state_, vert_emis_data_[i], dry_atm_.p_mid, + dry_atm_.z_iface, vert_emis_output); i++; Kokkos::fence(); } @@ -720,17 +706,10 @@ void MAMMicrophysics::run_impl(const double dt) { const auto zenith_angle = acos_cosine_zenith_; constexpr int gas_pcnst = mam_coupling::gas_pcnst(); -<<<<<<< HEAD - const auto& elevated_emis_output = elevated_emis_output_; - const auto& extfrc = extfrc_; - const auto& forcings = forcings_; - constexpr int extcnt = mam4::gas_chemistry::extcnt; -======= const auto &vert_emis_output = vert_emis_output_; const auto &extfrc = extfrc_; const auto &forcings = forcings_; constexpr int extcnt = mam4::gas_chemistry::extcnt; ->>>>>>> EAMxx: Clang format const int offset_aerosol = mam4::utils::gasses_start_ind(); Real adv_mass_kg_per_moles[gas_pcnst]; @@ -779,7 +758,7 @@ void MAMMicrophysics::run_impl(const double dt) { // We may need to move this line where we read files. forcings_in[i].file_alt_data = file_alt_data; for(int isec = 0; isec < forcings[i].nsectors; ++isec) { - const auto field = elevated_emis_output[isec + forcings[i].offset]; + const auto field = vert_emis_output[isec + forcings[i].offset]; forcings_in[i].fields_data[isec] = ekat::subview(field, icol); } } // extcnt for loop From 60a90e92b05069ab136fccbf464486eb7976b3e5 Mon Sep 17 00:00:00 2001 From: Michael J Schmidt Date: Fri, 15 Nov 2024 15:44:22 -0700 Subject: [PATCH 38/41] ff mam4xx to current main --- externals/mam4xx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externals/mam4xx b/externals/mam4xx index d1c70ab972b..e26b5b6faa5 160000 --- a/externals/mam4xx +++ b/externals/mam4xx @@ -1 +1 @@ -Subproject commit d1c70ab972bc00269939fd3296d93a719f273105 +Subproject commit e26b5b6faa5cdf39a5421328165c49bdb728b038 From c8c4d6592bd32f88fd3c6bb23a41ebea566b591b Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Fri, 15 Nov 2024 15:54:39 -0800 Subject: [PATCH 39/41] EAMxx: Remove all diffs from microphysics cpp file --- ...mxx_mam_microphysics_process_interface.cpp | 131 ++++++++++-------- 1 file changed, 70 insertions(+), 61 deletions(-) diff --git a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp index 35f27e1db00..57eaf927548 100644 --- a/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp +++ b/components/eamxx/src/physics/mam/eamxx_mam_microphysics_process_interface.cpp @@ -3,8 +3,8 @@ // impl namespace for some driver level functions for microphysics #include "readfiles/photo_table_utils.cpp" +#include "readfiles/find_season_index_utils.hpp" #include "physics/rrtmgp/shr_orb_mod_c2f.hpp" -#include "readfiles/photo_table_utils.cpp" namespace scream { @@ -36,7 +36,7 @@ MAMMicrophysics::MAMMicrophysics(const ekat::Comm &comm, config_.linoz.o3_lbl = m_params.get("mam4_o3_lbl"); config_.linoz.o3_tau = m_params.get("mam4_o3_tau"); config_.linoz.o3_sfc = m_params.get("mam4_o3_sfc"); - config_.linoz.psc_T = m_params.get("mam4_psc_T"); + config_.linoz.psc_T = m_params.get("mam4_psc_T"); } AtmosphereProcessType MAMMicrophysics::type() const { @@ -81,20 +81,19 @@ void MAMMicrophysics::set_grids( // ----------- Atmospheric quantities ------------- // Specific humidity [kg/kg](Require only for building DS) - add_tracer("qv", grid_, kg / kg); // specific humidity + add_tracer("qv", grid_, kg/kg); // specific humidity // Cloud liquid mass mixing ratio [kg/kg](Require only for building DS) - add_tracer("qc", grid_, kg / kg); // cloud liquid wet mixing ratio + add_tracer("qc", grid_, kg/kg); // cloud liquid wet mixing ratio // Cloud ice mass mixing ratio [kg/kg](Require only for building DS) - add_tracer("qi", grid_, kg / kg); // ice wet mixing ratio + add_tracer("qi", grid_, kg/kg); // ice wet mixing ratio // Cloud liquid number mixing ratio [1/kg](Require only for building DS) - add_tracer("nc", grid_, - n_unit); // cloud liquid wet number mixing ratio + add_tracer("nc", grid_, n_unit); // cloud liquid wet number mixing ratio // Cloud ice number mixing ratio [1/kg](Require only for building DS) - add_tracer("ni", grid_, n_unit); // ice number mixing ratio + add_tracer("ni", grid_, n_unit); // ice number mixing ratio // Temperature[K] at midpoints add_field("T_mid", scalar3d_mid, K, grid_name); @@ -162,7 +161,7 @@ void MAMMicrophysics::set_grids( mam_coupling::int_aero_mmr_field_name(m, a); if(strlen(int_mmr_field_name) > 0) { - add_tracer(int_mmr_field_name, grid_, kg / kg); + add_tracer(int_mmr_field_name, grid_, kg/kg); } } // for loop species } // for loop nmodes interstitial @@ -184,7 +183,7 @@ void MAMMicrophysics::set_grids( // aerosol-related gases: mass mixing ratios for(int g = 0; g < mam_coupling::num_aero_gases(); ++g) { const char *gas_mmr_field_name = mam_coupling::gas_mmr_field_name(g); - add_tracer(gas_mmr_field_name, grid_, kg / kg); + add_tracer(gas_mmr_field_name, grid_, kg/kg); } // Creating a Linoz reader and setting Linoz parameters involves reading data @@ -207,7 +206,7 @@ void MAMMicrophysics::set_grids( LinozHorizInterp_, linoz_file_name_); // linoz reader - const auto io_grid_linoz = LinozHorizInterp_->get_src_grid(); + const auto io_grid_linoz = LinozHorizInterp_->get_tgt_grid(); const int num_cols_io_linoz = io_grid_linoz->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io_linoz = @@ -235,7 +234,7 @@ void MAMMicrophysics::set_grids( TracerHorizInterp_, oxid_file_name_); const int nvars = int(var_names.size()); - const auto io_grid = TracerHorizInterp_->get_src_grid(); + const auto io_grid = TracerHorizInterp_->get_tgt_grid(); const int num_cols_io = io_grid->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io = @@ -260,78 +259,88 @@ void MAMMicrophysics::set_grids( "num_a1", "num_a2", "num_a4", "soag"}; for(const auto &var_name : extfrc_lst_) { - std::string item_name = "mam4_" + var_name + "_verti_emiss_file_name"; + std::string item_name = "mam4_" + var_name + "_elevated_emiss_file_name"; const auto file_name = m_params.get(item_name); - vert_emis_file_name_[var_name] = file_name; + elevated_emis_file_name_[var_name] = file_name; } - vert_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; - vert_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; - vert_emis_var_names_["so4_a2"] = {"contvolc"}; - vert_emis_var_names_["pom_a4"] = {"BB"}; - vert_emis_var_names_["bc_a4"] = {"BB"}; - vert_emis_var_names_["num_a1"] = { + elevated_emis_var_names_["so2"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; + elevated_emis_var_names_["so4_a1"] = {"BB", "ENE_ELEV", "IND_ELEV", "contvolc"}; + elevated_emis_var_names_["so4_a2"] = {"contvolc"}; + elevated_emis_var_names_["pom_a4"] = {"BB"}; + elevated_emis_var_names_["bc_a4"] = {"BB"}; + elevated_emis_var_names_["num_a1"] = { "num_a1_SO4_ELEV_BB", "num_a1_SO4_ELEV_ENE", "num_a1_SO4_ELEV_IND", "num_a1_SO4_ELEV_contvolc"}; - vert_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"}; + elevated_emis_var_names_["num_a2"] = {"num_a2_SO4_ELEV_contvolc"}; // num_a4 // FIXME: why the sectors in this files are num_a1; // I guess this should be num_a4? Is this a bug in the orginal nc files? - vert_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB", + elevated_emis_var_names_["num_a4"] = {"num_a1_BC_ELEV_BB", "num_a1_POM_ELEV_BB"}; - vert_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"}; + elevated_emis_var_names_["soag"] = {"SOAbb_src", "SOAbg_src", "SOAff_src"}; - int verti_emiss_cyclical_ymd = m_params.get("verti_emiss_ymd"); + int elevated_emiss_cyclical_ymd = m_params.get("elevated_emiss_ymd"); for(const auto &var_name : extfrc_lst_) { - const auto file_name = vert_emis_file_name_[var_name]; - const auto var_names = vert_emis_var_names_[var_name]; + const auto file_name = elevated_emis_file_name_[var_name]; + const auto var_names = elevated_emis_var_names_[var_name]; scream::mam_coupling::TracerData data_tracer; scream::mam_coupling::setup_tracer_data(data_tracer, file_name, - verti_emiss_cyclical_ymd); + elevated_emiss_cyclical_ymd); auto hor_rem = scream::mam_coupling::create_horiz_remapper( grid_, file_name, extfrc_map_file, var_names, data_tracer); + auto file_reader = - scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name); - VertEmissionsHorizInterp_.push_back(hor_rem); - VertEmissionsDataReader_.push_back(file_reader); - vert_emis_data_.push_back(data_tracer); - } // var_name vert emissions + scream::mam_coupling::create_tracer_data_reader(hor_rem, file_name, + data_tracer.file_type); + ElevatedEmissionsHorizInterp_.push_back(hor_rem); + ElevatedEmissionsDataReader_.push_back(file_reader); + elevated_emis_data_.push_back(data_tracer); + } // var_name elevated emissions int i = 0; int offset_emis_ver = 0; for(const auto &var_name : extfrc_lst_) { - const auto file_name = vert_emis_file_name_[var_name]; - const auto var_names = vert_emis_var_names_[var_name]; + const auto file_name = elevated_emis_file_name_[var_name]; + const auto var_names = elevated_emis_var_names_[var_name]; const int nvars = static_cast(var_names.size()); forcings_[i].nsectors = nvars; // I am assuming the order of species in extfrc_lst_. // Indexing in mam4xx is fortran. forcings_[i].frc_ndx = i + 1; - const auto io_grid_emis = VertEmissionsHorizInterp_[i]->get_src_grid(); + const auto io_grid_emis = ElevatedEmissionsHorizInterp_[i]->get_tgt_grid(); const int num_cols_io_emis = io_grid_emis->get_num_local_dofs(); // Number of columns on this rank const int num_levs_io_emis = io_grid_emis ->get_num_vertical_levels(); // Number of levels per column - vert_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars); - vert_emis_data_[i].allocate_temporal_views(); - forcings_[i].file_alt_data = vert_emis_data_[i].has_altitude_; + elevated_emis_data_[i].init(num_cols_io_emis, num_levs_io_emis, nvars); + elevated_emis_data_[i].allocate_temporal_views(); + forcings_[i].file_alt_data = elevated_emis_data_[i].has_altitude_; for(int isp = 0; isp < nvars; ++isp) { forcings_[i].offset = offset_emis_ver; - vert_emis_output_[isp + offset_emis_ver] = - view_2d("vert_emis_output_", ncol_, nlev_); + elevated_emis_output_[isp + offset_emis_ver] = + view_2d("elevated_emis_output_", ncol_, nlev_); } offset_emis_ver += nvars; ++i; } // end i EKAT_REQUIRE_MSG( - offset_emis_ver <= int(mam_coupling::MAX_NUM_VERT_EMISSION_FIELDS), + offset_emis_ver <= int(mam_coupling::MAX_NUM_ELEVATED_EMISSIONS_FIELDS), "Error! Number of fields is bigger than " - "MAX_NUM_VERT_EMISSION_FIELDS. Increase the " - "MAX_NUM_VERT_EMISSION_FIELDS in tracer_reader_utils.hpp \n"); + "MAX_NUM_ELEVATED_EMISSIONS_FIELDS. Increase the " + "MAX_NUM_ELEVATED_EMISSIONS_FIELDS in tracer_reader_utils.hpp \n"); } // Tracer external forcing data + + { + const std::string season_wes_file = m_params.get("mam4_season_wes_file"); + const auto& clat = col_latitudes_; + mam_coupling::find_season_index_reader(season_wes_file, + clat, + index_season_lai_); + } } // set_grids // ================================================================ @@ -520,8 +529,8 @@ void MAMMicrophysics::initialize_impl(const RunType run_type) { for(int i = 0; i < static_cast(extfrc_lst_.size()); ++i) { scream::mam_coupling::update_tracer_data_from_file( - VertEmissionsDataReader_[i], curr_month, *VertEmissionsHorizInterp_[i], - vert_emis_data_[i]); + ElevatedEmissionsDataReader_[i], curr_month, *ElevatedEmissionsHorizInterp_[i], + elevated_emis_data_[i]); } invariants_ = view_3d("invarians", ncol_, nlev_, mam4::gas_chemistry::nfs); @@ -618,20 +627,20 @@ void MAMMicrophysics::run_impl(const double dt) { linoz_output); // out Kokkos::fence(); - vert_emiss_time_state_.t_now = ts.frac_of_year_in_days(); + elevated_emiss_time_state_.t_now = ts.frac_of_year_in_days(); int i = 0; for(const auto &var_name : extfrc_lst_) { - const auto file_name = vert_emis_file_name_[var_name]; - const auto var_names = vert_emis_var_names_[var_name]; + const auto file_name = elevated_emis_file_name_[var_name]; + const auto var_names = elevated_emis_var_names_[var_name]; const int nsectors = int(var_names.size()); - view_2d vert_emis_output[nsectors]; + view_2d elevated_emis_output[nsectors]; for(int isp = 0; isp < nsectors; ++isp) { - vert_emis_output[isp] = vert_emis_output_[isp + forcings_[i].offset]; + elevated_emis_output[isp] = elevated_emis_output_[isp + forcings_[i].offset]; } scream::mam_coupling::advance_tracer_data( - VertEmissionsDataReader_[i], *VertEmissionsHorizInterp_[i], ts, - vert_emiss_time_state_, vert_emis_data_[i], dry_atm_.p_mid, - dry_atm_.z_iface, vert_emis_output); + ElevatedEmissionsDataReader_[i], *ElevatedEmissionsHorizInterp_[i], ts, + elevated_emiss_time_state_, elevated_emis_data_[i], dry_atm_.p_mid, + dry_atm_.z_iface, elevated_emis_output); i++; Kokkos::fence(); } @@ -706,10 +715,10 @@ void MAMMicrophysics::run_impl(const double dt) { const auto zenith_angle = acos_cosine_zenith_; constexpr int gas_pcnst = mam_coupling::gas_pcnst(); - const auto &vert_emis_output = vert_emis_output_; - const auto &extfrc = extfrc_; - const auto &forcings = forcings_; - constexpr int extcnt = mam4::gas_chemistry::extcnt; + const auto& elevated_emis_output = elevated_emis_output_; + const auto& extfrc = extfrc_; + const auto& forcings = forcings_; + constexpr int extcnt = mam4::gas_chemistry::extcnt; const int offset_aerosol = mam4::utils::gasses_start_ind(); Real adv_mass_kg_per_moles[gas_pcnst]; @@ -758,7 +767,7 @@ void MAMMicrophysics::run_impl(const double dt) { // We may need to move this line where we read files. forcings_in[i].file_alt_data = file_alt_data; for(int isec = 0; isec < forcings[i].nsectors; ++isec) { - const auto field = vert_emis_output[isec + forcings[i].offset]; + const auto field = elevated_emis_output[isec + forcings[i].offset]; forcings_in[i].fields_data[isec] = ekat::subview(field, icol); } } // extcnt for loop @@ -800,9 +809,9 @@ void MAMMicrophysics::run_impl(const double dt) { linoz_o3col_clim_icol, linoz_PmL_clim_icol, linoz_dPmL_dO3_icol, linoz_dPmL_dT_icol, linoz_dPmL_dO3col_icol, linoz_cariolle_pscs_icol, eccf, adv_mass_kg_per_moles, clsmap_4, - permute_4, offset_aerosol, config.linoz.o3_sfc, config.linoz.o3_tau, - config.linoz.o3_lbl, dry_diameter_icol, wet_diameter_icol, - wetdens_icol); + permute_4, offset_aerosol, + config.linoz.o3_sfc, config.linoz.o3_tau, config.linoz.o3_lbl, + dry_diameter_icol, wet_diameter_icol, wetdens_icol); }); // parallel_for for the column loop Kokkos::fence(); From e02b76cdf93a8c1ce09e06b9fbfa26de3856ae73 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Fri, 15 Nov 2024 20:18:34 -0800 Subject: [PATCH 40/41] EAMxx: Adds missing namelist entries for multi process test --- .../input.yaml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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: From 71e4bc5059aebad00c8c7ae82309422b4fc1cb06 Mon Sep 17 00:00:00 2001 From: Balwinder Singh Date: Sat, 16 Nov 2024 10:59:15 -0800 Subject: [PATCH 41/41] EAMxx: Moves fences into the interface and remove extra comments --- components/eamxx/cime_config/namelist_defaults_scream.xml | 5 ----- .../mam/eamxx_mam_srf_and_online_emissions_functions.hpp | 3 --- .../eamxx_mam_srf_and_online_emissions_process_interface.cpp | 5 ++--- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/components/eamxx/cime_config/namelist_defaults_scream.xml b/components/eamxx/cime_config/namelist_defaults_scream.xml index d23169173ce..771928a8122 100644 --- a/components/eamxx/cime_config/namelist_defaults_scream.xml +++ b/components/eamxx/cime_config/namelist_defaults_scream.xml @@ -372,15 +372,10 @@ 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/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 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 index c1414d2077f..9c01daf8223 100644 --- 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 @@ -33,7 +33,6 @@ void init_fluxes(const int &ncol, Kokkos::TeamVectorRange(team, gas_start_ind, pcnst), [&](int icnst) { flux_col(icnst) = 0; }); }); - Kokkos::fence(); } // init_fluxes ends //-------- compute online emissions for dust, sea salt and marine organics ----- @@ -66,8 +65,6 @@ void compute_online_dust_nacl_emiss( // out fluxes_col); }); - Kokkos::fence(); - } // compute_online_dust_nacl_emiss ends } // namespace 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 58a1c341fa5..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 @@ -421,7 +421,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // 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 const auto ts = timestamp() + dt; @@ -429,8 +429,6 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { // Online emissions from dust and sea salt //-------------------------------------------------------------------- - // compute_online_dust_nacl_emiss(); - // --- Interpolate marine organics data -- // Update TimeState, note the addition of dt @@ -480,6 +478,7 @@ void MAMSrfOnlineEmiss::run_impl(const double dt) { z_mid, // output constituent_fluxes); + Kokkos::fence(); //-------------------------------------------------------------------- // Interpolate srf emiss data read in from emissions files //--------------------------------------------------------------------