Skip to content

Decouple OBC dynamics and tracer update#1130

Open
herrwang0 wants to merge 6 commits into
NOAA-GFDL:dev/gfdlfrom
herrwang0:refactor-obc-split-dyn-tracer-update
Open

Decouple OBC dynamics and tracer update#1130
herrwang0 wants to merge 6 commits into
NOAA-GFDL:dev/gfdlfrom
herrwang0:refactor-obc-split-dyn-tracer-update

Conversation

@herrwang0

Copy link
Copy Markdown

This PR removes the extra read/update to OBC tracers at every dynamics time step. It also adds a parameter LOCK_BGC_OBC_TO_DT_TRACER (default .false.) to preview the scenario that the problematic DT_OBC_SEG_UPDATE_OBGC become obsolete.

This is a preview of a future state in which DT_OBC_SEG_UPDATE_OBGC is deprecated. All answers are bitwise identical.

How the update schedule changes

Previously, all OBC segment data, dynamics and tracers, is read and updated at the start of each dynamic step. The tracer read/update is redundant as it won't be used until the tracer advection step, which is after a multiple of dynamic steps. DT_OBC_SEG_UPDATE_OBGC complicates the picture for BGC tracers, but as discussed in #1121, the only reasonable value for DT_OBC_SEG_UPDATE_OBGC is DT_TRACER_ADVECT . (DT_OBC_SEG_UPDATE_OBGC = DT also works, but it yields the same answer as DT_TRACER_ADVECT and is less efficient)

The diagrams below compare the two update schedules. The answers are unchanged for either (provided that DT_OBC_SEG_UPDATE_OBGC is set correctly), but the new behavior should be more efficient.

LOCK_BGC_OBC_TO_DT_TRACER = .false. (default, existing behavior)

D = dynamics; T = T/S tracer; B = BGC tracer.

  • If DT_OBC_SEG_UPDATE_OBGC = DT
            t=0      dt      2dt     3dt     4dt     5dt     6dt     7dt     8dt
             |       |       |       |       |       |       |       |       |
dyn step:    [DT    ][DTB   ][DTB   ][DTB   ][DTB   ][DTB   ][DTB   ][DTB   ]
             ↙--------------↲↙--------------↲↙--------------↲↙--------------↲
tracer step: [              ][              ][              ][              ]
  • If DT_OBC_SEG_UPDATE_OBGC = DT_TRACER_ADVECT
            t=0      dt      2dt     3dt     4dt     5dt     6dt     7dt     8dt
             |       |       |       |       |       |       |       |       |
dyn step:    [DT    ][DTB   ][DT    ][DTB   ][DT    ][DTB   ][DT    ][DTB   ]
             ↙--------------↲↙--------------↲↙--------------↲↙--------------↲
tracer step: [              ][              ][              ][              ]

LOCK_BGC_OBC_TO_DT_TRACER = .true. (new behavior)

T/S and BGC OBC updates are removed from update_OBC_data and moved to step_MOM_tracer_dyn. DT_OBC_SEG_UPDATE_OBGC is ignored. The asterisk in the diagram below means dz used to remap external tracers is from the previous dynamics update (at the beginning of a dynamic step, and therefore h is not updated). This is incorrect and only to keep answers unchanged. An upcoming PR will address this issue.

            t=0      dt      2dt     3dt     4dt     5dt     6dt     7dt     8dt
             |       |       |       |       |       |       |       |       |
dyn step:    [D     ][D     ][D     ][D     ][D     ][D     ][D     ][D     ]
             ↙--------------↲↙--------------↲↙--------------↲↙--------------↲
tracer step: [TB*           ][TB*           ][TB*           ][TB*           ]

Commits

6032018: Add a new field dz to segments, which is used for remapping input data. This is a preparatory step to decouple dynamics and tracer updates.
6569894: Split update_OBC_segment_data into update_OBC_dynamics_data and update_OBC_tracer_data
6a8ce68: Apply the split for update_OBC_segment_*
c592158: Split read_OBC_segment_data into read_OBC_dynamics_data and read_OBC_tracer_data
e033eb5: Apply the split for read_OBC_segment_*
bc80e75: Add parameter LOCK_BGC_OBC_TO_DT_TRACER

Comments

Parameter LOCK_BGC_OBC_TO_DT_TRACER should be only treated as a testing tool (for timing, e.g.). If we can reach an agreement to obsolete DT_OBC_SEG_UPDATE_OBGC, then we should simply remove all LOCK_BGC_OBC_TO_DT_TRACER=False paths, as well as some guards introduced in #1121.

AI disclaimer

Various models by Claude are used to realize a small part of the code, draft commit messages, PR descriptions. Everything is checked and tested by human.

Add a 3D allocatable array `segment%dz` to `OBC_segment_type` to store
per-layer vertical extents at OBC segments.

This is a preparatory step to split reading dynamics and tracer data at
OBC.
Split the body of update_OBC_segment_data into two new public
subroutines so the dynamical fields and the tracer reservoir can be
updated independently:

