sbc: rotate initial wind/stress coefficients on rotated grids#919
Open
koldunovn wants to merge 1 commit into
Open
sbc: rotate initial wind/stress coefficients on rotated grids#919koldunovn wants to merge 1 commit into
koldunovn wants to merge 1 commit into
Conversation
On a rotated grid (rotated_grid=.true.), the geo->rotated (g2r) rotation of the wind/stress interpolation coefficients is applied only in sbc_do, gated on do_rotation_wind which is set only when coefficients are refreshed (inside the getcoeffld branch). The cold-start initialization nc_sbc_ini loads the first coefficients (getcoeffld) and builds the wind (data_timeinterp) but never rotates them. At step 1, force_newcoeff is .false. (no year change) and rdate lies inside the first forcing interval, so sbc_do does not refresh -> the rotation is skipped -> the first forcing window's wind stress on BOTH ocean and ice stays in the geographic frame, mis-directed by the local g2r angle. It self-corrects once the run crosses the first forcing-window boundary. The error is magnitude-preserving (rotation is orthogonal), so it is invisible to |stress|-based diagnostics (u*, turbulent fluxes, SST/SSS/SSH, ice area/volume, ice/ocean speed ratios) and to multi-year climate; only the wind-stress direction is wrong, at cold start. Fix: rotate the initial coefficients in nc_sbc_ini, mirroring the existing sbc_do rotation (guarded by l_xwind/l_xstre .and. rotated_grid). Steady-state behaviour is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On a rotated grid (
rotated_grid = .true., e.g. the standard CORE2 setup), the initial surface-forcing wind/stress interpolation coefficients are never rotated into the rotated-grid frame. As a result, for the first forcing window of a run the wind stress applied to both the ocean and the sea ice stays in the geographic frame and is mis-directed by the local geo→rotated (g2r) angle.Mechanism
The
g2rrotation of the wind/stress is applied only insbc_do(src/gen_surface_forcing.F90), and only when the time-interpolation coefficients are refreshed:The cold-start initialization
nc_sbc_iniloads the very first set of coefficients but never rotates them:At cold start,
force_newcoeffis.false.(no year change) andrdatelies inside the first forcing interval, so the refresh condition insbc_dois not met on the first steps →do_rotation_windstays.false.→ the rotation block is skipped → the wind used (atmdata = rdate*coef_a + coef_b) is the unrotated geographic wind. The rotation only kicks in once the run crosses the first forcing-window boundary andsbc_docallsgetcoeffldagain.Impact
dt = 1800 s, ≈ 6 steps / ≈ 3 model hours).|stress|is unchanged. This is why the bug is invisible to every magnitude-based diagnostic —u*, turbulent fluxes, SST/SSS/SSH, ice area/volume, ice/ocean speed ratios — and to multi-year climate validation. Only the wind-stress direction is wrong, so it perturbs the initial ice drift and surface-current direction for the first few steps.rotated_grid = .true.). Non-rotated setups are unaffected.How it was found
Discovered while bit-comparing a C re-implementation of the FESOM2 forcing/EVP path against this Fortran code at ocean step 1 (CORE2 mesh, JRA55, dt=1800, cold start). Every EVP input was bit-identical except the wind-on-ice stress
stress_atmice, which differed at all surface nodes. The difference was a pure rotation: per-node|stress|matched to 1.0000, while the direction differed by exactly the localg2rvector angle (stress_C = g2r(stress_Fortran), residual 0.0 at every node). Tracing it showed the C code rotated the wind from step 1 while this Fortran code did not, isolating the missing rotation tonc_sbc_ini.Fix
In
nc_sbc_ini, after loading the initial coefficients, apply the sameg2rrotation thatsbc_doalready applies on refresh (guarded byl_xwind/l_xstreandrotated_grid). It is a faithful mirror of the existingsbc_dorotation block, so steady-state behaviour is unchanged; it only fixes the cold-start window.Validation
Compared step-1 wind-on-ice stress (CORE2, JRA55, dt=1800, cold start) against a rotated-frame reference, before vs after the fix:
stress_atmiceC-vs-ref, per nodeMulti-year climate is unchanged (the bug was a bounded transient that self-corrects after the first forcing window), confirming the fix only touches the initial-window forcing direction.