Skip to content

Commit

Permalink
+Rescaled time variables in all MOM6 drivers
Browse files Browse the repository at this point in the history
  Dimensionally rescaled the internal variables with units of time in all of the
top-level driver codes, from units of [s] to [T ~> s], including thing
valid_time arguments to the various drivers' versions of convert_IOB_to_fluxes.
All answers are bitwise identical in all cases that have been tested, although
it should be noted that the mct_cap and nuopc_cap are not regularly tested as
a part of the GFDL MOM6-examples test suite.  These changes only apply to
dimensional rescaling of timestep variables and the documentation of units.
  • Loading branch information
Hallberg-NOAA committed Nov 30, 2022
1 parent 7055b7c commit 8128448
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 164 deletions.
8 changes: 4 additions & 4 deletions config_src/drivers/FMS_cap/MOM_surface_forcing_gfdl.F90
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
type(time_type), intent(in) :: Time !< The time of the fluxes, used for interpolating the
!! salinity to the right time, when it is being restored.
real, intent(in) :: valid_time !< The amount of time over which these fluxes
!! should be applied [s].
!! should be applied [T ~> s].
type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure
type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type
type(surface_forcing_CS),pointer :: CS !< A pointer to the control structure returned by a
Expand Down Expand Up @@ -333,7 +333,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,

! Indicate that there are new unused fluxes.
fluxes%fluxes_used = .false.
fluxes%dt_buoy_accum = US%s_to_T*valid_time
fluxes%dt_buoy_accum = valid_time