* update_OBC_dynamics_data — normal/tangential velocity, tangential
  gradient, SSH, and the thickness reservoir.
* update_OBC_tracer_data — tracer reservoir copy from
segment%field(m)%buffer_dst to tracer registry's %t field.
* update_OBC_tracer_data also has an optional logical argument
include_bgc (defaulting to .true.) to allow more control. This flag
will be used to preserve answers in an upcoming commit.
Replace calls to update_OBC_segment_data with two separate calls to
update_OBC_dynamics_data and update_OBC_tracer_data.

update_OBC_segment_data is remove from MOM_open_boundary.
Introduce read_OBC_dynamics_data and read_OBC_tracer_data, two new
public routines that split the per-segment field loop in
read_OBC_segment_data along its dynamics/tracer boundary
(m=1..NUM_PHYS_FIELDS-2 vs m=NUM_PHYS_FIELDS-1..segment%num_fields),
plus a private helper read_OBC_field_data that does the per-field
read and remap.

* read_OBC_dynamics_data calls thickness_to_dz and populates
segment%dz and segment%dZtot, then loops over the dynamical fields.
* read_OBC_tracer_data assumes segment%dz is already populated by a
prior dynamics_data call at the same time step and only loops over
tracer fields.
* read_OBC_tracer_data also has an optional logical argument
include_bgc (defaulting to .true.) to allow more control. This flags
will be used to preserve answers in an upcoming commit.
Replace calls to read_OBC_segment_data with two separate calls to
read_OBC_dynamics_data and read_OBC_tracer_data.

read_OBC_segment_data is remove from MOM_open_boundary.
Adds a preview parameter LOCK_BGC_OBC_TO_DT_TRACER (default false) that
locks BGC OBC segment data updates to every tracer advection step,
ignoring DT_OBC_SEG_UPDATE_OBGC. When true, dt_obc_seg_period is forced
to zero in initialize_MOM and tracer reads/updates are moved from the
dynamics step (update_OBC_data) to the tracer step
(step_MOM_tracer_dyn).

This is a preview of a future state in which DT_OBC_SEG_UPDATE_OBGC is
deprecated. All answers are bitwise identical when
LOCK_BGC_OBC_TO_DT_TRACER is false (the default).
@herrwang0 herrwang0 added the refactor Code cleanup with no changes in functionality or results label Jun 11, 2026
@theresa-cordero theresa-cordero self-requested a review June 24, 2026 13:58
@Hallberg-NOAA

Copy link
Copy Markdown
Member

I have carefully examined this sequence of commits, and I agree with the strategy behind them and that they are correctly implemented. However, we will need to test these changes with our OBC and tracer-heavy test cases before this commit could be accepted.

@Hallberg-NOAA Hallberg-NOAA added the Parameter change Input parameter changes (addition, removal, or description) label Jun 24, 2026
call get_param(param_file, mdl, "LOCK_BGC_OBC_TO_DT_TRACER", OBC%lock_bgc_obc_period, &
"If true, BGC OBC segment data is updated every tracer advection step, "//&
"ignoring DT_OBC_SEG_UPDATE_OBGC. This is a preview of a future state in "//&
"which DT_OBC_SEG_UPDATE_OBGC is deprecated.", default=.false.)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find the sentence This is a preview of a future state in which DT_OBC_SEG_UPDATE_OBGC is deprecated. unclear. I think this description should clearly state when the BGC OBC segment data is updated if the flag is true (as it does) and when it is updated when it is false, I believe that would be every dynamic timestep.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree and I admit this parameter itself is a bit awkward. Probably because I am jumping the gun here. Ideally, this dynamic/tracer split refactor would come more naturally, after the potentially problematic parameter DT_OBC_SEG_UPDATE_OBGC is deprecated. (hence the "preview" in the description)

type(thermo_var_ptrs), intent(in) :: tv !< Thermodynamics structure
real, dimension(SZI_(G),SZJ_(G),SZK_(GV)), intent(in) :: h !< Thickness [H ~> m or kg m-2]
type(time_type), intent(in) :: Time !< Model time
!> Read and remap segment data for a single field index m. This is the shared per-field worker

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call this a subroutine not a 'worker'

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh my, of course not. Thanks for catching this!

@herrwang0

herrwang0 commented Jun 27, 2026

Copy link
Copy Markdown
Author

The parameter LOCK_BGC_OBC_TO_DT_TRACER is an odd one. I would be happy to get rid of it, i.e. keeping the True route (there would be no answer change), in which case this PR would also make more sense.

There are two premises, though,

  1. DT_OBC_SEG_UPDATE_OBGC, which is potentially problematic if it is not set to DT_TRACER_ADVECT, is fully deprecated.
  2. The new update schedule does not adversely affect performance (in theory, there should be a marginal improvement).

I am open to suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Parameter change Input parameter changes (addition, removal, or description) refactor Code cleanup with no changes in functionality or results

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants