11#include " eamxx_cosp.hpp"
22#include " cosp_functions.hpp"
33#include " share/property_checks/field_within_interval_check.hpp"
4+ #include " physics/share/physics_constants.hpp"
45
56#include " ekat/ekat_assert.hpp"
67#include " ekat/util/ekat_units.hpp"
@@ -93,6 +94,12 @@ void Cosp::set_grids(const std::shared_ptr<const GridsManager> grids_manager)
9394 add_field<Computed>(" modis_ctptau" , scalar4d_ctptau, percent, grid_name, 1 );
9495 add_field<Computed>(" misr_cthtau" , scalar4d_cthtau, percent, grid_name, 1 );
9596 add_field<Computed>(" cosp_sunlit" , scalar2d, nondim, grid_name);
97+
98+ // We can allocate these now
99+ m_z_mid = Field (FieldIdentifier (" z_mid" ,scalar3d_mid,m,grid_name));
100+ m_z_int = Field (FieldIdentifier (" z_int" ,scalar3d_int,m,grid_name));
101+ m_z_mid.allocate_view ();
102+ m_z_int.allocate_view ();
96103}
97104
98105// =========================================================================================
@@ -131,106 +138,127 @@ void Cosp::run_impl (const double dt)
131138 // Make sure cosp frequency is multiple of rad frequency?
132139
133140 // Compare frequency in steps with current timestep
134- auto ts = timestamp ();
135- auto update_cosp = cosp_do (cosp_freq_in_steps, ts.get_num_steps ());
136-
137- // Get fields from field manager; note that we get host views because this
138- // interface serves primarily as a wrapper to a c++ to f90 bridge for the COSP
139- // all then need to be copied to layoutLeft views to permute the indices for
140- // F90.
141- //
142- // Need to make sure device data is synced to host?
143- get_field_in (" qv" ).sync_to_host ();
144- get_field_in (" qc" ).sync_to_host ();
145- get_field_in (" qi" ).sync_to_host ();
146- get_field_in (" sunlit" ).sync_to_host ();
147- get_field_in (" surf_radiative_T" ).sync_to_host ();
148- get_field_in (" T_mid" ).sync_to_host ();
149- get_field_in (" p_mid" ).sync_to_host ();
150- get_field_in (" p_int" ).sync_to_host ();
151- get_field_in (" cldfrac_rad" ).sync_to_host ();
152- get_field_in (" eff_radius_qc" ).sync_to_host ();
153- get_field_in (" eff_radius_qi" ).sync_to_host ();
154- get_field_in (" dtau067" ).sync_to_host ();
155- get_field_in (" dtau105" ).sync_to_host ();
156- get_field_in (" phis" ).sync_to_host ();
157- get_field_in (" pseudo_density" ).sync_to_host ();
158-
159- auto qv = get_field_in (" qv" ).get_view <const Real**, Host>();
160- auto qc = get_field_in (" qc" ).get_view <const Real**, Host>();
161- auto qi = get_field_in (" qi" ).get_view <const Real**, Host>();
162- auto sunlit = get_field_in (" sunlit" ).get_view <const Real*, Host>();
163- auto skt = get_field_in (" surf_radiative_T" ).get_view <const Real*, Host>();
164- auto T_mid = get_field_in (" T_mid" ).get_view <const Real**, Host>();
165- auto p_mid = get_field_in (" p_mid" ).get_view <const Real**, Host>();
166- auto p_int = get_field_in (" p_int" ).get_view <const Real**, Host>();
167- auto phis = get_field_in (" phis" ).get_view <const Real*, Host>();
168- auto pseudo_density = get_field_in (" pseudo_density" ).get_view <const Real**, Host>();
169- auto cldfrac = get_field_in (" cldfrac_rad" ).get_view <const Real**, Host>();
170- auto reff_qc = get_field_in (" eff_radius_qc" ).get_view <const Real**, Host>();
171- auto reff_qi = get_field_in (" eff_radius_qi" ).get_view <const Real**, Host>();
172- auto dtau067 = get_field_in (" dtau067" ).get_view <const Real**, Host>();
173- auto dtau105 = get_field_in (" dtau105" ).get_view <const Real**, Host>();
174- auto isccp_cldtot = get_field_out (" isccp_cldtot" ).get_view <Real*, Host>();
175- auto isccp_ctptau = get_field_out (" isccp_ctptau" ).get_view <Real***, Host>();
176- auto modis_ctptau = get_field_out (" modis_ctptau" ).get_view <Real***, Host>();
177- auto misr_cthtau = get_field_out (" misr_cthtau" ).get_view <Real***, Host>();
178- auto cosp_sunlit = get_field_out (" cosp_sunlit" ).get_view <Real*, Host>(); // Copy of sunlit flag with COSP frequency for proper averaging
179-
180- // Compute heights
181- const auto z_mid = CospFunc::view_2d<Real>(" z_mid" , m_num_cols, m_num_levs);
182- const auto z_int = CospFunc::view_2d<Real>(" z_int" , m_num_cols, m_num_levs+1 );
183- const auto dz = z_mid; // reuse tmp memory for dz
184- const auto ncol = m_num_cols;
185- const auto nlev = m_num_levs;
186- // calculate_z_int contains a team-level parallel_scan, which requires a special policy
187- // TODO: do this on device?
188- const auto scan_policy = ekat::ExeSpaceUtils<KTH::ExeSpace>::get_thread_range_parallel_scan_team_policy (ncol, nlev);
189- Kokkos::parallel_for (scan_policy, KOKKOS_LAMBDA (const KTH::MemberType& team) {
190- const int i = team.league_rank ();
191- const auto dz_s = ekat::subview (dz, i);
192- const auto p_mid_s = ekat::subview (p_mid, i);
193- const auto T_mid_s = ekat::subview (T_mid, i);
194- const auto qv_s = ekat::subview (qv, i);
195- const auto z_int_s = ekat::subview (z_int, i);
196- const auto z_mid_s = ekat::subview (z_mid, i);
197- const Real z_surf = phis (i) / 9.81 ;
198- const auto pseudo_density_s = ekat::subview (pseudo_density, i);
199- PF::calculate_dz (team, pseudo_density_s, p_mid_s, T_mid_s, qv_s, dz_s);
200- team.team_barrier ();
201- PF::calculate_z_int (team,nlev,dz_s,z_surf,z_int_s);
202- team.team_barrier ();
203- PF::calculate_z_mid (team,nlev,z_int_s,z_mid_s);
204- team.team_barrier ();
205- });
141+ // NOTE: timestamp() returns the time RIGHT BEFORE the call to run
142+ auto end_of_step = timestamp ()+dt;
143+ auto update_cosp = cosp_do (cosp_freq_in_steps, end_of_step.get_num_steps ());
206144
207145 // Call COSP wrapper routines
208146 if (update_cosp) {
147+ // Get fields from field manager; note that we get host views because this
148+ // interface serves primarily as a wrapper to a c++ to f90 bridge for the COSP
149+ // all then need to be copied to layoutLeft views to permute the indices for
150+ // F90.
151+
152+ // Ensure host data of input fields is up to date
153+ get_field_in (" qv" ).sync_to_host ();
154+ get_field_in (" qc" ).sync_to_host ();
155+ get_field_in (" qi" ).sync_to_host ();
156+ get_field_in (" sunlit" ).sync_to_host ();
157+ get_field_in (" surf_radiative_T" ).sync_to_host ();
158+ get_field_in (" T_mid" ).sync_to_host ();
159+ get_field_in (" p_int" ).sync_to_host ();
160+ get_field_in (" cldfrac_rad" ).sync_to_host ();
161+ get_field_in (" eff_radius_qc" ).sync_to_host ();
162+ get_field_in (" eff_radius_qi" ).sync_to_host ();
163+ get_field_in (" dtau067" ).sync_to_host ();
164+ get_field_in (" dtau105" ).sync_to_host ();
165+
166+ // Compute z_mid
167+ const auto T_mid_d = get_field_in (" T_mid" ).get_view <const Real**>();
168+ const auto qv_d = get_field_in (" qv" ).get_view <const Real**>();
169+ const auto p_mid_d = get_field_in (" p_mid" ).get_view <const Real**>();
170+ const auto phis_d = get_field_in (" phis" ).get_view <const Real*>();
171+ const auto pseudo_density_d = get_field_in (" pseudo_density" ).get_view <const Real**>();
172+ const auto z_mid_d = m_z_mid.get_view <Real**>();
173+ const auto z_int_d = m_z_int.get_view <Real**>();
174+ const auto ncol = m_num_cols;
175+ const auto nlev = m_num_levs;
176+
177+ using KT = KokkosTypes<DefaultDevice>;
178+ using ExeSpace = typename KT::ExeSpace;
179+ using ESU = ekat::ExeSpaceUtils<ExeSpace>;
180+ using PF = scream::PhysicsFunctions<DefaultDevice>;
181+
182+ const auto scan_policy = ESU::get_thread_range_parallel_scan_team_policy (ncol, nlev);
183+ const auto g = physics::Constants<Real>::gravit;
184+ Kokkos::parallel_for (scan_policy, KOKKOS_LAMBDA (const KT::MemberType& team) {
185+ const int i = team.league_rank ();
186+ const auto p_mid_s = ekat::subview (p_mid_d, i);
187+ const auto T_mid_s = ekat::subview (T_mid_d, i);
188+ const auto qv_s = ekat::subview (qv_d, i);
189+ const auto z_int_s = ekat::subview (z_int_d, i);
190+ const auto z_mid_s = ekat::subview (z_mid_d, i);
191+ const Real z_surf = phis_d (i) / g;
192+ const auto pseudo_density_s = ekat::subview (pseudo_density_d, i);
193+
194+ // 1. Compute dz (recycle z_mid_s as a temporary)
195+ const auto dz_s = z_mid_s; //
196+ PF::calculate_dz (team, pseudo_density_s, p_mid_s, T_mid_s, qv_s, dz_s);
197+ team.team_barrier ();
198+
199+ // 2. Compute z_int (vertical scan)
200+ PF::calculate_z_int (team,nlev,dz_s,z_surf,z_int_s);
201+ team.team_barrier ();
202+
203+ // 3. Compute z_mid (int->mid interpolation)
204+ PF::calculate_z_mid (team,nlev,z_int_s,z_mid_s);
205+ team.team_barrier ();
206+ });
207+ Kokkos::fence ();
208+
209+ m_z_mid.sync_to_host ();
210+ const auto z_mid_h = m_z_mid.get_view <const Real**,Host>();
211+ const auto T_mid_h = get_field_in (" T_mid" ).get_view <const Real**, Host>();
212+ const auto qv_h = get_field_in (" qv" ).get_view <const Real**, Host>();
213+ const auto p_mid_h = get_field_in (" p_mid" ).get_view <const Real**,Host>();
214+ const auto qc_h = get_field_in (" qc" ).get_view <const Real**, Host>();
215+ const auto qi_h = get_field_in (" qi" ).get_view <const Real**, Host>();
216+ const auto sunlit_h = get_field_in (" sunlit" ).get_view <const Real*, Host>();
217+ const auto skt_h = get_field_in (" surf_radiative_T" ).get_view <const Real*, Host>();
218+ const auto p_int_h = get_field_in (" p_int" ).get_view <const Real**, Host>();
219+ const auto cldfrac_h = get_field_in (" cldfrac_rad" ).get_view <const Real**, Host>();
220+ const auto reff_qc_h = get_field_in (" eff_radius_qc" ).get_view <const Real**, Host>();
221+ const auto reff_qi_h = get_field_in (" eff_radius_qi" ).get_view <const Real**, Host>();
222+ const auto dtau067_h = get_field_in (" dtau067" ).get_view <const Real**, Host>();
223+ const auto dtau105_h = get_field_in (" dtau105" ).get_view <const Real**, Host>();
224+
225+ auto isccp_cldtot_h = get_field_out (" isccp_cldtot" ).get_view <Real*, Host>();
226+ auto isccp_ctptau_h = get_field_out (" isccp_ctptau" ).get_view <Real***, Host>();
227+ auto modis_ctptau_h = get_field_out (" modis_ctptau" ).get_view <Real***, Host>();
228+ auto misr_cthtau_h = get_field_out (" misr_cthtau" ).get_view <Real***, Host>();
229+ auto cosp_sunlit_h = get_field_out (" cosp_sunlit" ).get_view <Real*, Host>(); // Copy of sunlit flag with COSP frequency for proper averaging
230+
209231 Real emsfc_lw = 0.99 ;
210- Kokkos::deep_copy (cosp_sunlit, sunlit);
211- CospFunc::view_2d<const Real> z_mid_c = z_mid; // Need a const version of z_mid for call to CospFunc::main
232+ Kokkos::deep_copy (cosp_sunlit_h, sunlit_h);
212233 CospFunc::main (
213- m_num_cols, m_num_subcols, m_num_levs, m_num_tau, m_num_ctp, m_num_cth,
214- emsfc_lw, sunlit, skt, T_mid, p_mid, p_int, z_mid_c, qv, qc, qi ,
215- cldfrac, reff_qc, reff_qi, dtau067, dtau105 ,
216- isccp_cldtot, isccp_ctptau, modis_ctptau, misr_cthtau
234+ m_num_cols, m_num_subcols, m_num_levs, m_num_tau, m_num_ctp, m_num_cth, emsfc_lw,
235+ sunlit_h, skt_h, T_mid_h, p_mid_h, p_int_h, z_mid_h, qv_h, qc_h, qi_h ,
236+ cldfrac_h, reff_qc_h, reff_qi_h, dtau067_h, dtau105_h ,
237+ isccp_cldtot_h, isccp_ctptau_h, modis_ctptau_h, misr_cthtau_h
217238 );
218239 // Remask night values to ZERO since our I/O does not know how to handle masked/missing values
219240 // in temporal averages; this is all host data, so we can just use host loops like its the 1980s
220241 for (int i = 0 ; i < m_num_cols; i++) {
221- if (sunlit (i) == 0 ) {
222- isccp_cldtot (i) = 0 ;
223- for (int j = 0 ; j < m_num_tau; j++) {
224- for (int k = 0 ; k < m_num_ctp; k++) {
225- isccp_ctptau (i,j,k) = 0 ;
226- modis_ctptau (i,j,k) = 0 ;
227- }
228- for (int k = 0 ; k < m_num_cth; k++) {
229- misr_cthtau (i,j,k) = 0 ;
230- }
231- }
242+ if (sunlit_h (i) == 0 ) {
243+ isccp_cldtot_h (i) = 0 ;
244+ for (int j = 0 ; j < m_num_tau; j++) {
245+ for (int k = 0 ; k < m_num_ctp; k++) {
246+ isccp_ctptau_h (i,j,k) = 0 ;
247+ modis_ctptau_h (i,j,k) = 0 ;
248+ }
249+ for (int k = 0 ; k < m_num_cth; k++) {
250+ misr_cthtau_h (i,j,k) = 0 ;
251+ }
232252 }
253+ }
233254 }
255+
256+ // Make sure dev data is up to date
257+ get_field_out (" isccp_cldtot" ).sync_to_dev ();
258+ get_field_out (" isccp_ctptau" ).sync_to_dev ();
259+ get_field_out (" modis_ctptau" ).sync_to_dev ();
260+ get_field_out (" misr_cthtau" ).sync_to_dev ();
261+ get_field_out (" cosp_sunlit" ).sync_to_dev ();
234262 } else {
235263 // If not updating COSP statistics, set these to ZERO; this essentially weights
236264 // the ISCCP cloud properties by the sunlit mask. What will be output for time-averages
@@ -241,17 +269,12 @@ void Cosp::run_impl (const double dt)
241269 // avg(X) = sum(M * X) / sum(M) = (sum(M * X)/N) / (sum(M)/N) = avg(M * X) / avg(M)
242270 //
243271 // TODO: mask this when/if the AD ever supports masked averages
244- Kokkos::deep_copy ( isccp_cldtot, 0. 0 );
245- Kokkos::deep_copy ( isccp_ctptau, 0. 0 );
246- Kokkos::deep_copy ( modis_ctptau, 0. 0 );
247- Kokkos::deep_copy ( misr_cthtau, 0. 0 );
248- Kokkos::deep_copy ( cosp_sunlit, 0. 0 );
272+ get_field_out ( " isccp_cldtot" ). deep_copy ( 0 );
273+ get_field_out ( " isccp_ctptau" ). deep_copy ( 0 );
274+ get_field_out ( " modis_ctptau" ). deep_copy ( 0 );
275+ get_field_out ( " misr_cthtau" ). deep_copy ( 0 );
276+ get_field_out ( " cosp_sunlit" ). deep_copy ( 0 );
249277 }
250- get_field_out (" isccp_cldtot" ).sync_to_dev ();
251- get_field_out (" isccp_ctptau" ).sync_to_dev ();
252- get_field_out (" modis_ctptau" ).sync_to_dev ();
253- get_field_out (" misr_cthtau" ).sync_to_dev ();
254- get_field_out (" cosp_sunlit" ).sync_to_dev ();
255278}
256279
257280// =========================================================================================
@@ -260,6 +283,5 @@ void Cosp::finalize_impl()
260283 // Finalize COSP wrappers
261284 CospFunc::finalize ();
262285}
263- // =========================================================================================
264286
265287} // namespace scream
0 commit comments