Skip to content

ABL wall models: level-consistent mean quantities on meshes refined at the wall - #2041

Open
hgopalan wants to merge 3 commits into
kynema:mainfrom
hgopalan:claude-abl-wall-refine
Open

hgopalan wants to merge 3 commits into
kynema:mainfrom
hgopalan:claude-abl-wall-refine

Conversation

@hgopalan

Copy link
Copy Markdown
Contributor

Summary

Fixes the level-to-level inconsistency of the ABL wall models on meshes where more than one level touches the wall-modeled boundary (#1752).

The Moeng, Schumann and constant shear-stress models combine the velocity and temperature of the first cell above the wall with their plane averages. The plane averages entered through one MOData object at one reference height (the level-0 half cell by default), while the local values came from the first cell of whichever level owns the wall. On a refined level the first cell sits lower, where the wind is slower and the air is warmer, so the same formula returned a different mean stress and heat flux on each level. For the Moeng model the plane-mean stress on a level whose first cell is at half the reference height is $u_*^2 (2r - 1)$ with $r = \phi_m(z_1)/\phi_m(z_{ref})$, i.e. about 20 % too low for the roughness and heights in #1752; the heat flux is short by the same factor. This is what produced the box-shaped imprint of the refinement region in the time-averaged fields of that issue, and it is independent of the time averaging.

Changes

  • ABLWallFunction now keeps a MOData per level (mo(lev)) on multi-level meshes. Each level carries the plane averages of the wall-adjacent cells it owns (excluding cells covered by a finer level) at its own first-cell height; in heat-flux mode the surface temperature of that level is set so that the mean heat flux equals the specified flux. The friction velocity, Obukhov length and surface heat flux are still computed once from the plane average at ABL.log_law_height, as before.
  • ABLVelWallFunc and ABLTempWallFunc build the shear-stress / heat-flux functor per level from mo(lev). On a single-level mesh mo(lev) is the existing mo(), so single-level results are unchanged (bit-identical on abl_godunov, see below).
  • The mol wall model (ABL.wall_het_model = mol) already evaluated everything at the local cell height and is untouched.
  • Docs: a paragraph under ABL.wall_shear_stress_type describing the per-level treatment.

Test

unit_tests/wind_energy/test_abl_wall_refinement.cpp: an ABL mesh with a level-1 box over half of the wall, logarithmic wind and temperature profiles that are uniform in every plane, wall-model BCs for velocity and temperature with a specified heat flux. It fills the wall-model ghost cells and checks that the mean wall stress over the cells each level owns equals $u_*^2$ along the mean wind and that the mean heat flux equals the specified flux, on both levels, to roundoff, for the moeng and schumann models. With the previous code the level-1 stress is about 30 % low on this mesh and the level-0 stress is off by a few percent through the interpolated plane average, so the test fails on both levels.

Regression tests

Cases with a wall model and a refinement level that touches the wall change (expected): abl_godunov_static_refinement, abl_godunov_static_refinement_rr4. There level 1 covers the whole wall, so the wall stress moves from $u_^2 (2r-1)$ to $u_^2$. Single-level ABL cases are bit-identical. Refined cases whose boxes do not reach the wall are unaffected.

Not in this PR

The multi-level FieldPlaneAveraging interpolates coarse cells next to a non-periodic boundary toward the ghost cell, which for a wall-model BC holds a gradient rather than a value. That is a separate issue and will come as its own PR.

🤖 Generated with Claude Code

…t the wall

The Moeng, Schumann and constant shear-stress models combine the velocity
and temperature of the first cell above the wall with their plane averages.
The averages entered through one MOData object at one reference height
(the level-0 half cell by default) while the local values came from the
first cell of whichever level owns the wall. With a refinement level that
touches the wall, that cell sits lower, so the same formula returned a
plane-mean stress of u*^2 (2 r - 1), r = phi_m(z_1)/phi_m(z_ref), on the
refined level, about 20 % low for typical roughness, and the heat flux was
short by the same factor (kynema#1752).

ABLWallFunction now keeps a MOData per level on multi-level meshes. Each
level carries the plane averages of the wall-adjacent cells it owns, at its
own first-cell height, and in heat-flux mode a surface temperature that
returns the specified flux from that mean state. The friction velocity,
Obukhov length and surface heat flux are still computed once from the
plane average at the reference height. The wall-model functors are built
per level from these data. Single-level meshes use the existing MOData and
are bit-identical.

A unit test with a level-1 box over half of the wall checks that the mean
wall stress and heat flux of every level equal u*^2 and the specified flux;
it fails on both levels with the previous code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
hgopalan and others added 2 commits September 16, 2026 21:21
GCC 13 at -O3 flags a potential null dereference when pushing the box
refinement onto mesh<RefineMesh>(); check the cast like the other tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nvcc rejects device lambdas in private member functions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant