Skip to content

Add specialized limiter function for AMR coarsen and refine - #164

Merged
andrewwinters5000 merged 25 commits into
trixi-framework:mainfrom
andrewwinters5000:amr-limiter
Jul 14, 2026
Merged

andrewwinters5000 merged 25 commits into
trixi-framework:mainfrom
andrewwinters5000:amr-limiter

Conversation

@andrewwinters5000

Copy link
Copy Markdown
Member

This adds specialized instances of PositivityPreservingLimiterShallowWater for use with the AMR coarsen and refine strategy now used in Trixi as #2396 there has been merged to main. This should fix the failing P4estMesh tests that have AMR and wet/dry.

Moreover, this removes the "band-aid" specialized AMR routines that hard-coded a limiter call that were introduced in #97. As that functionality was marked as experimental, this shouldn't be a breaking change.

@andrewwinters5000
andrewwinters5000 marked this pull request as ready for review July 6, 2026 18:39
@codecov

codecov Bot commented Jul 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.58120% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.07%. Comparing base (8981e2f) to head (9217d43).

Files with missing lines Patch % Lines
...c/callbacks_stage/positivity_shallow_water_dg2d.jl 96.43% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #164      +/-   ##
==========================================
+ Coverage   97.47%   99.07%   +1.60%     
==========================================
  Files         105      104       -1     
  Lines        5565     5576      +11     
==========================================
+ Hits         5424     5524     +100     
+ Misses        141       52      -89     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ranocha

ranocha commented Jul 7, 2026

Copy link
Copy Markdown
Member

Just to get it right: merging trixi-framework/Trixi.jl#2396 broke something in TrixiShallowWater.jl, since it used non-public API, correct? Can you add one of the elixirs affected by this to the downstream tests of TrixiShallowWater.jl that Trixi.jl executes for new PRs, please?

Comment thread test/test_p4est_2d.jl

@ranocha ranocha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks! This might be worth a NEWS.md entry. I will review the changes in more details.

@ranocha ranocha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks, this looks good overall. You also need to require a newer version of Trixi.jl (in Project.toml) including the feature to use limiters for AMR.

Comment on lines +25 to +28
# We compute the value directly with the mean values, as we assume that
# Jensen's inequality holds (e.g. pressure for compressible Euler equations).
value_mean = variable(u_mean, equations)
theta = (value_mean - threshold) / (value_mean - value_min)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TrixiShallowWater.jl limits only the water height, correct? Thus, it would be better to write something like

Suggested change
# We compute the value directly with the mean values, as we assume that
# Jensen's inequality holds (e.g. pressure for compressible Euler equations).
value_mean = variable(u_mean, equations)
theta = (value_mean - threshold) / (value_mean - value_min)
# We compute the value directly with the mean values. In the general
# case, this requires Jensen's inequality, e.g., for the pressure for
# the compressible Euler equations. However, we only need linear
# variables such as the `waterheight` here, where this is satisfied.
value_mean = variable(u_mean, equations)
theta = (value_mean - threshold) / (value_mean - value_min)

Comment on lines +120 to +142
u_node = get_node_vars(u, equations, dg, i, j, new_element_id)

# Cut off velocity in case that the water height is smaller than the threshold

h_node, h_v1_node, h_v2_node, b_node = u_node
h_mean, h_v1_mean, h_v2_mean, _ = u_mean # b_mean is not used as it must not be overwritten

if h_node <= threshold
h_v1_node = zero(eltype(u))
h_v2_node = zero(eltype(u))
h_v1_mean = zero(eltype(u))
h_v2_mean = zero(eltype(u))
end

u_node = SVector(h_node, h_v1_node, h_v2_node, b_node)
u_mean = SVector(h_mean, h_v1_mean, h_v2_mean, b_node)

# When velocities are cut off, the only averaged value is the water height,
# because the velocities are set to zero and this value is passed.
# Otherwise, the velocities are averaged, as well.
# Note that the auxiliary bottom topography variable `b` is never limited.
set_node_vars!(u, theta * u_node + (1 - theta) * u_mean,
equations, dg, i, j, new_element_id)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It looks like there is some duplication of code around inner blocks like this one. Did you check whether it could make sense to put parts like this one into the own (@inlined) functions?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That's a good point. I can double check if there are places where introducing a helper function is warranted.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If you @inline them, there will be no performance penalty.

Comment on lines +68 to +73
# Version used by the AMR callback
function (limiter!::PositivityPreservingLimiterShallowWater)(u, mesh, equations, solver,
cache, args...)
limiter_shallow_water!(u, limiter!.variables, mesh, equations,
solver, cache, args...)
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Something else that just came to my mind: What about the 1D case? As far as I see, only the 2D case is changed. However, this allow the 1D case as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Currently, the AMR in TrixiShallowWater is only available in 2D and only on P4estMesh. This is because we need special projections to and from the mortars to make sure that the simulation stays well-balanced. I never bothered to implement these projections on TreeMesh. That is why I did not modify the 1D variant of the limiters.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You don't need projections in 1D since there are no mortars, are there?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, a fantastic point! In 1D there are no mortars so it would be possible to do AMR with some modified limiters. I can try out such a run and add it to this PR or do a separate 1D PR, depending on how much additional code needs touched.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doing it in another PR would be perfectly fine. It would just be good to have this on the agenda, since some parts of the new code here allow 1D while others are specialized for 2D.

@andrewwinters5000 andrewwinters5000 Jul 8, 2026 •

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It is easy enough to get the AMR up and running on TreeMesh 1D. Although figuring out a good AMR indicator is another task, as you can see in this snapshot, the indicator fires a lot in the dry beach region.
Screenshot 2026-07-08 at 22 11 30

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I will do the 1D version in a different PR.

Comment on lines +127 to +135
if h_node <= threshold
h_v1_node = zero(eltype(u))
h_v2_node = zero(eltype(u))
h_v1_mean = zero(eltype(u))
h_v2_mean = zero(eltype(u))
end

u_node = SVector(h_node, h_v1_node, h_v2_node, b_node)
u_mean = SVector(h_mean, h_v1_mean, h_v2_mean, b_node)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This makes me feel a bit unhappy. If I understand it correctly, the mean value u_mean is computed first and then used to update the values in the child cells. However, the mean value of the momentum is set to zero if the nodal value at some point has h_node <= threshold. Thus, a zero momentum will propagate to other nodes, even with h_node > threshold, if they ocme after a dry node. This is likely not what we want, is it?

A solution could be to avoid overwriting u_mean by instead using something like

Suggested change
if h_node <= threshold
h_v1_node = zero(eltype(u))
h_v2_node = zero(eltype(u))
h_v1_mean = zero(eltype(u))
h_v2_mean = zero(eltype(u))
end
u_node = SVector(h_node, h_v1_node, h_v2_node, b_node)
u_mean = SVector(h_mean, h_v1_mean, h_v2_mean, b_node)
if h_node <= threshold
h_v1_node = zero(eltype(u))
h_v2_node = zero(eltype(u))
h_v1_mean = zero(eltype(u))
h_v2_mean = zero(eltype(u))
end
u_node = SVector(h_node, h_v1_node, h_v2_node, b_node)
u_mean_local = SVector(h_mean, h_v1_mean, h_v2_mean, b_node)

and using u_mean_local instead of u_mean below.

This situation should then be explained in a comment around this assignment. The same kind of observation holds for other cases as well.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Ah, yes. That is a possibility the zero momentum value could propagate beyond the node where we apply the cutoff. I can change and test this out.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Using the u_mean_local to ensure that only the node i,j is affected ended up slightly changing some test values of other wet/dry test cases.

@andrewwinters5000

Copy link
Copy Markdown
Member Author

Just to get it right: merging trixi-framework/Trixi.jl#2396 broke something in TrixiShallowWater.jl, since it used non-public API, correct? Can you add one of the elixirs affected by this to the downstream tests of TrixiShallowWater.jl that Trixi.jl executes for new PRs, please?

Yes, that is what happened. TrixiShallowWater was using a specialized version of coarsen / refine routines that had a "band-aid" hard coded limiter call. With the release of Trixi version 0.16.24 (that is used by the CI testing) the API change caused the TrixiShallowWater tests to then fallback to using the Trixi.coarsen or Trixi.refine routines that do not have the limiter call by default.

The limiting strategy from trixi-framework/Trixi.jl#2396 is the correct approach now that the AMR callback can accept the limiter! as an optional argument and the limiting is done with respect to parent or children mean values.

I will add one of the wet/dry + AMR elixirs to the downstream tests as this is a much tougher test than the fully wet AMR test that is currently in the downstream.

@andrewwinters5000

Copy link
Copy Markdown
Member Author

I have run into an issue that I am unsure the best way to solve. I added one of the AMR + wet/dry tests to test_upstream.jl as this would be run by Trixi.jl for the Downstream CI runs. However, within TrixiShallowWater.jl we use the test_upstream.jl for the Downgrade CI run. But, when downgrading to Trixi v0.15 the AMRCallback does not have the optional limiter! argument and the downgrade test fails.

Should I add logic in test_upstream.jl to only run the test if Trixi is v0.16.24 or higher?

@ranocha ranocha left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks!

Should I add logic in test_upstream.jl to only run the test if Trixi is v0.16.24 or higher?

No, we need to adapt the compatibility requirement for Trixi.jl anyway.

Comment thread docs/Project.toml Outdated
Comment thread test/Project.toml Outdated
Comment thread Project.toml Outdated
Comment thread test/test_upstream.jl Outdated
Comment thread test/test_upstream.jl Outdated
@andrewwinters5000
andrewwinters5000 merged commit f3adf8a into trixi-framework:main Jul 14, 2026
17 of 18 checks passed
@andrewwinters5000

Copy link
Copy Markdown
Member Author

Thanks!

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.

2 participants