From 97c9aebd77ebc69e947027fa89827bab9f65fe11 Mon Sep 17 00:00:00 2001 From: Theresa Morrison Date: Wed, 23 Oct 2024 16:15:17 -0400 Subject: [PATCH 1/4] Add option to set tracer advection timestep Add an option to seperate the tracer advection from the diabatic processes in MOM_step_thermo. Previously the advection and diabatic codes were seperated but called with the same timestep. If DT_TADVECT is not specified, then DT_THERM is used. The comments describing DT_THERM and DT_TADVECT have been modified to refelect this change. --- src/core/MOM.F90 | 62 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index b696213e1b..98eb6a22c6 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -274,9 +274,11 @@ module MOM type(time_type), pointer :: Time !< pointer to the ocean clock real :: dt !< (baroclinic) dynamics time step [T ~> s] - real :: dt_therm !< thermodynamics time step [T ~> s] + real :: dt_therm !< diabatic time step [T ~> s] + real :: dt_tadvect !< tracer advection time step [T ~> s] logical :: thermo_spans_coupling !< If true, thermodynamic and tracer time !! steps can span multiple coupled time steps. + logical :: tadvect_spans_coupling !< If true, thermodynamic and tracer time integer :: nstep_tot = 0 !< The total number of dynamic timesteps taken !! so far in this run segment logical :: count_calls = .false. !< If true, count the calls to step_MOM, rather than the @@ -533,7 +535,8 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS type(verticalGrid_type), pointer :: GV => NULL() ! Pointer to the vertical grid structure type(unit_scale_type), pointer :: US => NULL() ! Pointer to a structure containing ! various unit conversion factors - integer :: ntstep ! time steps between tracer updates or diabatic forcing + integer :: ntstep ! number of time steps between diabatic forcing updates + integer :: ntastep ! number of time steps between tracer advection updates integer :: n_max ! number of steps to take in this call integer :: halo_sz, dynamics_stencil @@ -543,6 +546,7 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS real :: time_interval ! time interval covered by this run segment [T ~> s]. real :: dt ! baroclinic time step [T ~> s] real :: dtdia ! time step for diabatic processes [T ~> s] + real :: dt_tadvect ! time step for tracer advection [T ~> s] real :: dt_therm ! a limited and quantized version of CS%dt_therm [T ~> s] real :: dt_therm_here ! a further limited value of dt_therm [T ~> s] @@ -553,9 +557,12 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS ! if it is not to be calculated anew [T ~> s]. real :: rel_time = 0.0 ! relative time since start of this call [T ~> s]. - logical :: do_advection ! If true, it is time to advect tracers. - logical :: thermo_does_span_coupling ! If true, thermodynamic forcing spans - ! multiple dynamic timesteps. + logical :: do_advection ! If true, do tracer advection. + logical :: do_diabatic ! If true, do diabatic update. + logical :: thermo_does_span_coupling ! If true,thermodynamic (diabatic) forcing spans + ! multiple coupling timesteps. + logical :: tadvect_does_span_coupling ! If true, tracer advection spans + ! multiple coupling timesteps. logical :: do_dyn ! If true, dynamics are updated with this call. logical :: do_thermo ! If true, thermodynamics and remapping may be applied with this call. logical :: debug_redundant ! If true, check redundant values on PE boundaries when debugging. @@ -661,6 +668,8 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS dt = time_interval / real(n_max) thermo_does_span_coupling = (CS%thermo_spans_coupling .and. & (CS%dt_therm > 1.5*cycle_time)) + tadvect_does_span_coupling = (CS%tadvect_spans_coupling .and. & + (CS%dt_tadvect > 1.5*cycle_time)) if (thermo_does_span_coupling) then ! Set dt_therm to be an integer multiple of the coupling time step. dt_therm = cycle_time * floor(CS%dt_therm / cycle_time + 0.001) @@ -673,6 +682,18 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS ntstep = MAX(1, MIN(n_max, floor(CS%dt_therm/dt + 0.001))) dt_therm = dt*ntstep endif + if (tadvect_does_span_coupling) then + ! Set dt_tadvect to be an integer multiple of the coupling time step. + dt_tadvect = cycle_time * floor(CS%dt_tadvect / cycle_time + 0.001) + ntastep = floor(dt_tadvect/dt + 0.001) + elseif (.not.do_thermo) then + dt_tadvect = CS%dt_tadvect + if (present(cycle_length)) dt_tadvect = min(CS%dt_tadvect, cycle_length) + ! ntstep is not used. + else + ntastep = MAX(1, MIN(n_max, floor(CS%dt_tadvect/dt + 0.001))) + dt_tadvect = dt*ntastep + endif !---------- Initiate group halo pass of the forcing fields call cpu_clock_begin(id_clock_pass) @@ -923,10 +944,10 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS !=========================================================================== ! This is the start of the tracer advection part of the algorithm. - if (thermo_does_span_coupling .or. .not.do_thermo) then - do_advection = (CS%t_dyn_rel_adv + 0.5*dt > dt_therm) + if (tadvect_does_span_coupling .or. .not.do_thermo) then + do_advection = (CS%t_dyn_rel_adv + 0.5*dt > dt_tadvect) else - do_advection = ((MOD(n,ntstep) == 0) .or. (n==n_max)) + do_advection = ((MOD(n,ntastep) == 0) .or. (n==n_max)) endif if (do_advection) then ! Do advective transport and lateral tracer mixing. @@ -939,7 +960,12 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS !=========================================================================== ! This is the second place where the diabatic processes and remapping could occur. - if ((CS%t_dyn_rel_adv==0.0) .and. do_thermo .and. (.not.CS%diabatic_first)) then + if (thermo_does_span_coupling .or. .not.do_dyn) then + do_diabatic = (CS%t_dyn_rel_thermo + 0.5*dt > dt_therm) + else + do_diabatic = ((MOD(n,ntstep) == 0) .or. (n==n_max)) + endif + if ((CS%t_dyn_rel_adv==0.0) .and. do_thermo .and. (.not.CS%diabatic_first) .and. do_diabatic) then dtdia = CS%t_dyn_rel_thermo ! If the MOM6 dynamic and thermodynamic time stepping is being orchestrated @@ -2335,7 +2361,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "coupling timestep in coupled mode.)", units="s", scale=US%s_to_T, & fail_if_missing=.true.) call get_param(param_file, "MOM", "DT_THERM", CS%dt_therm, & - "The thermodynamic and tracer advection time step. "//& + "The thermodynamic time step. "//& "Ideally DT_THERM should be an integer multiple of DT "//& "and less than the forcing or coupling time-step, unless "//& "THERMO_SPANS_COUPLING is true, in which case DT_THERM "//& @@ -2343,11 +2369,25 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "default DT_THERM is set to DT.", & units="s", scale=US%s_to_T, default=US%T_to_s*CS%dt) call get_param(param_file, "MOM", "THERMO_SPANS_COUPLING", CS%thermo_spans_coupling, & - "If true, the MOM will take thermodynamic and tracer "//& + "If true, the MOM will take thermodynamic "//& "timesteps that can be longer than the coupling timestep. "//& "The actual thermodynamic timestep that is used in this "//& "case is the largest integer multiple of the coupling "//& "timestep that is less than or equal to DT_THERM.", default=.false.) + call get_param(param_file, "MOM", "DT_TADVECT", CS%dt_tadvect, & + "The tracer advection time step. "//& + "Ideally DT_TADVECT should be an integer multiple of DT "//& + "and less than the forcing or coupling time-step, unless "//& + "TADVECT_SPANS_COUPLING is true, in which case DT_TADVECT "//& + "can be an integer multiple of the coupling timestep. By "//& + "default DT_TADVECT is set to DT_THERM.", & + units="s", scale=US%s_to_T, default=US%T_to_s*CS%dt_therm) + call get_param(param_file, "MOM", "TADVECT_SPANS_COUPLING", CS%tadvect_spans_coupling, & + "If true, the MOM will take tracer advection "//& + "timesteps that can be longer than the coupling timestep. "//& + "The actual tracer advection timestep that is used in this "//& + "case is the largest integer multiple of the coupling "//& + "timestep that is less than or equal to DT_TADVECT.", default=.false.) if (bulkmixedlayer) then CS%Hmix = -1.0 ; CS%Hmix_UV = -1.0 From e13b134ddbb93f1b9dafced67c53ad62747ff6f9 Mon Sep 17 00:00:00 2001 From: Theresa Morrison Date: Wed, 20 Nov 2024 14:24:11 -0500 Subject: [PATCH 2/4] Add error warning if diabatic_first is true Currently, this option is not intended to be used if diabatic_first is true, so a fatal error flag has been added. This will be addressed in a future pull request. --- src/core/MOM.F90 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 98eb6a22c6..a47b55e29d 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -2388,6 +2388,9 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "The actual tracer advection timestep that is used in this "//& "case is the largest integer multiple of the coupling "//& "timestep that is less than or equal to DT_TADVECT.", default=.false.) + if ( CS%diabatic_first .and. (CS%dt_tadvect /= CS%dt_therm) ) then + call MOM_error(FATAL,"MOM: If using DIABATIC_FIRST, DT_TADVECT must equal DT_THERM.") + endif if (bulkmixedlayer) then CS%Hmix = -1.0 ; CS%Hmix_UV = -1.0 From 9bd534007b35caaa029fd1215546441984434297 Mon Sep 17 00:00:00 2001 From: Theresa Morrison Date: Wed, 27 Nov 2024 09:41:37 -0500 Subject: [PATCH 3/4] Change variable, add do advection before thermo step Change DT_TADVECT to DT_TRACER_ADVECT to be clearer. dt_tadvect has been changed to dt_tr_adv and tadvect_spans_coupling to tradv_spans_coupling. An additional check has been added before the advection step. If thermo_spans_coupling is true, do_advection is true if t_dyn_rel_thermo is ~DT_THERM so that the thermodynamics step would happen at the next possible time. This ensures that the thermodynamics step is not prevented because advection has not been resolved. --- src/core/MOM.F90 | 65 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index a47b55e29d..ca5f1316c1 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -275,10 +275,10 @@ module MOM type(time_type), pointer :: Time !< pointer to the ocean clock real :: dt !< (baroclinic) dynamics time step [T ~> s] real :: dt_therm !< diabatic time step [T ~> s] - real :: dt_tadvect !< tracer advection time step [T ~> s] + real :: dt_tr_adv !< tracer advection time step [T ~> s] logical :: thermo_spans_coupling !< If true, thermodynamic and tracer time !! steps can span multiple coupled time steps. - logical :: tadvect_spans_coupling !< If true, thermodynamic and tracer time + logical :: tradv_spans_coupling !< If true, thermodynamic and tracer time integer :: nstep_tot = 0 !< The total number of dynamic timesteps taken !! so far in this run segment logical :: count_calls = .false. !< If true, count the calls to step_MOM, rather than the @@ -546,7 +546,7 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS real :: time_interval ! time interval covered by this run segment [T ~> s]. real :: dt ! baroclinic time step [T ~> s] real :: dtdia ! time step for diabatic processes [T ~> s] - real :: dt_tadvect ! time step for tracer advection [T ~> s] + real :: dt_tr_adv ! time step for tracer advection [T ~> s] real :: dt_therm ! a limited and quantized version of CS%dt_therm [T ~> s] real :: dt_therm_here ! a further limited value of dt_therm [T ~> s] @@ -561,7 +561,7 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS logical :: do_diabatic ! If true, do diabatic update. logical :: thermo_does_span_coupling ! If true,thermodynamic (diabatic) forcing spans ! multiple coupling timesteps. - logical :: tadvect_does_span_coupling ! If true, tracer advection spans + logical :: tradv_does_span_coupling ! If true, tracer advection spans ! multiple coupling timesteps. logical :: do_dyn ! If true, dynamics are updated with this call. logical :: do_thermo ! If true, thermodynamics and remapping may be applied with this call. @@ -668,8 +668,8 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS dt = time_interval / real(n_max) thermo_does_span_coupling = (CS%thermo_spans_coupling .and. & (CS%dt_therm > 1.5*cycle_time)) - tadvect_does_span_coupling = (CS%tadvect_spans_coupling .and. & - (CS%dt_tadvect > 1.5*cycle_time)) + tradv_does_span_coupling = (CS%tradv_spans_coupling .and. & + (CS%dt_tr_adv > 1.5*cycle_time)) if (thermo_does_span_coupling) then ! Set dt_therm to be an integer multiple of the coupling time step. dt_therm = cycle_time * floor(CS%dt_therm / cycle_time + 0.001) @@ -682,17 +682,17 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS ntstep = MAX(1, MIN(n_max, floor(CS%dt_therm/dt + 0.001))) dt_therm = dt*ntstep endif - if (tadvect_does_span_coupling) then - ! Set dt_tadvect to be an integer multiple of the coupling time step. - dt_tadvect = cycle_time * floor(CS%dt_tadvect / cycle_time + 0.001) - ntastep = floor(dt_tadvect/dt + 0.001) + if (tradv_does_span_coupling) then + ! Set dt_tr_adv to be an integer multiple of the coupling time step. + dt_tr_adv = cycle_time * floor(CS%dt_tr_adv / cycle_time + 0.001) + ntastep = floor(dt_tr_adv/dt + 0.001) elseif (.not.do_thermo) then - dt_tadvect = CS%dt_tadvect - if (present(cycle_length)) dt_tadvect = min(CS%dt_tadvect, cycle_length) + dt_tr_adv = CS%dt_tr_adv + if (present(cycle_length)) dt_tr_adv = min(CS%dt_tr_adv, cycle_length) ! ntstep is not used. else - ntastep = MAX(1, MIN(n_max, floor(CS%dt_tadvect/dt + 0.001))) - dt_tadvect = dt*ntastep + ntastep = MAX(1, MIN(n_max, floor(CS%dt_tr_adv/dt + 0.001))) + dt_tr_adv = dt*ntastep endif !---------- Initiate group halo pass of the forcing fields @@ -944,8 +944,9 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS !=========================================================================== ! This is the start of the tracer advection part of the algorithm. - if (tadvect_does_span_coupling .or. .not.do_thermo) then - do_advection = (CS%t_dyn_rel_adv + 0.5*dt > dt_tadvect) + if (tradv_does_span_coupling .or. .not.do_thermo) then + do_advection = (CS%t_dyn_rel_adv + 0.5*dt > dt_tr_adv) + if (CS%t_dyn_rel_thermo + 0.5*dt > dt_therm) do_advection = .true. else do_advection = ((MOD(n,ntastep) == 0) .or. (n==n_max)) endif @@ -2361,12 +2362,11 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "coupling timestep in coupled mode.)", units="s", scale=US%s_to_T, & fail_if_missing=.true.) call get_param(param_file, "MOM", "DT_THERM", CS%dt_therm, & - "The thermodynamic time step. "//& - "Ideally DT_THERM should be an integer multiple of DT "//& - "and less than the forcing or coupling time-step, unless "//& - "THERMO_SPANS_COUPLING is true, in which case DT_THERM "//& - "can be an integer multiple of the coupling timestep. By "//& - "default DT_THERM is set to DT.", & + "The thermodynamic time step. Ideally DT_THERM should be an "//& + "integer multiple of DT and of DT_TRACER_ADVECT "//& + "and less than the forcing or coupling time-step. However, if "//& + "THERMO_SPANS_COUPLING is true, DT_THERM can be an integer multiple "//& + "of the coupling timestep. By default DT_THERM is set to DT.", & units="s", scale=US%s_to_T, default=US%T_to_s*CS%dt) call get_param(param_file, "MOM", "THERMO_SPANS_COUPLING", CS%thermo_spans_coupling, & "If true, the MOM will take thermodynamic "//& @@ -2374,22 +2374,21 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "The actual thermodynamic timestep that is used in this "//& "case is the largest integer multiple of the coupling "//& "timestep that is less than or equal to DT_THERM.", default=.false.) - call get_param(param_file, "MOM", "DT_TADVECT", CS%dt_tadvect, & - "The tracer advection time step. "//& - "Ideally DT_TADVECT should be an integer multiple of DT "//& - "and less than the forcing or coupling time-step, unless "//& - "TADVECT_SPANS_COUPLING is true, in which case DT_TADVECT "//& - "can be an integer multiple of the coupling timestep. By "//& - "default DT_TADVECT is set to DT_THERM.", & + call get_param(param_file, "MOM", "DT_TRACER_ADVECT", CS%dt_tr_adv, & + "The tracer advection time step. Ideally DT_TRACER_ADVECT should be an "//& + "integer multiple of DT, less than DT_THERM, and less than the forcing "//& + "or coupling time-step. However, if TRADV_SPANS_COUPLING is true, "//& + "DT_TRACER_ADVECT can be longer than the coupling timestep. By "//& + "default DT_TRACER_ADVECT is set to DT_THERM.", & units="s", scale=US%s_to_T, default=US%T_to_s*CS%dt_therm) - call get_param(param_file, "MOM", "TADVECT_SPANS_COUPLING", CS%tadvect_spans_coupling, & + call get_param(param_file, "MOM", "TRADV_SPANS_COUPLING", CS%tradv_spans_coupling, & "If true, the MOM will take tracer advection "//& "timesteps that can be longer than the coupling timestep. "//& "The actual tracer advection timestep that is used in this "//& "case is the largest integer multiple of the coupling "//& - "timestep that is less than or equal to DT_TADVECT.", default=.false.) - if ( CS%diabatic_first .and. (CS%dt_tadvect /= CS%dt_therm) ) then - call MOM_error(FATAL,"MOM: If using DIABATIC_FIRST, DT_TADVECT must equal DT_THERM.") + "timestep that is less than or equal to DT_TRACER_ADVECT.", default=.false.) + if ( CS%diabatic_first .and. (CS%dt_tr_adv /= CS%dt_therm) ) then + call MOM_error(FATAL,"MOM: If using DIABATIC_FIRST, DT_TRACER_ADVECT must equal DT_THERM.") endif if (bulkmixedlayer) then From a31efd1f2a46f26b52f4437093e04b491d5a5828 Mon Sep 17 00:00:00 2001 From: Theresa Morrison Date: Tue, 3 Dec 2024 11:35:08 -0500 Subject: [PATCH 4/4] Add logic to do_diabatic check The do_diabatic check should only be done if do_thermo is true. This is relevant when do_dyn or do_thermo are being used from outside step_MOM to order the updates of the thermodynamics and dynamics, such as when the interspesed coupling is used. --- src/core/MOM.F90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 370a59c3c7..0d6282e26a 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -962,10 +962,12 @@ subroutine step_MOM(forces_in, fluxes_in, sfc_state, Time_start, time_int_in, CS !=========================================================================== ! This is the second place where the diabatic processes and remapping could occur. - if (thermo_does_span_coupling .or. .not.do_dyn) then - do_diabatic = (CS%t_dyn_rel_thermo + 0.5*dt > dt_therm) - else - do_diabatic = ((MOD(n,ntstep) == 0) .or. (n==n_max)) + if (do_thermo) then + if (thermo_does_span_coupling .or. .not.do_dyn) then + do_diabatic = (CS%t_dyn_rel_thermo + 0.5*dt > dt_therm) + else + do_diabatic = ((MOD(n,ntstep) == 0) .or. (n==n_max)) + endif endif if ((CS%t_dyn_rel_adv==0.0) .and. do_thermo .and. (.not.CS%diabatic_first) .and. do_diabatic) then