@@ -21,7 +21,7 @@ module dyn_mpas_subdriver
2121 pio_inq_varid, pio_inq_varndims, pio_inq_vartype, pio_noerr
2222
2323 ! Modules from MPAS.
24- use atm_core, only: atm_mpas_init_block
24+ use atm_core, only: atm_compute_output_diagnostics, atm_do_timestep, atm_mpas_init_block
2525 use atm_core_interface, only: atm_setup_core, atm_setup_domain
2626 use atm_time_integration, only: mpas_atm_dynamics_init
2727 use mpas_atm_dimensions, only: mpas_atm_set_dims
@@ -34,14 +34,15 @@ module dyn_mpas_subdriver
3434 mpas_pool_type, mpas_pool_field_info_type, &
3535 mpas_pool_character, mpas_pool_real, mpas_pool_integer, &
3636 mpas_stream_type, mpas_stream_noerr, &
37- mpas_time_type, mpas_start_time, &
37+ mpas_time_type, mpas_timeinterval_type, mpas_now, mpas_start_time, &
3838 mpas_io_native_precision, mpas_io_pnetcdf, mpas_io_read, mpas_io_write, &
3939 field0dchar, field1dchar, &
4040 field0dinteger, field1dinteger, field2dinteger, field3dinteger, &
4141 field0dreal, field1dreal, field2dreal, field3dreal, field4dreal, field5dreal
4242 use mpas_dmpar, only: mpas_dmpar_exch_halo_field, &
4343 mpas_dmpar_max_int, mpas_dmpar_sum_int
4444 use mpas_domain_routines, only: mpas_allocate_domain
45+ use mpas_field_routines, only: mpas_allocate_scratch_field
4546 use mpas_framework, only: mpas_framework_init_phase1, mpas_framework_init_phase2
4647 use mpas_io_streams, only: mpas_createstream, mpas_closestream, mpas_streamaddfield, &
4748 mpas_readstream, mpas_writestream, mpas_writestreamatt
@@ -51,11 +52,13 @@ module dyn_mpas_subdriver
5152 mpas_pool_get_array, &
5253 mpas_pool_add_dimension, mpas_pool_get_dimension, &
5354 mpas_pool_get_field, mpas_pool_get_field_info, &
54- mpas_pool_initialize_time_levels
55+ mpas_pool_initialize_time_levels, mpas_pool_shift_time_levels
5556 use mpas_stream_inquiry, only: mpas_stream_inquiry_new_streaminfo
5657 use mpas_stream_manager, only: postread_reindex, prewrite_reindex, postwrite_reindex
5758 use mpas_string_utils, only: mpas_string_replace
58- use mpas_timekeeping, only: mpas_get_clock_time, mpas_get_time
59+ use mpas_timekeeping, only: mpas_advance_clock, mpas_get_clock_time, mpas_get_time, &
60+ mpas_set_timeinterval, &
61+ operator (+ ), operator (<)
5962 use mpas_vector_operations, only: mpas_initialize_vectors
6063
6164 implicit none
@@ -106,7 +109,8 @@ end subroutine model_error_if
106109 logical , allocatable :: is_water_species(:)
107110
108111 ! Initialized by `dyn_mpas_init_phase4`.
109- integer :: coupling_time_interval
112+ integer :: coupling_time_interval = 0
113+ integer :: number_of_time_steps = 0
110114 contains
111115 private
112116
@@ -123,6 +127,7 @@ end subroutine model_error_if
123127 procedure , pass, public :: compute_unit_vector = > dyn_mpas_compute_unit_vector
124128 procedure , pass, public :: compute_edge_wind = > dyn_mpas_compute_edge_wind
125129 procedure , pass, public :: init_phase4 = > dyn_mpas_init_phase4
130+ procedure , pass, public :: run = > dyn_mpas_run
126131
127132 ! Accessor subroutines for users to access internal states of MPAS dynamical core.
128133
@@ -2525,19 +2530,21 @@ end subroutine dyn_mpas_compute_unit_vector
25252530 ! > \date 16 January 2020
25262531 ! > \details
25272532 ! > This subroutine computes the edge-normal wind vectors at edge points
2528- ! > (i.e., `u` in MPAS `state` pool) from wind components at cell points
2533+ ! > (i.e., `u` in MPAS `state` pool) from the wind components at cell points
25292534 ! > (i.e., `uReconstruct{Zonal,Meridional}` in MPAS `diag` pool). In MPAS, the
25302535 ! > former are PROGNOSTIC variables, while the latter are DIAGNOSTIC variables
25312536 ! > that are "reconstructed" from the former. This subroutine is essentially the
25322537 ! > inverse function of that reconstruction. The purpose is to provide an
25332538 ! > alternative way for MPAS to initialize from zonal and meridional wind
2534- ! > components at cell points.
2539+ ! > components at cell points. If `wind_tendency` is `.true.`, this subroutine
2540+ ! > operates on the wind tendency due to physics instead.
25352541 ! > \addenda
25362542 ! > Ported and refactored for CAM-SIMA. (KCW, 2024-05-08)
25372543 !
25382544 !- ------------------------------------------------------------------------------
2539- subroutine dyn_mpas_compute_edge_wind (self )
2545+ subroutine dyn_mpas_compute_edge_wind (self , wind_tendency )
25402546 class(mpas_dynamical_core_type), intent (in ) :: self
2547+ logical , intent (in ) :: wind_tendency
25412548
25422549 character (* ), parameter :: subname = ' dyn_mpas_subdriver::dyn_mpas_compute_edge_wind'
25432550 integer :: cell1, cell2, i
@@ -2561,22 +2568,36 @@ subroutine dyn_mpas_compute_edge_wind(self)
25612568 nullify(uedge)
25622569
25632570 ! Make sure halo layers are up-to-date before computation.
2564- call self % exchange_halo(' uReconstructZonal' )
2565- call self % exchange_halo(' uReconstructMeridional' )
2571+ if (wind_tendency) then
2572+ call self % exchange_halo(' tend_uzonal' )
2573+ call self % exchange_halo(' tend_umerid' )
2574+ else
2575+ call self % exchange_halo(' uReconstructZonal' )
2576+ call self % exchange_halo(' uReconstructMeridional' )
2577+ end if
25662578
25672579 ! Input.
25682580 call self % get_variable_pointer(nedges, ' dim' , ' nEdges' )
25692581
2570- call self % get_variable_pointer(ucellzonal, ' diag' , ' uReconstructZonal' )
2571- call self % get_variable_pointer(ucellmeridional, ' diag' , ' uReconstructMeridional' )
2582+ if (wind_tendency) then
2583+ call self % get_variable_pointer(ucellzonal, ' tend_physics' , ' tend_uzonal' )
2584+ call self % get_variable_pointer(ucellmeridional, ' tend_physics' , ' tend_umerid' )
2585+ else
2586+ call self % get_variable_pointer(ucellzonal, ' diag' , ' uReconstructZonal' )
2587+ call self % get_variable_pointer(ucellmeridional, ' diag' , ' uReconstructMeridional' )
2588+ end if
25722589
25732590 call self % get_variable_pointer(cellsonedge, ' mesh' , ' cellsOnEdge' )
25742591 call self % get_variable_pointer(east, ' mesh' , ' east' )
25752592 call self % get_variable_pointer(north, ' mesh' , ' north' )
25762593 call self % get_variable_pointer(edgenormalvectors, ' mesh' , ' edgeNormalVectors' )
25772594
25782595 ! Output.
2579- call self % get_variable_pointer(uedge, ' state' , ' u' , time_level= 1 )
2596+ if (wind_tendency) then
2597+ call self % get_variable_pointer(uedge, ' tend_physics' , ' tend_ru_physics' )
2598+ else
2599+ call self % get_variable_pointer(uedge, ' state' , ' u' , time_level= 1 )
2600+ end if
25802601
25812602 do i = 1 , nedges
25822603 cell1 = cellsonedge(1 , i)
@@ -2607,7 +2628,11 @@ subroutine dyn_mpas_compute_edge_wind(self)
26072628 nullify(uedge)
26082629
26092630 ! Make sure halo layers are up-to-date after computation.
2610- call self % exchange_halo(' u' )
2631+ if (wind_tendency) then
2632+ call self % exchange_halo(' tend_ru_physics' )
2633+ else
2634+ call self % exchange_halo(' u' )
2635+ end if
26112636
26122637 call self % debug_print(subname // ' completed' )
26132638 end subroutine dyn_mpas_compute_edge_wind
@@ -2640,6 +2665,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
26402665 logical , pointer :: config_do_restart
26412666 real (rkind), pointer :: config_dt
26422667 type (field0dreal), pointer :: field_0d_real
2668+ type (field2dreal), pointer :: field_2d_real
26432669 type (mpas_pool_type), pointer :: mpas_pool
26442670 type (mpas_time_type) :: mpas_time
26452671
@@ -2651,6 +2677,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
26512677 nullify(config_do_restart)
26522678 nullify(config_dt)
26532679 nullify(field_0d_real)
2680+ nullify(field_2d_real)
26542681 nullify(mpas_pool)
26552682
26562683 if (coupling_time_interval <= 0 ) then
@@ -2673,6 +2700,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
26732700 end if
26742701
26752702 self % coupling_time_interval = coupling_time_interval
2703+ self % number_of_time_steps = 0
26762704
26772705 call self % debug_print(' Coupling time interval is ' // stringify([real (self % coupling_time_interval, rkind)]) // &
26782706 ' seconds' )
@@ -2814,6 +2842,16 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
28142842 ! Prepare dynamics for time integration.
28152843 call mpas_atm_dynamics_init(self % domain_ptr)
28162844
2845+ ! Some additional "scratch" fields are needed for interoperability with CAM-SIMA, but they are not initialized by
2846+ ! `mpas_atm_dynamics_init`. Initialize them below.
2847+ call mpas_pool_get_field(self % domain_ptr % blocklist % allfields, ' tend_uzonal' , field_2d_real, timelevel= 1 )
2848+ call mpas_allocate_scratch_field(field_2d_real)
2849+ nullify(field_2d_real)
2850+
2851+ call mpas_pool_get_field(self % domain_ptr % blocklist % allfields, ' tend_umerid' , field_2d_real, timelevel= 1 )
2852+ call mpas_allocate_scratch_field(field_2d_real)
2853+ nullify(field_2d_real)
2854+
28172855 call self % debug_print(subname // ' completed' )
28182856
28192857 call self % debug_print(' Successful initialization of MPAS dynamical core' )
@@ -2867,6 +2905,113 @@ pure function almost_equal(a, b, absolute_tolerance, relative_tolerance)
28672905 end function almost_equal
28682906 end subroutine dyn_mpas_init_phase4
28692907
2908+ !- ------------------------------------------------------------------------------
2909+ ! subroutine dyn_mpas_run
2910+ !
2911+ ! > \brief Integrates the dynamical states with time
2912+ ! > \author Michael Duda
2913+ ! > \date 29 February 2020
2914+ ! > \details
2915+ ! > This subroutine calls MPAS dynamical solver in a loop, with each iteration
2916+ ! > of the loop advancing the dynamical states forward by one time step, until
2917+ ! > the coupling time interval is reached.
2918+ ! > Essentially, it closely follows what is done in `atm_core_run`, but without
2919+ ! > any calls to MPAS diagnostics manager or MPAS stream manager.
2920+ ! > \addenda
2921+ ! > Ported and refactored for CAM-SIMA. (KCW, 2024-06-21)
2922+ !
2923+ !- ------------------------------------------------------------------------------
2924+ subroutine dyn_mpas_run (self )
2925+ class(mpas_dynamical_core_type), intent (inout ) :: self
2926+
2927+ character (* ), parameter :: subname = ' dyn_mpas_subdriver::dyn_mpas_run'
2928+ character (strkind) :: date_time
2929+ integer :: ierr
2930+ real (rkind), pointer :: config_dt
2931+ type (mpas_pool_type), pointer :: mpas_pool_diag, mpas_pool_mesh, mpas_pool_state
2932+ type (mpas_time_type) :: mpas_time_end, mpas_time_now ! This derived type is analogous to `ESMF_Time`.
2933+ type (mpas_timeinterval_type) :: mpas_time_interval ! This derived type is analogous to `ESMF_TimeInterval`.
2934+
2935+ call self % debug_print(subname // ' entered' )
2936+
2937+ nullify(config_dt)
2938+ nullify(mpas_pool_diag, mpas_pool_mesh, mpas_pool_state)
2939+
2940+ call self % get_variable_pointer(config_dt, ' cfg' , ' config_dt' )
2941+ call self % get_pool_pointer(mpas_pool_diag, ' diag' )
2942+ call self % get_pool_pointer(mpas_pool_mesh, ' mesh' )
2943+ call self % get_pool_pointer(mpas_pool_state, ' state' )
2944+
2945+ mpas_time_now = mpas_get_clock_time(self % domain_ptr % clock, mpas_now, ierr= ierr)
2946+
2947+ if (ierr /= 0 ) then
2948+ call self % model_error(' Failed to get time for "mpas_now"' , subname, __LINE__)
2949+ end if
2950+
2951+ call mpas_get_time(mpas_time_now, datetimestring= date_time, ierr= ierr)
2952+
2953+ if (ierr /= 0 ) then
2954+ call self % model_error(' Failed to get time for "mpas_now"' , subname, __LINE__)
2955+ end if
2956+
2957+ call self % debug_print(' Time integration of MPAS dynamical core begins at ' // trim (adjustl (date_time)))
2958+
2959+ call mpas_set_timeinterval(mpas_time_interval, s= self % coupling_time_interval, ierr= ierr)
2960+
2961+ if (ierr /= 0 ) then
2962+ call self % model_error(' Failed to set coupling time interval' , subname, __LINE__)
2963+ end if
2964+
2965+ ! The `+` operator is overloaded here.
2966+ mpas_time_end = mpas_time_now + mpas_time_interval
2967+
2968+ ! Integrate until the coupling time interval is reached.
2969+ ! The `<` operator is overloaded here.
2970+ do while (mpas_time_now < mpas_time_end)
2971+ ! Number of time steps that has been completed in this MPAS dynamical core instance.
2972+ self % number_of_time_steps = self % number_of_time_steps + 1
2973+
2974+ ! Advance the dynamical states forward in time by `config_dt` seconds.
2975+ ! Current states are in time level 1. Upon exit, time level 2 will contain updated states.
2976+ call atm_do_timestep(self % domain_ptr, config_dt, self % number_of_time_steps)
2977+
2978+ ! MPAS `state` pool has two time levels.
2979+ ! Swap them after advancing a time step.
2980+ call mpas_pool_shift_time_levels(mpas_pool_state)
2981+
2982+ call mpas_advance_clock(self % domain_ptr % clock, ierr= ierr)
2983+
2984+ if (ierr /= 0 ) then
2985+ call self % model_error(' Failed to advance clock' , subname, __LINE__)
2986+ end if
2987+
2988+ mpas_time_now = mpas_get_clock_time(self % domain_ptr % clock, mpas_now, ierr= ierr)
2989+
2990+ if (ierr /= 0 ) then
2991+ call self % model_error(' Failed to get time for "mpas_now"' , subname, __LINE__)
2992+ end if
2993+
2994+ call self % debug_print(' Time step ' // stringify([self % number_of_time_steps]) // ' completed' )
2995+ end do
2996+
2997+ call mpas_get_time(mpas_time_now, datetimestring= date_time, ierr= ierr)
2998+
2999+ if (ierr /= 0 ) then
3000+ call self % model_error(' Failed to get time for "mpas_now"' , subname, __LINE__)
3001+ end if
3002+
3003+ call self % debug_print(' Time integration of MPAS dynamical core ends at ' // trim (adjustl (date_time)))
3004+
3005+ ! Compute diagnostic variables like `pressure`, `rho` and `theta` from time level 1 of MPAS `state` pool
3006+ ! by calling upstream MPAS functionality.
3007+ call atm_compute_output_diagnostics(mpas_pool_state, 1 , mpas_pool_diag, mpas_pool_mesh)
3008+
3009+ nullify(config_dt)
3010+ nullify(mpas_pool_diag, mpas_pool_mesh, mpas_pool_state)
3011+
3012+ call self % debug_print(subname // ' completed' )
3013+ end subroutine dyn_mpas_run
3014+
28703015 !- ------------------------------------------------------------------------------
28713016 ! function dyn_mpas_get_constituent_name
28723017 !
@@ -3148,7 +3293,7 @@ subroutine dyn_mpas_get_pool_pointer(self, pool_pointer, pool_name)
31483293 pool_pointer = > self % domain_ptr % configs
31493294 case (' dim' )
31503295 pool_pointer = > self % domain_ptr % blocklist % dimensions
3151- case (' diag' , ' mesh' , ' state' , ' tend' )
3296+ case (' diag' , ' mesh' , ' state' , ' tend' , ' tend_physics ' )
31523297 call mpas_pool_get_subpool(self % domain_ptr % blocklist % allstructs, trim (adjustl (pool_name)), pool_pointer)
31533298 case default
31543299 call self % model_error(' Unsupported pool name "' // trim (adjustl (pool_name)) // ' "' , subname, __LINE__)
0 commit comments