fluxes%heat_added(:,:) = 0.0
fluxes%salt_flux_added(:,:) = 0.0
Expand Down Expand Up @@ -581,7 +581,7 @@ subroutine convert_IOB_to_fluxes(IOB, fluxes, index_bounds, Time, valid_time, G,
!#CTRL# SSS_mean(i,j) = 0.5*(sfc_state%SSS(i,j) + CS%S_Restore(i,j))
!#CTRL# enddo ; enddo
!#CTRL# call apply_ctrl_forcing(SST_anom, SSS_anom, SSS_mean, fluxes%heat_added, &
!#CTRL# fluxes%vprec, day, US%s_to_T*valid_time, G, US, CS%ctrl_forcing_CSp)
!#CTRL# fluxes%vprec, day, valid_time, G, US, CS%ctrl_forcing_CSp)
!#CTRL# endif

! adjust the NET fresh-water flux to zero, if flagged
Expand Down Expand Up @@ -663,7 +663,7 @@ subroutine convert_IOB_to_forces(IOB, forces, index_bounds, Time, G, US, CS, dt_
!! previous call to surface_forcing_init.
real, optional, intent(in) :: dt_forcing !< A time interval over which to apply the
!! current value of ustar as a weighted running
!! average [s], or if 0 do not average ustar.
!! average [T ~> s], or if 0 do not average ustar.
!! Missing is equivalent to 0.
logical, optional, intent(in) :: reset_avg !< If true, reset the time average.

Expand Down
67 changes: 34 additions & 33 deletions config_src/drivers/FMS_cap/ocean_model_MOM.F90
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module ocean_model_mod
use MOM_coupler_types, only : coupler_type_spawn, coupler_type_write_chksums
use MOM_coupler_types, only : coupler_type_initialized, coupler_type_copy_data
use MOM_coupler_types, only : coupler_type_set_diags, coupler_type_send_data
use MOM_diag_mediator, only : diag_ctrl, enable_averaging, disable_averaging
use MOM_diag_mediator, only : diag_ctrl, enable_averages, disable_averaging
use MOM_diag_mediator, only : diag_mediator_close_registration, diag_mediator_end
use MOM_domains, only : MOM_domain_type, domain2d, clone_MOM_domain, get_domain_extent
use MOM_domains, only : pass_var, pass_vector, AGRID, BGRID_NE, CGRID_NE, TO_ALL, Omit_Corners
Expand Down Expand Up @@ -171,8 +171,8 @@ module ocean_model_mod
!! If false, the two phases are advanced with
!! separate calls. The default is true.
! The following 3 variables are only used here if single_step_call is false.
real :: dt !< (baroclinic) dynamics time step [s]
real :: dt_therm !< thermodynamics time step [s]
real :: dt !< (baroclinic) dynamics time step [T ~> s]
real :: dt_therm !< thermodynamics time step [T ~> s]
logical :: thermo_spans_coupling !< If true, thermodynamic and tracer time
!! steps can span multiple coupled time steps.
logical :: diabatic_first !< If true, apply diabatic and thermodynamic
Expand Down Expand Up @@ -293,16 +293,17 @@ subroutine ocean_model_init(Ocean_sfc, OS, Time_init, Time_in, wind_stagger, gas
"including both dynamics and thermodynamics. If false, "//&
"the two phases are advanced with separate calls.", default=.true.)
call get_param(param_file, mdl, "DT", OS%dt, &
"The (baroclinic) dynamics time step. The time-step that "//&
"is actually used will be an integer fraction of the "//&
"forcing time-step.", units="s", fail_if_missing=.true.)
"The (baroclinic) dynamics time step. The time-step that is actually "//&
"used will be an integer fraction of the forcing time-step.", &
units="s", scale=OS%US%s_to_T, fail_if_missing=.true.)
call get_param(param_file, mdl, "DT_THERM", OS%dt_therm, &
"The thermodynamic and tracer advection 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.", units="s", default=OS%dt)
"default DT_THERM is set to DT.", &
units="s", scale=OS%US%s_to_T, default=OS%US%T_to_s*OS%dt)
call get_param(param_file, "MOM", "THERMO_SPANS_COUPLING", OS%thermo_spans_coupling, &
"If true, the MOM will take thermodynamic and tracer "//&
"timesteps that can be longer than the coupling timestep. "//&
Expand Down Expand Up @@ -462,11 +463,11 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
type(time_type) :: Time1 ! The value of the ocean model's time at the start of a call to step_MOM.
integer :: index_bnds(4) ! The computational domain index bounds in the ice-ocean boundary type.
real :: weight ! Flux accumulation weight of the current fluxes.
real :: dt_coupling ! The coupling time step [s].
real :: dt_therm ! A limited and quantized version of OS%dt_therm [s].
real :: dt_dyn ! The dynamics time step [s].
real :: dtdia ! The diabatic time step [s].
real :: t_elapsed_seg ! The elapsed time in this update segment [s].
real :: dt_coupling ! The coupling time step [T ~> s].
real :: dt_therm ! A limited and quantized version of OS%dt_therm [T ~> s].
real :: dt_dyn ! The dynamics time step [T ~> s].
real :: dtdia ! The diabatic time step [T ~> s].
real :: t_elapsed_seg ! The elapsed time in this update segment [T ~> s].
integer :: n ! The internal iteration counter.
integer :: nts ! The number of baroclinic dynamics time steps in a thermodynamic step.
integer :: n_max ! The number of calls to step_MOM dynamics in this call to update_ocean_model.
Expand All @@ -478,7 +479,7 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
integer :: is, ie, js, je

call callTree_enter("update_ocean_model(), ocean_model_MOM.F90")
dt_coupling = time_type_to_real(Ocean_coupling_time_step)
dt_coupling = OS%US%s_to_T*time_type_to_real(Ocean_coupling_time_step)

if (.not.associated(OS)) then
call MOM_error(FATAL, "update_ocean_model called with an unassociated "// &
Expand Down Expand Up @@ -518,7 +519,7 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
call add_shelf_forces(OS%grid, OS%US, OS%Ice_shelf_CSp, OS%forces)
if (OS%icebergs_alter_ocean) &
call iceberg_forces(OS%grid, OS%forces, OS%use_ice_shelf, &
OS%sfc_state, dt_coupling, OS%marine_ice_CSp)
OS%sfc_state, OS%US%T_to_s*dt_coupling, OS%marine_ice_CSp)
endif

if (do_thermo) then
Expand All @@ -528,13 +529,13 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda

! Add ice shelf fluxes
if (OS%use_ice_shelf) &
call shelf_calc_flux(OS%sfc_state, OS%fluxes, OS%Time, dt_coupling, OS%Ice_shelf_CSp)
call shelf_calc_flux(OS%sfc_state, OS%fluxes, OS%Time, OS%US%T_to_s*dt_coupling, OS%Ice_shelf_CSp)
if (OS%icebergs_alter_ocean) &
call iceberg_fluxes(OS%grid, OS%US, OS%fluxes, OS%use_ice_shelf, &
OS%sfc_state, dt_coupling, OS%marine_ice_CSp)
OS%sfc_state, OS%US%T_to_s*dt_coupling, OS%marine_ice_CSp)

#ifdef _USE_GENERIC_TRACER
call enable_averaging(dt_coupling, OS%Time + Ocean_coupling_time_step, OS%diag) !Is this needed?
call enable_averages(dt_coupling, OS%Time + Ocean_coupling_time_step, OS%diag) !Is this needed?
call MOM_generic_tracer_fluxes_accumulate(OS%fluxes, 1.0) ! Here weight=1, so just store the current fluxes
call disable_averaging(OS%diag)
#endif
Expand All @@ -546,10 +547,10 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
OS%grid, OS%US, OS%forcing_CSp, OS%sfc_state)

