From b30d3d359e2403173527f0b5362b879f1f854fea Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 27 Apr 2026 12:03:37 -0400 Subject: [PATCH 1/7] Refactor MOM_initialize_OBCs * Rename MOM_initialize_OBCs to initialize_user_OBCs and narrow it to only set up OBC_USER_CONFIG. Its h, Time and restart_CS arguments are therefore dropped. * Faithfully move the remaining steps into the caller initialize_MOM. This includes the a few debug calls that have nothing to do with user OBC, a call to fill_thickness_segments, and most importantly, the OBC_RESERVOIR_INIT_BUG tracer reservoir setup (setup_OBC_tracer_reservoirs / set_initialized_OBC_tracer_reservoirs / fill_temp_salt_segments), which will undergo further refactor in the next few commits. * Reformat the parameter description for OBC_USER_CONFIG into an aligned list. --- src/core/MOM.F90 | 57 ++++++-- .../MOM_state_initialization.F90 | 134 ++++++------------ 2 files changed, 88 insertions(+), 103 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 74fc6f7ed0..b8f55e6e98 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -7,7 +7,7 @@ module MOM ! Infrastructure modules use MOM_array_transform, only : rotate_array, rotate_vector -use MOM_debugging, only : MOM_debugging_init, hchksum, uvchksum, totalTandS +use MOM_debugging, only : MOM_debugging_init, hchksum, uvchksum, qchksum, totalTandS use MOM_debugging, only : check_redundant, query_debugging_checks use MOM_checksum_packages, only : MOM_thermo_chksum, MOM_state_chksum use MOM_checksum_packages, only : MOM_accel_chksum, MOM_surface_chksum @@ -127,6 +127,9 @@ module MOM use MOM_open_boundary, only : rotate_OBC_config use MOM_open_boundary, only : open_boundary_halo_update, write_OBC_info, chksum_OBC_segments use MOM_open_boundary, only : segment_thickness_reservoir_init +use MOM_open_boundary, only : fill_temp_salt_segments, fill_thickness_segments +use MOM_open_boundary, only : set_initialized_OBC_tracer_reservoirs +use MOM_open_boundary, only : open_boundary_test_extern_h use MOM_open_boundary, only : copy_OBC_radiation_coefs use MOM_open_boundary, only : copy_OBC_tracer_reservoirs, copy_OBC_thickness_reservoirs use MOM_porous_barriers, only : porous_widths_layer, porous_widths_interface, porous_barriers_init @@ -136,7 +139,7 @@ module MOM use MOM_set_visc, only : set_visc_init, set_visc_end use MOM_shared_initialization, only : write_ocean_geometry_file use MOM_sponge, only : init_sponge_diags, sponge_CS -use MOM_state_initialization, only : MOM_initialize_state, MOM_initialize_OBCs +use MOM_state_initialization, only : MOM_initialize_state, initialize_user_OBCs use MOM_stoch_eos, only : MOM_stoch_eos_init, MOM_stoch_eos_run, MOM_stoch_eos_CS use MOM_stoch_eos, only : stoch_EOS_register_restarts, post_stoch_EOS_diags, mom_calc_varT use MOM_sum_output, only : write_energy, accumulate_net_input @@ -2412,6 +2415,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & logical :: calc_dtbt ! Indicates whether the dynamically adjusted barotropic ! time step needs to be updated before it is used. logical :: debug_truncations ! If true, turn on diagnostics useful for debugging truncations. + logical :: obc_debug_test ! If true, call open_boundary_test_extern_uv. integer :: first_direction ! An integer that indicates which direction is to be ! updated first in directionally split parts of the ! calculation. @@ -2424,6 +2428,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & logical :: semi_Boussinesq ! If true, this run is partially non-Boussinesq logical :: use_KPP ! If true, diabatic is using KPP vertical mixing logical :: MLE_use_PBL_MLD ! If true, use stored boundary layer depths for submesoscale restratification. + logical :: OBC_TS_reservoir_init_bug logical :: OBC_reservoir_init_bug logical :: OBC_bgc_time_ref_bug ! If true, use the start of the current run (not the overall ! start time) as the reference for OBC BGC tracer update schedule. @@ -2622,6 +2627,10 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call get_param(param_file, "MOM", "DEBUG_OBCS", CS%debug_OBCs, & "If true, write out verbose debugging data about OBCs.", & default=.false., debuggingParam=.true., do_not_log=(number_of_OBC_segments<=0)) + call get_param(param_file, "MOM", "OBC_DEBUGGING_TESTS", obc_debug_test, & + "If true, do additional calls resetting values to help verify the correctness "//& + "of the open boundary condition code.", default=.false., do_not_log=.true., & + old_name="DEBUG_OBC", debuggingParam=.true.) call get_param(param_file, "MOM", "ENABLE_BUGS_BY_DEFAULT", enable_bugs, & "If true, the defaults for certain recently added bug-fix flags are set to "//& "recreate the bugs so that the code can be moved forward without changing "//& @@ -2631,7 +2640,15 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & "means that bugs are only used if they are actively selected, but it also "//& "means that answers may change when code is updated due to newly found bugs.", & default=.true.) - + ! Log this parameter in MOM_initialize_state + call get_param(param_file, "MOM", "OBC_TS_RESERVOIR_INIT_BUG", OBC_TS_reservoir_init_bug, & + "If true, set the OBC temperature and salinity reservoirs at the startup of a "//& + "new run from initial values that are set before remapping.", default=enable_bugs, & + do_not_log=.true.) + call get_param(param_file, "MOM", "OBC_RESERVOIR_INIT_BUG", OBC_reservoir_init_bug, & + "If true, set the OBC tracer reservoirs at the startup of a new run from the "//& + "interior tracer concentrations regardless of properties that may be explicitly "//& + "specified for the reservoir concentrations.", default=enable_bugs) call get_param(param_file, "MOM", "DT", CS%dt, & "The (baroclinic) dynamics time step. The time-step that "//& "is actually used will be an integer fraction of the "//& @@ -3311,11 +3328,6 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & CS%tv%S => S_in if (associated(CS%OBC)) then - ! Log this parameter in MOM_initialize_state - call get_param(param_file, "MOM", "OBC_RESERVOIR_INIT_BUG", OBC_reservoir_init_bug, & - "If true, set the OBC tracer reservoirs at the startup of a new run from the "//& - "interior tracer concentrations regardless of properties that may be explicitly "//& - "specified for the reservoir concentrations.", default=enable_bugs, do_not_log=.true.) if (OBC_reservoir_init_bug .and. (allocated(CS%OBC%tres_x) .or. allocated(CS%OBC%tres_y))) & call MOM_error(FATAL, "OBC_RESERVOIR_INIT_BUG can not be set to true with grid rotation.") endif @@ -3414,7 +3426,34 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & endif if (associated(CS%OBC)) then - call MOM_initialize_OBCs(CS%h, CS%tv, CS%OBC, Time, G, GV, US, param_file, restart_CSp, CS%tracer_Reg) + if (use_temperature) then + if (.not. OBC_TS_reservoir_init_bug) then + ! Store the updated temperatures and salinities at the open boundaries, noting that they may + ! still be updated by the calls in the next 50 lines, so the code setting the tracer + ! reservoir values will come later in the calling routine. + call fill_temp_salt_segments(G, GV, US, CS%OBC, CS%tv) + endif + if (OBC_reservoir_init_bug .and. is_new_run(restart_CSp)) then + ! Set up OBC%trex_x and OBC%tres_y as they have not been read from a restart file. + ! When OBC_RESERVOIR_INIT_BUG is false, setup_OBC_tracer_reservoirs() is called from initialize_MOM + ! after all tracer package initialization is finished and grid rotation has been dealt with. + call setup_OBC_tracer_reservoirs(G, GV, CS%OBC) + ! Ensure that the values of the tracer reservoirs that have just been set will not be revised. + call set_initialized_OBC_tracer_reservoirs(G, CS%OBC, restart_CSp) + endif + endif + + call initialize_user_OBCs(CS%tv, CS%OBC, G, GV, US, param_file, CS%tracer_Reg) + + if (CS%debug) then + call hchksum(G%mask2dT, 'After initialize_user_OBCs: mask2dT ', G%HI) + call uvchksum('After initialize_user_OBCs: mask2dC[uv]', G%mask2dCu, G%mask2dCv, G%HI) + call qchksum(G%mask2dBu, 'After initialize_user_OBCs: mask2dBu ', G%HI) + endif + if (obc_debug_test) call open_boundary_test_extern_h(G, GV, CS%OBC, CS%h) + + if (CS%OBC%use_h_res) & + call fill_thickness_segments(G, GV, US, CS%OBC, CS%h) if (use_temperature) then call pass_var(CS%tv%T, G%Domain, complete=.false.) diff --git a/src/initialization/MOM_state_initialization.F90 b/src/initialization/MOM_state_initialization.F90 index 95e1260459..b3d6421352 100644 --- a/src/initialization/MOM_state_initialization.F90 +++ b/src/initialization/MOM_state_initialization.F90 @@ -22,10 +22,8 @@ module MOM_state_initialization use MOM_interface_heights, only : find_eta, dz_to_thickness, dz_to_thickness_simple use MOM_interface_heights, only : calc_derived_thermo use MOM_io, only : file_exists, field_size, MOM_read_data, MOM_read_vector, slasher -use MOM_open_boundary, only : ocean_OBC_type, open_boundary_test_extern_h -use MOM_open_boundary, only : fill_temp_salt_segments, setup_OBC_tracer_reservoirs -use MOM_open_boundary, only : fill_thickness_segments -use MOM_open_boundary, only : set_initialized_OBC_tracer_reservoirs +use MOM_open_boundary, only : ocean_OBC_type +use MOM_open_boundary, only : fill_temp_salt_segments use MOM_restart, only : restore_state, is_new_run, copy_restart_var, copy_restart_vector use MOM_restart, only : restart_registry_lock, MOM_restart_CS use MOM_sponge, only : set_up_sponge_field, set_up_sponge_ML_density @@ -100,7 +98,7 @@ module MOM_state_initialization #include -public MOM_initialize_state, MOM_initialize_OBCs +public MOM_initialize_state, initialize_user_OBCs ! A note on unit descriptions in comments: MOM6 uses units that can be rescaled for dimensional ! consistency testing. These are noted in comments with units like Z, H, L, and T, along with @@ -450,7 +448,7 @@ subroutine MOM_initialize_state(u, v, h, tv, Time, G, GV, US, PF, dirs, & call get_param(PF, mdl, "OBC_TS_RESERVOIR_INIT_BUG", OBC_TS_reservoir_init_bug, & "If true, set the OBC temperature and salinity reservoirs at the startup of a "//& "new run from initial values that are set before remapping.", & - default=enable_bugs, do_not_log=.true.) + default=enable_bugs) if (OBC_TS_reservoir_init_bug) then ! These calls should be moved down to join the OBC code, but doing so changes answers because ! the temperatures and salinities can change due to the remapping and reading from the restarts. @@ -653,112 +651,60 @@ subroutine MOM_initialize_state(u, v, h, tv, Time, G, GV, US, PF, dirs, & end subroutine MOM_initialize_state -subroutine MOM_initialize_OBCs(h, tv, OBC, Time, G, GV, US, PF, restart_CS, tracer_Reg) +subroutine initialize_user_OBCs(tv, OBC, G, GV, US, PF, tracer_Reg) type(ocean_grid_type), intent(inout) :: G !< The ocean's grid structure. type(verticalGrid_type), intent(in) :: GV !< The ocean's vertical grid structure. type(unit_scale_type), intent(in) :: US !< A dimensional unit scaling type - real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), & - intent(inout) :: h !< Layer thicknesses [H ~> m or kg m-2] type(thermo_var_ptrs), intent(inout) :: tv !< A structure pointing to various thermodynamic !! variables type(ocean_OBC_type), pointer :: OBC !< The open boundary condition control structure. - type(time_type), intent(in) :: Time !< Time at the start of the run segment. type(param_file_type), intent(in) :: PF !< A structure indicating the open file to parse !! for model parameter values. - type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure type(tracer_registry_type), pointer :: tracer_Reg !< A pointer to the tracer registry ! Local variables character(len=200) :: config - logical :: enable_bugs ! If true, the defaults for recently added bug-fix flags are set to - ! recreate the bugs, or if false bugs are only used if actively selected. - logical :: debug ! If true, write debugging output. - logical :: debug_obc ! If true, do additional calls resetting values to help debug the correctness - ! of the open boundary condition code. - logical :: OBC_TS_reservoir_init_bug ! If true, set the OBC temperature and salinity reservoirs - ! at the startup of a new run from initial values that are set before remapping. - logical :: OBC_reservoir_init_bug ! If true, set the OBC tracer reservoirs at the startup of a new - ! run from the interior tracer concentrations regardless of properties that - ! may be explicitly specified for the reservoir concentrations. - - call callTree_enter('MOM_initialize_OBCs()') - if (associated(OBC)) then - call get_param(PF, mdl, "DEBUG", debug, default=.false.) - call get_param(PF, mdl, "OBC_DEBUGGING_TESTS", debug_obc, & - "If true, do additional calls resetting values to help verify the correctness "//& - "of the open boundary condition code.", default=.false., & - do_not_log=.true., old_name="DEBUG_OBC", debuggingParam=.true.) - call get_param(PF, mdl, "ENABLE_BUGS_BY_DEFAULT", enable_bugs, & - default=.true., do_not_log=.true.) ! This is logged from MOM.F90. - call get_param(PF, mdl, "OBC_TS_RESERVOIR_INIT_BUG", OBC_TS_reservoir_init_bug, & - "If true, set the OBC temperature and salinity reservoirs at the startup of a "//& - "new run from initial values that are set before remapping.", default=enable_bugs) - if (associated(tv%T) .and. (.not.OBC_TS_reservoir_init_bug)) then - ! Store the updated temperatures and salinities at the open boundaries, noting that they may - ! still be updated by the calls in the next 50 lines, so the code setting the tracer - ! reservoir values will come later in the calling routine. - call fill_temp_salt_segments(G, GV, US, OBC, tv) - endif - call get_param(PF, mdl, "OBC_RESERVOIR_INIT_BUG", OBC_reservoir_init_bug, & - "If true, set the OBC tracer reservoirs at the startup of a new run from the "//& - "interior tracer concentrations regardless of properties that may be explicitly "//& - "specified for the reservoir concentrations.", default=enable_bugs) - if (OBC_reservoir_init_bug .and. associated(tv%T) .and. is_new_run(restart_CS)) then - ! Set up OBC%trex_x and OBC%tres_y as they have not been read from a restart file. - ! When OBC_RESERVOIR_INIT_BUG is false, setup_OBC_tracer_reservoirs() is called from initialize_MOM - ! after all tracer package initialization is finished and grid rotation has been dealt with. - call setup_OBC_tracer_reservoirs(G, GV, OBC) - ! Ensure that the values of the tracer reservoirs that have just been set will not be revised. - call set_initialized_OBC_tracer_reservoirs(G, OBC, restart_CS) - endif - ! This controls user code for setting open boundary data - call get_param(PF, mdl, "OBC_USER_CONFIG", config, & - "A string that sets how the user code is invoked to set open boundary data: \n"//& - " DOME - specified inflow on northern boundary\n"//& - " dyed_channel - supercritical with dye on the inflow boundary\n"//& - " dyed_obcs - circle_obcs with dyes on the open boundaries\n"//& - " Kelvin - barotropic Kelvin wave forcing on the western boundary\n"//& - " shelfwave - Flather with shelf wave forcing on western boundary\n"//& - " supercritical - now only needed here for the allocations\n"//& - " tidal_bay - Flather with tidal forcing on eastern boundary\n"//& - " USER - user specified", default="none") - if (trim(config) == "DOME") then - call DOME_set_OBC_data(OBC, tv, G, GV, US, PF, tracer_Reg) - elseif (trim(config) == "dyed_channel") then - call dyed_channel_set_OBC_tracer_data(OBC, G, GV, PF, tracer_Reg) - OBC%update_OBC = .true. - elseif (trim(config) == "dyed_obcs") then - call dyed_obcs_set_OBC_data(OBC, G, GV, PF, tracer_Reg) - elseif (trim(config) == "Kelvin") then - OBC%update_OBC = .true. - elseif (trim(config) == "shelfwave") then - OBC%update_OBC = .true. - elseif (lowercase(trim(config)) == "supercritical") then - call supercritical_set_OBC_data(OBC, G, GV, US, PF) - elseif (trim(config) == "tidal_bay") then - OBC%update_OBC = .true. - elseif (trim(config) == "USER") then - call user_set_OBC_data(OBC, tv, G, GV, PF, tracer_Reg) - elseif (.not. trim(config) == "none") then - call MOM_error(FATAL, "The open boundary conditions specified by "//& - "OBC_USER_CONFIG = "//trim(config)//" have not been fully implemented.") - endif + if (.not. associated(OBC)) return - if (debug) then - call hchksum(G%mask2dT, 'MOM_initialize_OBCs: mask2dT ', G%HI) - call uvchksum('MOM_initialize_OBCs: mask2dC[uv]', G%mask2dCu, G%mask2dCv, G%HI) - call qchksum(G%mask2dBu, 'MOM_initialize_OBCs: mask2dBu ', G%HI) - endif - if (debug_OBC) call open_boundary_test_extern_h(G, GV, OBC, h) + call callTree_enter('initialize_user_OBCs()') - if (OBC%use_h_res) & - call fill_thickness_segments(G, GV, US, OBC, h) + ! This controls user code for setting open boundary data + call get_param(PF, mdl, "OBC_USER_CONFIG", config, & + "A string that sets how the user code is invoked to set open boundary data: \n"//& + " \t DOME - specified inflow on northern boundary\n"//& + " \t dyed_channel - supercritical with dye on the inflow boundary\n"//& + " \t dyed_obcs - circle_obcs with dyes on the open boundaries\n"//& + " \t Kelvin - barotropic Kelvin wave forcing on the western boundary\n"//& + " \t shelfwave - Flather with shelf wave forcing on western boundary\n"//& + " \t supercritical - now only needed here for the allocations\n"//& + " \t tidal_bay - Flather with tidal forcing on eastern boundary\n"//& + " \t USER - user specified", default="none") + if (trim(config) == "DOME") then + call DOME_set_OBC_data(OBC, tv, G, GV, US, PF, tracer_Reg) + elseif (trim(config) == "dyed_channel") then + call dyed_channel_set_OBC_tracer_data(OBC, G, GV, PF, tracer_Reg) + OBC%update_OBC = .true. + elseif (trim(config) == "dyed_obcs") then + call dyed_obcs_set_OBC_data(OBC, G, GV, PF, tracer_Reg) + elseif (trim(config) == "Kelvin") then + OBC%update_OBC = .true. + elseif (trim(config) == "shelfwave") then + OBC%update_OBC = .true. + elseif (lowercase(trim(config)) == "supercritical") then + call supercritical_set_OBC_data(OBC, G, GV, US, PF) + elseif (trim(config) == "tidal_bay") then + OBC%update_OBC = .true. + elseif (trim(config) == "USER") then + call user_set_OBC_data(OBC, tv, G, GV, PF, tracer_Reg) + elseif (.not. trim(config) == "none") then + call MOM_error(FATAL, "The open boundary conditions specified by "//& + "OBC_USER_CONFIG = "//trim(config)//" have not been fully implemented.") endif - call callTree_leave('MOM_initialize_OBCs()') + call callTree_leave('initialize_user_OBCs()') -end subroutine MOM_initialize_OBCs +end subroutine initialize_user_OBCs !> Reads the layer thicknesses or interface heights from a file. subroutine initialize_thickness_from_file(h, depth_tot, G, GV, US, param_file, file_has_thickness, & From f0592ac61740917b0c65c7755e70a76c8a63d588 Mon Sep 17 00:00:00 2001 From: He Wang Date: Wed, 27 May 2026 13:36:16 -0400 Subject: [PATCH 2/7] Refactor initialize_OBC_segment_reservoirs Remove the thickness-reservoir initialization block from initialize_OBC_segment_reservoirs and rename the routine to initialize_OBC_tracer_reservoirs to reflect that it now handles only tracer reservoirs. The removed block set segment%h_Reg%h_res from the external thickness, which is repeating fill_thickness_segments. There is currently no true external thickness value ever assigned. Besides, this block of code only works when segment%h_Reg%is_initialized was false. That condition is never reached, as fill_thickness_segments is called earlier, which initializes h_res and sets is_initialized=.true. --- src/core/MOM.F90 | 4 ++-- src/core/MOM_open_boundary.F90 | 27 ++++++--------------------- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index b8f55e6e98..36820593e2 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -118,7 +118,7 @@ module MOM use MOM_open_boundary, only : ocean_OBC_type, open_boundary_end use MOM_open_boundary, only : register_temp_salt_segments, update_segment_tracer_reservoirs use MOM_open_boundary, only : read_OBC_dynamics_data, read_OBC_tracer_data -use MOM_open_boundary, only : initialize_OBC_segment_reservoirs +use MOM_open_boundary, only : initialize_OBC_tracer_reservoirs use MOM_open_boundary, only : setup_OBC_tracer_reservoirs use MOM_open_boundary, only : setup_OBC_thickness_reservoirs use MOM_open_boundary, only : open_boundary_register_restarts, remap_OBC_fields @@ -3467,7 +3467,7 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & ! BGC data is not read/updated at initialization since OBC%update_OBC_seg_data is false. call read_OBC_tracer_data(G, GV, US, CS%OBC, Time, include_bgc=.false.) call update_OBC_tracer_data(CS%OBC, include_bgc=.false.) - call initialize_OBC_segment_reservoirs(GV, CS%OBC) + call initialize_OBC_tracer_reservoirs(CS%OBC) endif if (use_ice_shelf .and. CS%debug) then diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index ecd5e51106..ee33ddf398 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -53,7 +53,7 @@ module MOM_open_boundary public read_OBC_tracer_data public update_OBC_dynamics_data public update_OBC_tracer_data -public initialize_OBC_segment_reservoirs +public initialize_OBC_tracer_reservoirs public open_boundary_test_extern_uv public open_boundary_test_extern_h public open_boundary_zero_normal_flow @@ -4999,25 +4999,22 @@ subroutine update_OBC_tracer_data(OBC, include_bgc) enddo ! end segment loop end subroutine update_OBC_tracer_data -!> Initialize thickness and tracer reservoirs to external value. -subroutine initialize_OBC_segment_reservoirs(GV, OBC) - type(verticalGrid_type), intent(in) :: GV !< Ocean vertical grid structure - type(ocean_OBC_type), pointer :: OBC !< Open boundary structure +!> Initialize tracer reservoirs to the current external tracer value. +subroutine initialize_OBC_tracer_reservoirs(OBC) + type(ocean_OBC_type), pointer :: OBC !< Open boundary structure ! Local variables type(OBC_segment_type), pointer :: segment => NULL() integer :: isd, ied, jsd, jed, IsdB, IedB, JsdB, JedB integer :: is_seg, ie_seg, js_seg, je_seg, nz integer :: n, m, nt, i, j, k - character(len=256) :: msg ! Error message if (.not. associated(OBC)) return - nz = GV%ke + nz = OBC%ke do n=1,OBC%number_of_segments segment => OBC%segment(n) - if (.not. segment%on_pe) cycle isd = segment%HI%isd ; ied = segment%HI%ied ; IsdB = segment%HI%IsdB ; IedB = segment%HI%IedB @@ -5031,18 +5028,6 @@ subroutine initialize_OBC_segment_reservoirs(GV, OBC) js_seg = JsdB ; je_seg = JedB ! = js_seg endif - ! Thickness - ! If the thickness reservoir has not yet been initialized, then set to external value. - if (OBC%thickness_x_reservoirs_used .or. OBC%thickness_y_reservoirs_used) then - if (.not. segment%h_Reg%is_initialized) then ! h_Reg may be initialized by fill_thickness_segments - do k=1,nz ; do j=js_seg,je_seg ; do i=is_seg,ie_seg - segment%h_Reg%h_res(i,j,k) = segment%h_Reg%h(i,j,k) - enddo ; enddo ; enddo - segment%h_Reg%is_initialized = .true. - endif - endif - - ! Tracers ! If the tracer reservoir has not yet been initialized, then set to external value. do m=NUM_PHYS_FIELDS-1, segment%num_fields ! F_T = NUM_PHYS_FIELDS-1 and F_S = NUM_PHYS_FIELDS if ((.not. allocated(segment%field(m)%buffer_dst)) .or. & @@ -5056,7 +5041,7 @@ subroutine initialize_OBC_segment_reservoirs(GV, OBC) endif enddo ! end tracer field loop enddo ! end segment loop -end subroutine initialize_OBC_segment_reservoirs +end subroutine initialize_OBC_tracer_reservoirs !> Update the OBC ramp value as a function of time. !! If called with the optional argument activate=.true., record the From 4e6f49aad650aad24e47edc444f324cf1393a449 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 27 Apr 2026 15:32:12 -0400 Subject: [PATCH 3/7] Refactor setup_OBC_tracer_reservoirs Simplify OBC tracer-reservoir initialization in initialize_MOM by eliminating the special-case handling that existed only for the OBC_RESERVOIR_INIT_BUG=True new-run path: - Guard the later initialize_OBC_tracer_reservoirs call with (((.not. OBC_reservoir_init_bug) .or. CS%diabatic_first) .and. is_new_run(restart_CSp)) so that on the bug route %tres set by fill_temp_salt_segments is preserved (not overwritten by %t), letting the %tres -> OBC%tres_[xy] save be deferred to the setup_OBC_tracer_reservoirs call later in initialize_MOM. - In setup_OBC_tracer_reservoirs, drop the "OBC%tres_[xy] = %t" branch taken when a segment reservoir was not yet is_initialized. The per-segment reservoir %tres is now unmodified by initialize_OBC_tracer_reservoirs in the OBC_RESERVOIR_INIT_BUG=True route, so the call to setup_OBC_tracer_reservoirs can be postponed and combined with a later call. The OBC%tres_[xy] = %t is also no longer needed. - Remove the early bug-route calls that ran right after fill_temp_salt_segments: setup_OBC_tracer_reservoirs and set_initialized_OBC_tracer_reservoirs. - Delete the now-unused public subroutine set_initialized_OBC_tracer_reservoirs and its export. With these changes %tres is no longer round-tripped through %t on the bug route, so the early setup and the %t fallback are redundant. --- src/core/MOM.F90 | 35 +++++++++++----------- src/core/MOM_open_boundary.F90 | 54 +++++++--------------------------- 2 files changed, 28 insertions(+), 61 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 36820593e2..c15e64ff1c 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -128,7 +128,6 @@ module MOM use MOM_open_boundary, only : open_boundary_halo_update, write_OBC_info, chksum_OBC_segments use MOM_open_boundary, only : segment_thickness_reservoir_init use MOM_open_boundary, only : fill_temp_salt_segments, fill_thickness_segments -use MOM_open_boundary, only : set_initialized_OBC_tracer_reservoirs use MOM_open_boundary, only : open_boundary_test_extern_h use MOM_open_boundary, only : copy_OBC_radiation_coefs use MOM_open_boundary, only : copy_OBC_tracer_reservoirs, copy_OBC_thickness_reservoirs @@ -3426,22 +3425,16 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & endif if (associated(CS%OBC)) then - if (use_temperature) then - if (.not. OBC_TS_reservoir_init_bug) then - ! Store the updated temperatures and salinities at the open boundaries, noting that they may - ! still be updated by the calls in the next 50 lines, so the code setting the tracer - ! reservoir values will come later in the calling routine. - call fill_temp_salt_segments(G, GV, US, CS%OBC, CS%tv) - endif - if (OBC_reservoir_init_bug .and. is_new_run(restart_CSp)) then - ! Set up OBC%trex_x and OBC%tres_y as they have not been read from a restart file. - ! When OBC_RESERVOIR_INIT_BUG is false, setup_OBC_tracer_reservoirs() is called from initialize_MOM - ! after all tracer package initialization is finished and grid rotation has been dealt with. - call setup_OBC_tracer_reservoirs(G, GV, CS%OBC) - ! Ensure that the values of the tracer reservoirs that have just been set will not be revised. - call set_initialized_OBC_tracer_reservoirs(G, CS%OBC, restart_CSp) - endif - endif + + if (use_temperature .and. ((.not. OBC_TS_reservoir_init_bug) .and. OBC_reservoir_init_bug)) & + call fill_temp_salt_segments(G, GV, US, CS%OBC, CS%tv) + ! For either OBC_TS_reservoir_init_bug=True or OBC_reservoir_init_bug=True, initialize_OBC_segment_reservoirs + ! is no longer called. So segment's %tres set by fill_temp_salt_segments is preserved for the rest of the + ! initialize_MOM subroutine. Therefore, we no longer need call setup_OBC_tracer_reservoirs here to set up + ! OBC%tres_x/y with %t at this point. And subroutine set_initialized_OBC_tracer_reservoirs, which locks the + ! restart fields, is also not needed. Because we can now set OBC%tres_x/y with %tres (unchanged when + ! setup_OBC_tracer_reservoirs is called in ~line 3755), the OBC%tres_x/y=%t route in setup_OBC_tracer_reservoirs + ! is now unused and removed, as changed there by this commmit. call initialize_user_OBCs(CS%tv, CS%OBC, G, GV, US, param_file, CS%tracer_Reg) @@ -3467,7 +3460,13 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & ! BGC data is not read/updated at initialization since OBC%update_OBC_seg_data is false. call read_OBC_tracer_data(G, GV, US, CS%OBC, Time, include_bgc=.false.) call update_OBC_tracer_data(CS%OBC, include_bgc=.false.) - call initialize_OBC_tracer_reservoirs(CS%OBC) + ! Conditionally call to initialize_OBC_tracer_reservoirs so that %tres is not overwritten by %t + ! in the bug route. In this way, in new runs %tres -> OBC%tres_x/y (call to setup_OBC_tracer_reservoirs) + ! can be pushed down to the block in line 3738. Restart runs are not affected. + if (((.not. OBC_reservoir_init_bug) .or. CS%diabatic_first) .and. is_new_run(restart_CSp)) & + ! T/S tracer reservoirs copy %t -> %tres and set is_initialized=.True. [but not BGC] + ! BGC data is not read/updated at initialization since OBC%update_OBC_seg_data is false. + call initialize_OBC_tracer_reservoirs(CS%OBC) endif if (use_ice_shelf .and. CS%debug) then diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index ee33ddf398..ffb4ea139b 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -76,7 +76,6 @@ module MOM_open_boundary public copy_OBC_thickness_reservoirs public update_segment_tracer_reservoirs public update_segment_thickness_reservoirs -public set_initialized_OBC_tracer_reservoirs public update_OBC_ramp public remap_OBC_fields public rotate_OBC_config @@ -2604,26 +2603,19 @@ subroutine setup_OBC_tracer_reservoirs(G, GV, OBC, restart_CS) if (segment%is_E_or_W .and. set_tres_x) then I = segment%HI%IsdB - if (segment%tr_Reg%Tr(m)%is_initialized) then - do k=1,GV%ke ; do j=segment%HI%jsd,segment%HI%jed - OBC%tres_x(I,j,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,j,k) - enddo ; enddo - else - do k=1,GV%ke ; do j=segment%HI%jsd,segment%HI%jed - OBC%tres_x(I,j,k,m) = I_scale * segment%tr_Reg%Tr(m)%t(i,j,k) - enddo ; enddo - endif + do k=1,GV%ke ; do j=segment%HI%jsd,segment%HI%jed + ! The only place OBC%tres_x/y = %t route was used was when OBC_RESERVOIR_INIT_BUG=True in a new run, + ! in which case is_initialized is not turned on in fill_temp_salt_segments, and this subroutine is + ! called immediately afterward. Now, as initialize_OBC_tracer_reservoirs in initialize_MOM is + ! not called when OBC_RESERVOIR_INIT_BUG=True, %tres is not modified and is_initialized is not set, + ! the call to setup_OBC_tracer_reservoirs can be safely postponed, the OBC%tres_x=%t route can be removed. + OBC%tres_x(I,j,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,j,k) + enddo ; enddo elseif (segment%is_N_or_S .and. set_tres_y) then J = segment%HI%JsdB - if (segment%tr_Reg%Tr(m)%is_initialized) then - do k=1,GV%ke ; do i=segment%HI%isd,segment%HI%ied - OBC%tres_y(i,J,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,J,k) - enddo ; enddo - else - do k=1,GV%ke ; do i=segment%HI%isd,segment%HI%ied - OBC%tres_y(i,J,k,m) = I_scale * segment%tr_Reg%Tr(m)%t(i,J,k) - enddo ; enddo - endif + do k=1,GV%ke ; do i=segment%HI%isd,segment%HI%ied + OBC%tres_y(i,J,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,J,k) + enddo ; enddo endif endif ; endif enddo @@ -2696,30 +2688,6 @@ subroutine setup_OBC_thickness_reservoirs(G, GV, OBC, restart_CS) end subroutine setup_OBC_thickness_reservoirs -!> Record that the tracer reservoirs have been initialized so that their values are not reset later. -subroutine set_initialized_OBC_tracer_reservoirs(G, OBC, restart_CS) - type(ocean_grid_type), intent(in) :: G !< Ocean grid structure - type(ocean_OBC_type), intent(in) :: OBC !< Open boundary control structure - type(MOM_restart_CS), intent(inout) :: restart_CS !< MOM restart control structure - character(len=12) :: x_var_name, y_var_name - integer :: m - - do m=1,OBC%ntr - ! Set the names of the reservoirs for this tracer in the restart file - if (modulo(G%HI%turns, 2) == 0) then - write(x_var_name,'("tres_x_",I3.3)') m - write(y_var_name,'("tres_y_",I3.3)') m - else - write(x_var_name,'("tres_y_",I3.3)') m - write(y_var_name,'("tres_x_",I3.3)') m - endif - - if (OBC%tracer_x_reservoirs_used(m)) call set_initialized(OBC%tres_x, x_var_name, restart_CS) - if (OBC%tracer_y_reservoirs_used(m)) call set_initialized(OBC%tres_y, y_var_name, restart_CS) - enddo - -end subroutine set_initialized_OBC_tracer_reservoirs - !> Copy radiation and oblique boundary condition coefficients (phase speeds and normalizing !! denominator) from the global restart arrays into the per-segment arrays. subroutine copy_OBC_radiation_coefs(OBC) From 3b0c11ba18beee0952141c0866fc34a4d21f2337 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 27 Apr 2026 17:42:18 -0400 Subject: [PATCH 4/7] Split OBC reservoir setup and copy by run type Restructure the OBC reservoir block that follows tracer_flow_control_init in initialize_MOM into two mutually exclusive branches selected by is_new_run(restart_CSp): Previously all of these ran every time, relying on query_initialized inside the setup_* routines and on an explicit if-guard before copy_OBC_tracer_reservoirs to sort out the new-run vs restart cases. The restructure makes the branching explicit, improves readability. * new run: save segment %tres / %h_res into the global restart arrays OBC%tres_[xy] / OBC%h_res_[xy] * setup_OBC_tracer_reservoirs * setup_OBC_thickness_reservoirs * restart: restore OBC%tres_[xy] / OBC%h_res_[xy] read from the restart file back into the segment reservoirs * open_boundary_halo_update * copy_OBC_radiation_coefs * copy_OBC_tracer_reservoirs * copy_OBC_thickness_reservoirs OBC halo update clock init is moved from open_boundary_halo_update to open_boundary_config. --- src/core/MOM.F90 | 23 ++++++++++++----------- src/core/MOM_open_boundary.F90 | 9 +++++---- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index c15e64ff1c..65badd444d 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -3878,19 +3878,20 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & call register_diags_offline_transport(Time, CS%diag, CS%offline_CSp, GV, US) endif + ! [if new_sim] Save %tres to OBC%tres_[xy] (needs to be after tracer_flow_control_init to init BGC) + ! [if restart] copy OBC%tres_[xy] to %tres if (associated(CS%OBC)) then - ! At this point any information related to the tracer reservoirs has either been read from - ! the restart file or has been specified in the segments. Initialize the tracer reservoir - ! values from the segments if they have not been set via the restart file. - call setup_OBC_tracer_reservoirs(G, GV, CS%OBC, restart_CSp) - call setup_OBC_thickness_reservoirs(G, GV, CS%OBC, restart_CSp) - call open_boundary_halo_update(G, CS%OBC) - call copy_OBC_radiation_coefs(CS%OBC) - if (.not. (CS%OBC%reservoir_init_bug .and. new_sim .and. CS%diabatic_first)) & - ! The if-guard is needed to preserve old answers with OBC_RESERVOIR_INIT_BUG=True, in which case - ! segment T/S reservoir %tres and global restart arrays OBC%tres_x/y have diverged at this point. + if (is_new_run(restart_CSp)) then + if (save_IC) then + call setup_OBC_tracer_reservoirs(G, GV, CS%OBC, restart_CSp) + call setup_OBC_thickness_reservoirs(G, GV, CS%OBC, restart_CSp) + endif + else + call open_boundary_halo_update(G, CS%OBC) + call copy_OBC_radiation_coefs(CS%OBC) call copy_OBC_tracer_reservoirs(CS%OBC) - call copy_OBC_thickness_reservoirs(CS%OBC, G, GV) + call copy_OBC_thickness_reservoirs(CS%OBC, G, GV) + endif endif call register_obsolete_diagnostics(param_file, CS%diag) diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index ffb4ea139b..88d951bd82 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -954,6 +954,8 @@ subroutine open_boundary_config(G, US, param_file, OBC) call open_boundary_dealloc(OBC) endif + id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=CLOCK_ROUTINE) + end subroutine open_boundary_config !> Setup vertical remapping for open boundaries @@ -2293,13 +2295,12 @@ subroutine open_boundary_halo_update(G, OBC) if (.not.associated(OBC)) return - id_clock_pass = cpu_clock_id('(Ocean OBC halo updates)', grain=CLOCK_ROUTINE) if (OBC%radiation_BCs_exist_globally) call pass_vector(OBC%rx_normal, OBC%ry_normal, G%Domain, & To_All+Scalar_Pair) if (OBC%oblique_BCs_exist_globally) then -! call pass_vector(OBC%rx_oblique_u, OBC%ry_oblique_v, G%Domain, To_All+Scalar_Pair) -! call pass_vector(OBC%ry_oblique_u, OBC%rx_oblique_v, G%Domain, To_All+Scalar_Pair) -! call pass_vector(OBC%cff_normal_u, OBC%cff_normal_v, G%Domain, To_All+Scalar_Pair) + ! call pass_vector(OBC%rx_oblique_u, OBC%ry_oblique_v, G%Domain, To_All+Scalar_Pair) + ! call pass_vector(OBC%ry_oblique_u, OBC%rx_oblique_v, G%Domain, To_All+Scalar_Pair) + ! call pass_vector(OBC%cff_normal_u, OBC%cff_normal_v, G%Domain, To_All+Scalar_Pair) call create_group_pass(OBC%pass_oblique, OBC%rx_oblique_u, OBC%ry_oblique_v, G%Domain, To_All+Scalar_Pair) call create_group_pass(OBC%pass_oblique, OBC%ry_oblique_u, OBC%rx_oblique_v, G%Domain, To_All+Scalar_Pair) call create_group_pass(OBC%pass_oblique, OBC%cff_normal_u, OBC%cff_normal_v, G%Domain, To_All+Scalar_Pair) From 70c52e48ded980dcd6712f9ebfb03bd9046c13fa Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 27 Apr 2026 18:23:27 -0400 Subject: [PATCH 5/7] Remove is_initialized checks in OBC fills fill_temp_salt_segments is only called before initialize_OBC_tracer_reservoirs, which is the sole place that sets segment%tr_Reg%Tr(:)%is_initialized to .true. So when fill_temp_salt_segments is called, the is_initialized flag is always .false., so the "if (.not. is_initialized)" guards is redundant. Similar for fill_obgc_segments, the only place BGC tracers' is_initialized flag is switched to true is in initialize_OBC_tracer_reservoirs, which never happens because update_OBC_seg_data=.false. during initialization. As in the previous commit, assigning BGC tracers' OBC%tres_[xy] can be deferred to a call to setup_OBC_tracer_reservoirs later in initialize_MOM. --- src/core/MOM_open_boundary.F90 | 38 +++++++++------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index 88d951bd82..8dec4f260d 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -5453,31 +5453,12 @@ subroutine fill_obgc_segments(G, GV, OBC, tr_ptr, tr_name) enddo ; enddo endif - if (.not.segment%tr_Reg%Tr(nt)%is_initialized) & - segment%tr_Reg%Tr(nt)%tres(:,:,:) = segment%tr_Reg%Tr(nt)%t(:,:,:) - - if (OBC%reservoir_init_bug) then - ! OBC%tres_x and OBC%tres_y should not be set here, but in a subsequent call to setup_OBC_tracer_reservoirs. - ! Note that fill_obgc_segments is not called for runs that start from a restart file. - I_scale = 1.0 - if (segment%tr_Reg%Tr(nt)%scale /= 0.0) I_scale = 1.0 / segment%tr_Reg%Tr(nt)%scale - if (segment%is_E_or_W) then - if (allocated(OBC%tres_x)) then - I = segment%HI%IsdB - do k=1,nz ; do j=segment%HI%jsd,segment%HI%jed - OBC%tres_x(I,j,k,nt) = I_scale * segment%tr_Reg%Tr(nt)%tres(I,j,k) - enddo ; enddo - endif - else ! segment%is_N_or_S - if (allocated(OBC%tres_y)) then - J = segment%HI%JsdB - do k=1,nz ; do i=segment%HI%isd,segment%HI%ied - OBC%tres_y(i,J,k,nt) = I_scale * segment%tr_Reg%Tr(nt)%tres(i,J,k) - enddo ; enddo - endif - endif - endif + ! The only place that sets is_initialized=.true. is in initialize_OBC_tracer_reservoirs, but + ! that can never happen because for BGC tracers update_OBC_seg_data=.false. during initialization. + segment%tr_Reg%Tr(nt)%tres(:,:,:) = segment%tr_Reg%Tr(nt)%t(:,:,:) + ! For the bug route, BGC tracers' OBC%tres_x/y is set by per-segment %tres in ~line 3727 in MOM.F90 by a call + ! to setup_OBC_tracer_reservoirs. enddo ! End of loop over segments. end subroutine fill_obgc_segments @@ -5533,10 +5514,11 @@ subroutine fill_temp_salt_segments(G, GV, US, OBC, tv) endif enddo ; enddo endif - if (.not.segment%tr_Reg%Tr(1)%is_initialized) & - segment%tr_Reg%Tr(1)%tres(:,:,:) = segment%tr_Reg%Tr(1)%t(:,:,:) - if (.not.segment%tr_Reg%Tr(2)%is_initialized) & - segment%tr_Reg%Tr(2)%tres(:,:,:) = segment%tr_Reg%Tr(2)%t(:,:,:) + + ! fill_temp_salt_segments is only called before initialize_OBC_tracer_reservoirs, the only + ! place is_initialized can be set to .true. So the conditional assign is redundant. + segment%tr_Reg%Tr(1)%tres(:,:,:) = segment%tr_Reg%Tr(1)%t(:,:,:) + segment%tr_Reg%Tr(2)%tres(:,:,:) = segment%tr_Reg%Tr(2)%t(:,:,:) enddo end subroutine fill_temp_salt_segments From 8b4a16af85c6ad924a21153e108038b584f48b19 Mon Sep 17 00:00:00 2001 From: He Wang Date: Mon, 27 Apr 2026 18:24:52 -0400 Subject: [PATCH 6/7] Reorder OBC init sequence in initialize_MOM Reorganize the open boundary condition setup in initialize_MOM simplify call sequence and improve readability. The new order is 1. Fill segment dynamics fields (unconditional): * calc_derived_thermo, * read_OBC_dynamics_data, * update_OBC_dynamics_data, * fill_thickness_segments. 2. Initialize T/S tracer reservoirs (new runs only) * First fill with interior values via fill_temp_salt_segments * Then the external read/update and initialize_OBC_tracer_reservoirs. 3. Initialize_user_OBCs, which can later override the reservoirs. The dynamics fill is moved ahead of the reservoir seeding and kept unconditional because its outputs are required regardless of the reservoir-init bug flags: the continuity calls in initialize_dyn_* consume the segment transports, read_OBC_tracer_data needs the segment%dz set by update_OBC_dynamics_data, and the update provides the one-time fill relied on when OBC_VALUE_UPDATE_BUG is true. Also remove the debug mask checksums after initialize_user_OBCs (mask2dT, mask2dC[uv], mask2dBu) and the now-unused qchksum import. There are no calls after the last mask chksum in MOM_initialize_fixed, so these debugs are not needed. --- src/core/MOM.F90 | 74 +++++++++++++++++----------------- src/core/MOM_open_boundary.F90 | 5 --- 2 files changed, 36 insertions(+), 43 deletions(-) diff --git a/src/core/MOM.F90 b/src/core/MOM.F90 index 65badd444d..256f203a65 100644 --- a/src/core/MOM.F90 +++ b/src/core/MOM.F90 @@ -7,7 +7,7 @@ module MOM ! Infrastructure modules use MOM_array_transform, only : rotate_array, rotate_vector -use MOM_debugging, only : MOM_debugging_init, hchksum, uvchksum, qchksum, totalTandS +use MOM_debugging, only : MOM_debugging_init, hchksum, uvchksum, totalTandS use MOM_debugging, only : check_redundant, query_debugging_checks use MOM_checksum_packages, only : MOM_thermo_chksum, MOM_state_chksum use MOM_checksum_packages, only : MOM_accel_chksum, MOM_surface_chksum @@ -3425,48 +3425,46 @@ subroutine initialize_MOM(Time, Time_init, param_file, dirs, CS, & endif if (associated(CS%OBC)) then - - if (use_temperature .and. ((.not. OBC_TS_reservoir_init_bug) .and. OBC_reservoir_init_bug)) & - call fill_temp_salt_segments(G, GV, US, CS%OBC, CS%tv) - ! For either OBC_TS_reservoir_init_bug=True or OBC_reservoir_init_bug=True, initialize_OBC_segment_reservoirs - ! is no longer called. So segment's %tres set by fill_temp_salt_segments is preserved for the rest of the - ! initialize_MOM subroutine. Therefore, we no longer need call setup_OBC_tracer_reservoirs here to set up - ! OBC%tres_x/y with %t at this point. And subroutine set_initialized_OBC_tracer_reservoirs, which locks the - ! restart fields, is also not needed. Because we can now set OBC%tres_x/y with %tres (unchanged when - ! setup_OBC_tracer_reservoirs is called in ~line 3755), the OBC%tres_x/y=%t route in setup_OBC_tracer_reservoirs - ! is now unused and removed, as changed there by this commmit. - - call initialize_user_OBCs(CS%tv, CS%OBC, G, GV, US, param_file, CS%tracer_Reg) - - if (CS%debug) then - call hchksum(G%mask2dT, 'After initialize_user_OBCs: mask2dT ', G%HI) - call uvchksum('After initialize_user_OBCs: mask2dC[uv]', G%mask2dCu, G%mask2dCv, G%HI) - call qchksum(G%mask2dBu, 'After initialize_user_OBCs: mask2dBu ', G%HI) - endif - if (obc_debug_test) call open_boundary_test_extern_h(G, GV, CS%OBC, CS%h) - + ! Read and calculate segment dynamic fields. Needed for + ! (a) continuity calls in initialize_dyn_* + ! (b) segment%dz used for subsequent read_OBC_tracer_data call + ! (c) One-time dynamic update when OBC_VALUE_UPDATE_BUG=True + ! Calcualte tv%SpV_avg for dz used in read_OBC_segment_data + call calc_derived_thermo(CS%tv, CS%h, G, GV, US) + ! OBC file -> segment%field()%buffer_dst + call read_OBC_dynamics_data(G, GV, US, CS%OBC, CS%tv, CS%h, Time) + ! segment%field()%buffer_dst -> segment%normal_trans etc + call update_OBC_dynamics_data(G, GV, US, CS%OBC, CS%h, Time) + ! h -> %h and %h_res if (CS%OBC%use_h_res) & call fill_thickness_segments(G, GV, US, CS%OBC, CS%h) - if (use_temperature) then - call pass_var(CS%tv%T, G%Domain, complete=.false.) - call pass_var(CS%tv%S, G%Domain, complete=.true.) + ! Initialize OBC segment T/S reservoir [segment%tr_Reg%Tr()%tres] + if (is_new_run(restart_CSp) .and. use_temperature) then + ! T/S reservoirs are initialized by interior data. + ! They may be overriden by external data again a few lines later. The only reason that this + ! call is made unconditional to OBC_reservoir_init_bug or diabatic_first is to provide a + ! fallback for buggy cases where use_temperature is True but external T/S are not given. + if (.not. OBC_TS_reservoir_init_bug) & + ! Interior T/S -> segment%tr_Reg%Tr()%t and segment%tr_Reg%Tr()%tres + call fill_temp_salt_segments(G, GV, US, CS%OBC, CS%tv) + + ! T/S reservoirs are initialized by external data. + ! OBC file -> segment%field()%buffer_dst -> segment%tr_Reg%Tr()%t -> segment%tr_Reg%Tr()%tres. + ! Note BGC data is deliberately not read/updated at initialization since OBC%update_OBC_seg_data is false. + if ((.not. OBC_reservoir_init_bug) .or. CS%diabatic_first) then + ! OBC file -> segment%field()%buffer_dst + call read_OBC_tracer_data(G, GV, US, CS%OBC, Time, include_bgc=.false.) + ! [T/S] segment%field()%buffer_dst -> segment%tr_Reg%Tr()%t + call update_OBC_tracer_data(CS%OBC, include_bgc=.false.) + ! [T/S] segment%tr_Reg%Tr()%t -> segment%tr_Reg%Tr()%tres + call initialize_OBC_tracer_reservoirs(CS%OBC) + endif endif - call calc_derived_thermo(CS%tv, CS%h, G, GV, US) - ! Call this during initialization to fill boundary arrays from fixed values - call read_OBC_dynamics_data(G, GV, US, CS%OBC, CS%tv, CS%h, Time) - call update_OBC_dynamics_data(G, GV, US, CS%OBC, CS%h, Time) - ! BGC data is not read/updated at initialization since OBC%update_OBC_seg_data is false. - call read_OBC_tracer_data(G, GV, US, CS%OBC, Time, include_bgc=.false.) - call update_OBC_tracer_data(CS%OBC, include_bgc=.false.) - ! Conditionally call to initialize_OBC_tracer_reservoirs so that %tres is not overwritten by %t - ! in the bug route. In this way, in new runs %tres -> OBC%tres_x/y (call to setup_OBC_tracer_reservoirs) - ! can be pushed down to the block in line 3738. Restart runs are not affected. - if (((.not. OBC_reservoir_init_bug) .or. CS%diabatic_first) .and. is_new_run(restart_CSp)) & - ! T/S tracer reservoirs copy %t -> %tres and set is_initialized=.True. [but not BGC] - ! BGC data is not read/updated at initialization since OBC%update_OBC_seg_data is false. - call initialize_OBC_tracer_reservoirs(CS%OBC) + call initialize_user_OBCs(CS%tv, CS%OBC, G, GV, US, param_file, CS%tracer_Reg) + + if (obc_debug_test) call open_boundary_test_extern_h(G, GV, CS%OBC, CS%h) endif if (use_ice_shelf .and. CS%debug) then diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index 8dec4f260d..c4e918df07 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -5453,12 +5453,7 @@ subroutine fill_obgc_segments(G, GV, OBC, tr_ptr, tr_name) enddo ; enddo endif - ! The only place that sets is_initialized=.true. is in initialize_OBC_tracer_reservoirs, but - ! that can never happen because for BGC tracers update_OBC_seg_data=.false. during initialization. segment%tr_Reg%Tr(nt)%tres(:,:,:) = segment%tr_Reg%Tr(nt)%t(:,:,:) - - ! For the bug route, BGC tracers' OBC%tres_x/y is set by per-segment %tres in ~line 3727 in MOM.F90 by a call - ! to setup_OBC_tracer_reservoirs. enddo ! End of loop over segments. end subroutine fill_obgc_segments From 99d40af6f5c038c359e119bb91fd8fbe927e92f7 Mon Sep 17 00:00:00 2001 From: He Wang Date: Fri, 26 Jun 2026 14:11:45 -0400 Subject: [PATCH 7/7] Remove is_initialized from OBC tracer reservoir The is_initialized flag is now only read by initialize_OBC_tracer_reservoirs, and the only purpose is to skip %tres = %t copy for tracers whose reservoir value had been set explicitly via register_segment_tracer(..., OBC_scalar=...) in user cases. That guard is no longer needed after initialize_user_OBCs is now called after initialize_OBC_tracer_reservoirs, so any user-specified reservoir value is applied last and cannot be overwritten. The h_Reg (thickness reservoir) is_initialized flag is untouched. --- src/core/MOM_open_boundary.F90 | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/src/core/MOM_open_boundary.F90 b/src/core/MOM_open_boundary.F90 index c4e918df07..71dfbddf57 100644 --- a/src/core/MOM_open_boundary.F90 +++ b/src/core/MOM_open_boundary.F90 @@ -154,7 +154,6 @@ module MOM_open_boundary !> Tracer on OBC segment data structure, for putting into a segment tracer registry. type, public :: OBC_segment_tracer_type - logical :: is_initialized !< Reservoir values have been set when True character(len=32) :: name !< Tracer name used for error messages integer :: ntr_index = -1 !< Index of segment tracer in the global tracer registry real, allocatable :: t(:,:,:) !< External tracer concentration array in rescaled @@ -2605,11 +2604,6 @@ subroutine setup_OBC_tracer_reservoirs(G, GV, OBC, restart_CS) if (segment%is_E_or_W .and. set_tres_x) then I = segment%HI%IsdB do k=1,GV%ke ; do j=segment%HI%jsd,segment%HI%jed - ! The only place OBC%tres_x/y = %t route was used was when OBC_RESERVOIR_INIT_BUG=True in a new run, - ! in which case is_initialized is not turned on in fill_temp_salt_segments, and this subroutine is - ! called immediately afterward. Now, as initialize_OBC_tracer_reservoirs in initialize_MOM is - ! not called when OBC_RESERVOIR_INIT_BUG=True, %tres is not modified and is_initialized is not set, - ! the call to setup_OBC_tracer_reservoirs can be safely postponed, the OBC%tres_x=%t route can be removed. OBC%tres_x(I,j,k,m) = I_scale * segment%tr_Reg%Tr(m)%tres(i,j,k) enddo ; enddo elseif (segment%is_N_or_S .and. set_tres_y) then @@ -5002,12 +4996,9 @@ subroutine initialize_OBC_tracer_reservoirs(OBC) if ((.not. allocated(segment%field(m)%buffer_dst)) .or. & (segment%field(m)%bgc_tracer .and. (.not. OBC%update_OBC_seg_data))) cycle nt = segment%field(m)%tr_index - if (.not. segment%tr_Reg%Tr(nt)%is_initialized) then ! T/S may be initialized by fill_temp_salt_segments - do k=1,nz ; do j=js_seg,je_seg ; do i=is_seg,ie_seg - segment%tr_Reg%Tr(nt)%tres(i,j,k) = segment%tr_Reg%Tr(nt)%t(i,j,k) - enddo ; enddo ; enddo - segment%tr_Reg%Tr(nt)%is_initialized = .true. - endif + do k=1,nz ; do j=js_seg,je_seg ; do i=is_seg,ie_seg + segment%tr_Reg%Tr(nt)%tres(i,j,k) = segment%tr_Reg%Tr(nt)%t(i,j,k) + enddo ; enddo ; enddo enddo ! end tracer field loop enddo ! end segment loop end subroutine initialize_OBC_tracer_reservoirs @@ -5224,12 +5215,10 @@ subroutine register_segment_tracer(tr_ptr, ntr_index, param_file, GV, segment, O if (present(OBC_scalar)) then init_value = OBC_scalar - segment%tr_Reg%Tr(ntseg)%is_initialized = .true. segment%tr_Reg%Tr(ntseg)%resrv_lfac_in = 0.0 segment%tr_Reg%Tr(ntseg)%resrv_lfac_out = 0.0 else init_value = 0.0 - segment%tr_Reg%Tr(ntseg)%is_initialized = .false. ! Currently, resrv_lfac_in/out are for BGC tracers only. if (present(resrv_lfac_in)) segment%tr_Reg%Tr(ntseg)%resrv_lfac_in = resrv_lfac_in if (present(resrv_lfac_out)) segment%tr_Reg%Tr(ntseg)%resrv_lfac_out = resrv_lfac_out @@ -5510,8 +5499,6 @@ subroutine fill_temp_salt_segments(G, GV, US, OBC, tv) enddo ; enddo endif - ! fill_temp_salt_segments is only called before initialize_OBC_tracer_reservoirs, the only - ! place is_initialized can be set to .true. So the conditional assign is redundant. segment%tr_Reg%Tr(1)%tres(:,:,:) = segment%tr_Reg%Tr(1)%t(:,:,:) segment%tr_Reg%Tr(2)%tres(:,:,:) = segment%tr_Reg%Tr(2)%t(:,:,:) enddo