@@ -21,7 +21,7 @@ module dyn_mpas_subdriver
21
21
pio_inq_varid, pio_inq_varndims, pio_inq_vartype, pio_noerr
22
22
23
23
! 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
25
25
use atm_core_interface, only: atm_setup_core, atm_setup_domain
26
26
use atm_time_integration, only: mpas_atm_dynamics_init
27
27
use mpas_atm_dimensions, only: mpas_atm_set_dims
@@ -34,14 +34,15 @@ module dyn_mpas_subdriver
34
34
mpas_pool_type, mpas_pool_field_info_type, &
35
35
mpas_pool_character, mpas_pool_real, mpas_pool_integer, &
36
36
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, &
38
38
mpas_io_native_precision, mpas_io_pnetcdf, mpas_io_read, mpas_io_write, &
39
39
field0dchar, field1dchar, &
40
40
field0dinteger, field1dinteger, field2dinteger, field3dinteger, &
41
41
field0dreal, field1dreal, field2dreal, field3dreal, field4dreal, field5dreal
42
42
use mpas_dmpar, only: mpas_dmpar_exch_halo_field, &
43
43
mpas_dmpar_max_int, mpas_dmpar_sum_int
44
44
use mpas_domain_routines, only: mpas_allocate_domain
45
+ use mpas_field_routines, only: mpas_allocate_scratch_field
45
46
use mpas_framework, only: mpas_framework_init_phase1, mpas_framework_init_phase2
46
47
use mpas_io_streams, only: mpas_createstream, mpas_closestream, mpas_streamaddfield, &
47
48
mpas_readstream, mpas_writestream, mpas_writestreamatt
@@ -51,11 +52,13 @@ module dyn_mpas_subdriver
51
52
mpas_pool_get_array, &
52
53
mpas_pool_add_dimension, mpas_pool_get_dimension, &
53
54
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
55
56
use mpas_stream_inquiry, only: mpas_stream_inquiry_new_streaminfo
56
57
use mpas_stream_manager, only: postread_reindex, prewrite_reindex, postwrite_reindex
57
58
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 (<)
59
62
use mpas_vector_operations, only: mpas_initialize_vectors
60
63
61
64
implicit none
@@ -106,7 +109,8 @@ end subroutine model_error_if
106
109
logical , allocatable :: is_water_species(:)
107
110
108
111
! Initialized by `dyn_mpas_init_phase4`.
109
- integer :: coupling_time_interval
112
+ integer :: coupling_time_interval = 0
113
+ integer :: number_of_time_steps = 0
110
114
contains
111
115
private
112
116
@@ -123,6 +127,7 @@ end subroutine model_error_if
123
127
procedure , pass, public :: compute_unit_vector = > dyn_mpas_compute_unit_vector
124
128
procedure , pass, public :: compute_edge_wind = > dyn_mpas_compute_edge_wind
125
129
procedure , pass, public :: init_phase4 = > dyn_mpas_init_phase4
130
+ procedure , pass, public :: run = > dyn_mpas_run
126
131
127
132
! Accessor subroutines for users to access internal states of MPAS dynamical core.
128
133
@@ -2525,19 +2530,21 @@ end subroutine dyn_mpas_compute_unit_vector
2525
2530
! > \date 16 January 2020
2526
2531
! > \details
2527
2532
! > 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
2529
2534
! > (i.e., `uReconstruct{Zonal,Meridional}` in MPAS `diag` pool). In MPAS, the
2530
2535
! > former are PROGNOSTIC variables, while the latter are DIAGNOSTIC variables
2531
2536
! > that are "reconstructed" from the former. This subroutine is essentially the
2532
2537
! > inverse function of that reconstruction. The purpose is to provide an
2533
2538
! > 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.
2535
2541
! > \addenda
2536
2542
! > Ported and refactored for CAM-SIMA. (KCW, 2024-05-08)
2537
2543
!
2538
2544
!- ------------------------------------------------------------------------------
2539
- subroutine dyn_mpas_compute_edge_wind (self )
2545
+ subroutine dyn_mpas_compute_edge_wind (self , wind_tendency )
2540
2546
class(mpas_dynamical_core_type), intent (in ) :: self
2547
+ logical , intent (in ) :: wind_tendency
2541
2548
2542
2549
character (* ), parameter :: subname = ' dyn_mpas_subdriver::dyn_mpas_compute_edge_wind'
2543
2550
integer :: cell1, cell2, i
@@ -2561,22 +2568,36 @@ subroutine dyn_mpas_compute_edge_wind(self)
2561
2568
nullify(uedge)
2562
2569
2563
2570
! 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
2566
2578
2567
2579
! Input.
2568
2580
call self % get_variable_pointer(nedges, ' dim' , ' nEdges' )
2569
2581
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
2572
2589
2573
2590
call self % get_variable_pointer(cellsonedge, ' mesh' , ' cellsOnEdge' )
2574
2591
call self % get_variable_pointer(east, ' mesh' , ' east' )
2575
2592
call self % get_variable_pointer(north, ' mesh' , ' north' )
2576
2593
call self % get_variable_pointer(edgenormalvectors, ' mesh' , ' edgeNormalVectors' )
2577
2594
2578
2595
! 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
2580
2601
2581
2602
do i = 1 , nedges
2582
2603
cell1 = cellsonedge(1 , i)
@@ -2607,7 +2628,11 @@ subroutine dyn_mpas_compute_edge_wind(self)
2607
2628
nullify(uedge)
2608
2629
2609
2630
! 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
2611
2636
2612
2637
call self % debug_print(subname // ' completed' )
2613
2638
end subroutine dyn_mpas_compute_edge_wind
@@ -2640,6 +2665,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
2640
2665
logical , pointer :: config_do_restart
2641
2666
real (rkind), pointer :: config_dt
2642
2667
type (field0dreal), pointer :: field_0d_real
2668
+ type (field2dreal), pointer :: field_2d_real
2643
2669
type (mpas_pool_type), pointer :: mpas_pool
2644
2670
type (mpas_time_type) :: mpas_time
2645
2671
@@ -2651,6 +2677,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
2651
2677
nullify(config_do_restart)
2652
2678
nullify(config_dt)
2653
2679
nullify(field_0d_real)
2680
+ nullify(field_2d_real)
2654
2681
nullify(mpas_pool)
2655
2682
2656
2683
if (coupling_time_interval <= 0 ) then
@@ -2673,6 +2700,7 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
2673
2700
end if
2674
2701
2675
2702
self % coupling_time_interval = coupling_time_interval
2703
+ self % number_of_time_steps = 0
2676
2704
2677
2705
call self % debug_print(' Coupling time interval is ' // stringify([real (self % coupling_time_interval, rkind)]) // &
2678
2706
' seconds' )
@@ -2814,6 +2842,16 @@ subroutine dyn_mpas_init_phase4(self, coupling_time_interval)
2814
2842
! Prepare dynamics for time integration.
2815
2843
call mpas_atm_dynamics_init(self % domain_ptr)
2816
2844
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
+
2817
2855
call self % debug_print(subname // ' completed' )
2818
2856
2819
2857
call self % debug_print(' Successful initialization of MPAS dynamical core' )
@@ -2867,6 +2905,113 @@ pure function almost_equal(a, b, absolute_tolerance, relative_tolerance)
2867
2905
end function almost_equal
2868
2906
end subroutine dyn_mpas_init_phase4
2869
2907
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
+
2870
3015
!- ------------------------------------------------------------------------------
2871
3016
! function dyn_mpas_get_constituent_name
2872
3017
!
@@ -3148,7 +3293,7 @@ subroutine dyn_mpas_get_pool_pointer(self, pool_pointer, pool_name)
3148
3293
pool_pointer = > self % domain_ptr % configs
3149
3294
case (' dim' )
3150
3295
pool_pointer = > self % domain_ptr % blocklist % dimensions
3151
- case (' diag' , ' mesh' , ' state' , ' tend' )
3296
+ case (' diag' , ' mesh' , ' state' , ' tend' , ' tend_physics ' )
3152
3297
call mpas_pool_get_subpool(self % domain_ptr % blocklist % allstructs, trim (adjustl (pool_name)), pool_pointer)
3153
3298
case default
3154
3299
call self % model_error(' Unsupported pool name "' // trim (adjustl (pool_name)) // ' "' , subname, __LINE__)
0 commit comments