if (OS%use_ice_shelf) &
call shelf_calc_flux(OS%sfc_state, OS%flux_tmp, OS%Time, dt_coupling, OS%Ice_shelf_CSp)
call shelf_calc_flux(OS%sfc_state, OS%flux_tmp, OS%Time, OS%US%T_to_s*dt_coupling, OS%Ice_shelf_CSp)
if (OS%icebergs_alter_ocean) &
call iceberg_fluxes(OS%grid, OS%US, OS%flux_tmp, OS%use_ice_shelf, &
OS%sfc_state, dt_coupling, OS%marine_ice_CSp)
OS%sfc_state, OS%US%T_to_s*dt_coupling, OS%marine_ice_CSp)

call fluxes_accumulate(OS%flux_tmp, OS%fluxes, OS%grid, weight)
#ifdef _USE_GENERIC_TRACER
Expand Down Expand Up @@ -579,15 +580,15 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
Time1 = Time_seg_start

if (OS%offline_tracer_mode .and. do_thermo) then
call step_offline(OS%forces, OS%fluxes, OS%sfc_state, Time1, dt_coupling, OS%MOM_CSp)
call step_offline(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dt_coupling, OS%MOM_CSp)
elseif ((.not.do_thermo) .or. (.not.do_dyn)) then
! The call sequence is being orchestrated from outside of update_ocean_model.
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dt_coupling, OS%MOM_CSp, &
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dt_coupling, OS%MOM_CSp, &
Waves=OS%Waves, do_dynamics=do_dyn, do_thermodynamics=do_thermo, &
start_cycle=start_cycle, end_cycle=end_cycle, cycle_length=cycle_length, &
reset_therm=Ocn_fluxes_used)
elseif (OS%single_step_call) then
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dt_coupling, OS%MOM_CSp, Waves=OS%Waves)
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dt_coupling, OS%MOM_CSp, Waves=OS%Waves)
else ! Step both the dynamics and thermodynamics with separate calls.
n_max = 1 ; if (dt_coupling > OS%dt) n_max = ceiling(dt_coupling/OS%dt - 0.001)
dt_dyn = dt_coupling / real(n_max)
Expand All @@ -609,18 +610,18 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
"THERMO_SPANS_COUPLING and DIABATIC_FIRST.")
if (modulo(n-1,nts)==0) then
dtdia = dt_dyn*min(nts,n_max-(n-1))
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dtdia, OS%MOM_CSp, &
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dtdia, OS%MOM_CSp, &
Waves=OS%Waves, do_dynamics=.false., do_thermodynamics=.true., &
start_cycle=(n==1), end_cycle=.false., cycle_length=dt_coupling)
start_cycle=(n==1), end_cycle=.false., cycle_length=OS%US%T_to_s*dt_coupling)
endif

call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dt_dyn, OS%MOM_CSp, &
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dt_dyn, OS%MOM_CSp, &
Waves=OS%Waves, do_dynamics=.true., do_thermodynamics=.false., &
start_cycle=.false., end_cycle=(n==n_max), cycle_length=dt_coupling)
start_cycle=.false., end_cycle=(n==n_max), cycle_length=OS%US%T_to_s*dt_coupling)
else
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dt_dyn, OS%MOM_CSp, &
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dt_dyn, OS%MOM_CSp, &
Waves=OS%Waves, do_dynamics=.true., do_thermodynamics=.false., &
start_cycle=(n==1), end_cycle=.false., cycle_length=dt_coupling)
start_cycle=(n==1), end_cycle=.false., cycle_length=OS%US%T_to_s*dt_coupling)

step_thermo = .false.
if (thermo_does_span_coupling) then
Expand All @@ -634,15 +635,15 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda

if (step_thermo) then
! Back up Time1 to the start of the thermodynamic segment.
Time1 = Time1 - real_to_time(dtdia - dt_dyn)
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, dtdia, OS%MOM_CSp, &
Time1 = Time1 - real_to_time(OS%US%T_to_s*(dtdia - dt_dyn))
call step_MOM(OS%forces, OS%fluxes, OS%sfc_state, Time1, OS%US%T_to_s*dtdia, OS%MOM_CSp, &
Waves=OS%Waves, do_dynamics=.false., do_thermodynamics=.true., &
start_cycle=.false., end_cycle=(n==n_max), cycle_length=dt_coupling)
start_cycle=.false., end_cycle=(n==n_max), cycle_length=OS%US%T_to_s*dt_coupling)
endif
endif

t_elapsed_seg = t_elapsed_seg + dt_dyn
Time1 = Time_seg_start + real_to_time(t_elapsed_seg)
Time1 = Time_seg_start + real_to_time(OS%US%T_to_s*t_elapsed_seg)
enddo
endif

