@@ -188,15 +188,53 @@ void MAMMicrophysics::set_grids(
188
188
add_field<Updated>(" constituent_fluxes" , scalar2d_pcnst, kg / m2 / s,
189
189
grid_name);
190
190
191
+ // FIXME: moved this to class
191
192
// Number of externally forced chemical species
192
- constexpr int extcnt = mam4::gas_chemistry::extcnt;
193
+ // constexpr int extcnt = mam4::gas_chemistry::extcnt;
193
194
194
- FieldLayout scalar3d_extcnt = grid_->get_3d_vector_layout (true , extcnt , " ext_cnt" );
195
+ FieldLayout scalar3d_extcnt = grid_->get_3d_vector_layout (true , extcnt_ , " ext_cnt" );
195
196
196
197
// Register computed fields for external forcing
197
198
// - extfrc: 3D instantaneous forcing rate [kg/m³/s]
198
199
add_field<Computed>(" mam4_external_forcing" , scalar3d_extcnt, kg / m3 / s, grid_name);
199
200
201
+ // extents are [ncol, nlev, gas_pcnst, nq<...>]
202
+ FieldLayout tensor3d_gas_tend =
203
+ grid_->get_3d_tensor_layout (true , {num_gas_spec_, mam4::microphysics::nqtendaa ()},
204
+ {" num_gas_spec" , " nqtendaa" });
205
+ FieldLayout tensor3d_gas_tend_cw =
206
+ grid_->get_3d_tensor_layout (true , {num_gas_spec_, mam4::microphysics::nqqcwtendaa ()},
207
+ {" num_gas_spec" , " nqqcwtendaa" });
208
+ FieldLayout tensor2d_gas_tend =
209
+ grid_->get_3d_tensor_layout (true , {num_gas_spec_, mam4::microphysics::nqtendaa ()},
210
+ {" num_gas_spec" , " nqtendaa" });
211
+ FieldLayout tensor2d_gas_tend_cw =
212
+ grid_->get_3d_tensor_layout (true , {num_gas_spec_, mam4::microphysics::nqqcwtendaa ()},
213
+ {" num_gas_spec" , " nqqcwtendaa" });
214
+ FieldLayout vector3d_gas_tend = grid_->get_3d_vector_layout (true , num_gas_spec_, " num_gas_spec" );
215
+
216
+ // fields for holding diagnostic quantities relating to gas-species tendencies
217
+ // NOTE: denoting this as non-dimensional because the original fortran MAM used these to store
218
+ // different "tracer mixing ratios" that could be [mol/mol], [#/kmol], etc.
219
+ // consider breaking this into separate 3D-vector fields?
220
+ add_field<Computed>(" gas_spec_tendencies" , tensor3d_gas_tend, nondim, grid_name);
221
+ add_field<Computed>(" gas_spec_tendencies_cw" , tensor3d_gas_tend_cw, nondim, grid_name);
222
+ // these are the column-integrated fields returned by vert_contraction()
223
+ add_field<Computed>(" col_int_gas_spec_tend" , tensor2d_gas_tend, nondim, grid_name);
224
+ add_field<Computed>(" col_int_gas_spec_tend_cw" , tensor2d_gas_tend_cw, nondim, grid_name);
225
+ // and finally, the weights used by vert_contraction()
226
+ add_field<Computed>(" wts_gas_spec_tends" , vector3d_gas_tend, nondim, grid_name);
227
+
228
+ // ===============================================================================================
229
+ // mam::microphysics wants the full column of these tendencies
230
+ // TODO: is it better for each column's threadTeam to have its own 3d view,
231
+ // or subview into a 4d view that contains all 'ncol' columns of these?
232
+ // view_3d qgcm_tendaa("diag_tendency-col", nlev, num_gas_spec_, mam4::microphysics::nqtendaa());
233
+ // view_3d qqcwgcm_tendaa("diag_tendency_cw-col", nlev, num_gas_spec_, mam4::microphysics::nqqcwtendaa());
234
+ // Kokkos::deep_copy(qgcm_tendaa, 0.0);
235
+ // Kokkos::deep_copy(qqcwgcm_tendaa, 0.0);
236
+ // ===============================================================================================
237
+
200
238
// Creating a Linoz reader and setting Linoz parameters involves reading data
201
239
// from a file and configuring the necessary parameters for the Linoz model.
202
240
{
@@ -387,7 +425,8 @@ void MAMMicrophysics::init_buffers(const ATMBufferManager &buffer_manager) {
387
425
int MAMMicrophysics::get_len_temporary_views () {
388
426
const int photo_table_len = get_photo_table_work_len (photo_table_);
389
427
const int sethet_work_len = mam4::mo_sethet::get_total_work_len_sethet ();
390
- constexpr int extcnt = mam4::gas_chemistry::extcnt;
428
+ // FIXME: moved this to class
429
+ // constexpr int extcnt = mam4::gas_chemistry::extcnt;
391
430
int work_len = 0 ;
392
431
// work_photo_table_
393
432
work_len += ncol_ * photo_table_len;
@@ -398,13 +437,14 @@ int MAMMicrophysics::get_len_temporary_views() {
398
437
// invariants_
399
438
work_len += ncol_ * nlev_ * mam4::gas_chemistry::nfs;
400
439
// extfrc_
401
- work_len += ncol_ * nlev_ * extcnt ;
440
+ work_len += ncol_ * nlev_ * extcnt_ ;
402
441
return work_len;
403
442
}
404
443
void MAMMicrophysics::init_temporary_views () {
405
444
const int photo_table_len = get_photo_table_work_len (photo_table_);
406
445
const int sethet_work_len = mam4::mo_sethet::get_total_work_len_sethet ();
407
- constexpr int extcnt = mam4::gas_chemistry::extcnt;
446
+ // FIXME: moved this to class
447
+ // constexpr int extcnt = mam4::gas_chemistry::extcnt;
408
448
auto work_ptr = (Real *)buffer_.temporary_views .data ();
409
449
410
450
work_photo_table_ = view_2d (work_ptr, ncol_, photo_table_len);
@@ -416,8 +456,8 @@ void MAMMicrophysics::init_temporary_views() {
416
456
work_ptr += ncol_ * nlev_ * mam4::mo_photo::phtcnt;
417
457
invariants_ = view_3d (work_ptr, ncol_, nlev_, mam4::gas_chemistry::nfs);
418
458
work_ptr += ncol_ * nlev_ * mam4::gas_chemistry::nfs;
419
- extfrc_ = view_3d (work_ptr, ncol_, nlev_, extcnt );
420
- work_ptr += ncol_ * nlev_ * extcnt ;
459
+ extfrc_ = view_3d (work_ptr, ncol_, nlev_, extcnt_ );
460
+ work_ptr += ncol_ * nlev_ * extcnt_ ;
421
461
422
462
// Error check
423
463
// NOTE: workspace_provided can be larger than workspace_used, but let's try
@@ -774,18 +814,18 @@ void MAMMicrophysics::run_impl(const double dt) {
774
814
Kokkos::deep_copy (acos_cosine_zenith_, acos_cosine_zenith_host_);
775
815
}
776
816
const auto zenith_angle = acos_cosine_zenith_;
777
- constexpr int gas_pcnst = mam_coupling::gas_pcnst ();
778
817
779
- const auto &extfrc = extfrc_;
780
- const auto &forcings = forcings_;
781
- constexpr int extcnt = mam4::gas_chemistry::extcnt;
818
+ const auto &extfrc = extfrc_;
819
+ const auto &forcings = forcings_;
820
+ // FIXME: moved this to class
821
+ // constexpr int extcnt = mam4::gas_chemistry::extcnt;
782
822
783
823
const int offset_aerosol = mam4::utils::gasses_start_ind ();
784
- Real adv_mass_kg_per_moles[gas_pcnst ];
824
+ Real adv_mass_kg_per_moles[num_gas_spec_ ];
785
825
// NOTE: Making copies of clsmap_4 and permute_4 to fix undefined arrays on
786
826
// the device.
787
- int clsmap_4[gas_pcnst ], permute_4[gas_pcnst ];
788
- for (int i = 0 ; i < gas_pcnst ; ++i) {
827
+ int clsmap_4[num_gas_spec_ ], permute_4[num_gas_spec_ ];
828
+ for (int i = 0 ; i < num_gas_spec_ ; ++i) {
789
829
// NOTE: state_q is kg/kg-dry-air; adv_mass is in g/mole.
790
830
// Convert adv_mass to kg/mole as vmr_from_mmr function uses
791
831
// molec_weight_dry_air with kg/mole units
@@ -803,13 +843,22 @@ void MAMMicrophysics::run_impl(const double dt) {
803
843
const auto &index_season_lai = index_season_lai_;
804
844
const int pcnst = mam4::pcnst;
805
845
806
- Kokkos::Array<Real, gas_pcnst > molar_mass_g_per_mol_tmp;
807
- for (int i = 0 ; i < gas_pcnst ; ++i) {
846
+ Kokkos::Array<Real, num_gas_spec_ > molar_mass_g_per_mol_tmp;
847
+ for (int i = 0 ; i < num_gas_spec_ ; ++i) {
808
848
molar_mass_g_per_mol_tmp[i] = mam4::gas_chemistry::adv_mass[i]; // host-only access
809
849
}
810
850
851
+ // these are computed in the column loop and used afterward, so declare here
852
+ // TODO: consider enabling/disabling this based on whether output is required--this would
853
+ // require another version of perform_atmospheric_chemistry_and_microphysics(), potentially
854
+ // templated on the forthcoming diagnostics struct
855
+ // nonetheless, skip for now
856
+ // Slice into the diagnostic fields to pass a single column to mam4xx::microphysics
857
+ auto gas_spec_tend = get_field_out (" gas_spec_tendencies" );
858
+ auto gas_spec_tend_cw = get_field_out (" gas_spec_tendencies_cw" );
859
+ auto gas_spec_tend_wts = get_field_out (" wts_gas_spec_tends" );
811
860
// NOTE: we need to initialize photo_rates_
812
- Kokkos::deep_copy (photo_rates_,0.0 );
861
+ Kokkos::deep_copy (photo_rates_, 0.0 );
813
862
// loop over atmosphere columns and compute aerosol microphyscs
814
863
Kokkos::parallel_for (
815
864
" MAMMicrophysics::run_impl" , policy,
@@ -833,9 +882,9 @@ void MAMMicrophysics::run_impl(const double dt) {
833
882
mam_coupling::aerosols_for_column (dry_aero, icol);
834
883
835
884
const auto invariants_icol = ekat::subview (invariants, icol);
836
- mam4::mo_setext::Forcing forcings_in[extcnt ];
885
+ mam4::mo_setext::Forcing forcings_in[extcnt_ ];
837
886
838
- for (int i = 0 ; i < extcnt ; ++i) {
887
+ for (int i = 0 ; i < extcnt_ ; ++i) {
839
888
const int nsectors = forcings[i].nsectors ;
840
889
const int frc_ndx = forcings[i].frc_ndx ;
841
890
const auto file_alt_data = forcings[i].file_alt_data ;
@@ -921,20 +970,19 @@ void MAMMicrophysics::run_impl(const double dt) {
921
970
}
922
971
}
923
972
}
973
+
974
+ auto gas_spec_tend_V = gas_spec_tend.get_view <Real****>();
975
+ auto gas_spec_tend_cw_V = gas_spec_tend_cw.get_view <Real****>();
976
+ auto gas_spec_tend_wts_V = gas_spec_tend_wts.get_view <Real***>();
977
+ // NOTE: these are called "qgcm_tendaa" and "qqcwgcm_tendaa" in mam4xx
978
+ auto gas_spec_tend_col = ekat::subview (gas_spec_tend_V, icol);
979
+ auto gas_spec_tend_cw_col = ekat::subview (gas_spec_tend_cw_V, icol);
980
+
924
981
// These output values need to be put somewhere:
925
- const auto aqso4_flx_col = ekat::subview (aqso4_flx, icol); // deposition flux of so4 [mole/mole/s]
926
- const auto aqh2so4_flx_col = ekat::subview (aqh2so4_flx, icol); // deposition flux of h2so4 [mole/mole/s]
927
- Real dflx_col[gas_pcnst] = {}; // deposition velocity [1/cm/s]
928
- Real dvel_col[gas_pcnst] = {}; // deposition flux [1/cm^2/s]
929
- // mam::microphysics wants the full column of these tendencies
930
- // TODO: is it better for each column's threadTeam to have its own 3d view,
931
- // or subview into a 4d view that contains all 'ncol' columns of these?
932
- view_3d qgcm_tendaa (" diag_tendency-col" , nlev, gas_pcnst, mam4::microphysics::nqtendaa ());
933
- view_3d qqcwgcm_tendaa (" diag_tendency_cw-col" , nlev, gas_pcnst, mam4::microphysics::nqqcwtendaa ());
934
- Kokkos::deep_copy (qgcm_tendaa, 0.0 );
935
- Kokkos::deep_copy (qqcwgcm_tendaa, 0.0 );
982
+ Real dflx_col[num_gas_spec_] = {}; // deposition velocity [1/cm/s]
983
+ Real dvel_col[num_gas_spec_] = {}; // deposition flux [1/cm^2/s]
936
984
// Output: values are dvel, dflx
937
- // Diagnostic Output: qgcm_tendaa, qqcwgcm_tendaa
985
+ // Diagnostic Output: gas_spec_tend_col, gas_spec_tend_cw_col
938
986
// Input/Output: progs::stateq, progs::qqcw
939
987
team.team_barrier ();
940
988
mam4::microphysics::perform_atmospheric_chemistry_and_microphysics (
@@ -955,31 +1003,41 @@ void MAMMicrophysics::run_impl(const double dt) {
955
1003
dvel_col, dflx_col, qgcm_tendaa, qqcwgcm_tendaa, progs);
956
1004
957
1005
team.team_barrier ();
1006
+ const auto pdel_col = ekat::subview (dry_atm.p_del , icol);
1007
+ auto wts_col = ekat::subview (gas_spec_tend_wts_V, icol);
1008
+ set_vert_contraction_weights (wts_col, molar_mass_g_per_mol_tmp, pdel_col, nlev_);
1009
+
958
1010
// Update constituent fluxes with gas drydep fluxes (dflx)
959
1011
// FIXME: Possible units mismatch (dflx is in kg/cm2/s but
960
1012
// constituent_fluxes is kg/m2/s) (Following mimics Fortran code
961
1013
// behavior but we should look into it)
962
1014
Kokkos::parallel_for (Kokkos::TeamVectorRange (team, offset_aerosol, pcnst), [&](int ispc) {
963
1015
constituent_fluxes (icol, ispc) -= dflx_col[ispc - offset_aerosol];
964
1016
});
965
-
966
1017
}); // parallel_for for the column loop
967
1018
Kokkos::fence ();
968
1019
1020
+ // the variables in these column-integrated tendencies correspond to these MAM diagnostics:
1021
+ // ['<xyz>_sfgaex1', '<xyz>_sfgaex2', '<xyz>_sfnnuc1', '<xyz>_sfcoag1']
1022
+ auto col_int_gas_spec_tend = get_field_out (" col_int_gas_spec_tend" );
1023
+ auto col_int_gas_spec_tend_cw = get_field_out (" col_int_gas_spec_tend_cw" );
1024
+ vert_contraction<Real>(col_int_gas_spec_tend, gas_spec_tend, gas_spec_tend_wts);
1025
+ vert_contraction<Real>(col_int_gas_spec_tend_cw, gas_spec_tend_cw, gas_spec_tend_wts);
1026
+
969
1027
auto extfrc_fm = get_field_out (" mam4_external_forcing" ).get_view <Real***>();
970
1028
971
1029
// Avogadro's number [molecules/mol]
972
1030
const Real Avogadro = haero::Constants::avogadro;
973
1031
// Mapping from external forcing species index to physics constituent index
974
1032
// NOTE: These indices should match the species in extfrc_lst
975
1033
// TODO: getting rid of hard-coded indices
976
- Kokkos::Array<int , extcnt > extfrc_pcnst_index = {3 , 6 , 14 , 27 , 28 , 13 , 18 , 30 , 5 };
1034
+ Kokkos::Array<int , extcnt_ > extfrc_pcnst_index = {3 , 6 , 14 , 27 , 28 , 13 , 18 , 30 , 5 };
977
1035
978
1036
// Transpose extfrc_ from internal layout [ncol][nlev][extcnt]
979
1037
// to output layout [ncol][extcnt][nlev]
980
1038
// This aligns with expected field storage in the EAMxx infrastructure.
981
1039
Kokkos::parallel_for (" transpose_extfrc" ,
982
- Kokkos::MDRangePolicy<Kokkos::Rank<3 >>({0 ,0 ,0 }, {ncol, extcnt , nlev}),
1040
+ Kokkos::MDRangePolicy<Kokkos::Rank<3 >>({0 ,0 ,0 }, {ncol, extcnt_ , nlev}),
983
1041
KOKKOS_LAMBDA (const int i, const int j, const int k) {
984
1042
const int pcnst_idx = extfrc_pcnst_index[j];
985
1043
const Real molar_mass_g_per_mol = molar_mass_g_per_mol_tmp[pcnst_idx]; // g/mol
0 commit comments