Expand All @@ -653,7 +654,7 @@ subroutine update_ocean_model(Ice_ocean_boundary, OS, Ocean_sfc, time_start_upda
if (do_thermo) OS%nstep_thermo = OS%nstep_thermo + 1

if (do_dyn) then
call mech_forcing_diags(OS%forces, dt_coupling, OS%grid, OS%Time_dyn, OS%diag, OS%forcing_CSp%handles)
call mech_forcing_diags(OS%forces, OS%US%T_to_s*dt_coupling, OS%grid, OS%Time_dyn, OS%diag, OS%forcing_CSp%handles)
endif

if (OS%fluxes%fluxes_used .and. do_thermo) then
Expand Down
21 changes: 10 additions & 11 deletions config_src/drivers/ice_solo_driver/ice_shelf_driver.F90
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ program Shelf_main
use MOM_cpu_clock, only : CLOCK_COMPONENT
use MOM_debugging, only : MOM_debugging_init
use MOM_diag_mediator, only : diag_mediator_init, diag_mediator_infrastructure_init
use MOM_diag_mediator, only : enable_averaging, disable_averaging, diag_mediator_end
use MOM_diag_mediator, only : diag_ctrl, diag_mediator_close_registration
use MOM_diag_mediator, only : diag_mediator_end, diag_ctrl, diag_mediator_close_registration
use MOM_domains, only : MOM_infra_init, MOM_infra_end
use MOM_domains, only : MOM_domains_init, clone_MOM_domain, pass_var
use MOM_dyn_horgrid, only : dyn_horgrid_type, create_dyn_horgrid, destroy_dyn_horgrid
Expand Down Expand Up @@ -96,13 +95,13 @@ program Shelf_main
type(time_type) :: time_chg ! An amount of time to adjust the segment_start_time
! and elapsed time to avoid roundoff problems.

real :: elapsed_time = 0.0 ! Elapsed time in this run [s].
real :: elapsed_time = 0.0 ! Elapsed time in this run [T ~> s].

logical :: elapsed_time_master ! If true, elapsed time is used to set the
! model's master clock (Time). This is needed
! if Time_step_shelf is not an exact
! representation of time_step.
real :: time_step ! The time step [s]
real :: time_step ! The time step [T ~> s]

! A pointer to a structure containing metrics and related information.
type(ocean_grid_type), pointer :: ocn_grid
Expand Down Expand Up @@ -232,7 +231,7 @@ program Shelf_main
call get_param(param_file, mod_name, "ICE_VELOCITY_TIMESTEP", time_step, &
"The time step for changing forcing, coupling with other "//&
"components, or potentially writing certain diagnostics.", &
units="s", fail_if_missing=.true.)
units="s", scale=US%s_to_T, fail_if_missing=.true.)

if (sum(date) >= 0) then
! In this case, the segment starts at a time fixed by ocean_solo.res
Expand Down Expand Up @@ -282,8 +281,8 @@ program Shelf_main
segment_start_time = Time
elapsed_time = 0.0

Time_step_shelf = real_to_time(time_step)
elapsed_time_master = (abs(time_step - time_type_to_real(Time_step_shelf)) > 1.0e-12*time_step)
Time_step_shelf = real_to_time(US%T_to_s*time_step)
elapsed_time_master = (abs(time_step - US%s_to_T*time_type_to_real(Time_step_shelf)) > 1.0e-12*time_step)
if (elapsed_time_master) &
call MOM_mesg("Using real elapsed time for the master clock.", 2)

Expand Down Expand Up @@ -384,18 +383,18 @@ program Shelf_main
! Time = Time + Time_step_shelf
! This is here to enable fractional-second time steps.
elapsed_time = elapsed_time + time_step
if (elapsed_time > 2e9) then
if (elapsed_time > 2.0e9*US%s_to_T) then
! This is here to ensure that the conversion from a real to an integer can be accurately
! represented in long runs (longer than ~63 years). It will also ensure that elapsed time
! does not lose resolution of order the timetype's resolution, provided that the timestep and
! tick are larger than 10-5 seconds. If a clock with a finer resolution is used, a smaller
! value would be required.
time_chg = real_to_time(elapsed_time)
time_chg = real_to_time(US%T_to_s*elapsed_time)
segment_start_time = segment_start_time + time_chg
elapsed_time = elapsed_time - time_type_to_real(time_chg)
elapsed_time = elapsed_time - US%s_to_T*time_type_to_real(time_chg)
endif
if (elapsed_time_master) then
Master_Time = segment_start_time + real_to_time(elapsed_time)
Master_Time = segment_start_time + real_to_time(US%T_to_s*elapsed_time)
else
Master_Time = Master_Time + Time_step_shelf
endif
Expand Down
Loading

0 comments on commit 8128448

Please sign in to comment.