Add positivity-limiter to the AMRCallback#2396
Add positivity-limiter to the AMRCallback#2396patrickersing wants to merge 40 commits intotrixi-framework:mainfrom
AMRCallback#2396Conversation
Review checklistThis checklist is meant to assist creators of PRs (to let them know what reviewers will typically look for) and reviewers (to guide them in a structured review process). Items do not need to be checked explicitly for a PR to be eligible for merging. Purpose and scope
Code quality
Documentation
Testing
Performance
Verification
Created with ❤️ by the Trixi.jl community. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #2396 +/- ##
========================================
Coverage 97.08% 97.09%
========================================
Files 612 612
Lines 47668 47906 +238
========================================
+ Hits 46277 46510 +233
- Misses 1391 1396 +5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
andrewwinters5000
left a comment
There was a problem hiding this comment.
Thanks for add this generic capability! It is much better than my hotfix from TrixiSW. My main question regards T8codeMesh
Co-authored-by: Andrew Winters <andrew.ross.winters@liu.se>
|
Does it makes sense to call the limiter after refinement as well and not only after coarsening? Maybe make this also optional with a boolean variable? Normally, coarsening should be the troublemaker |
There is no guarantee on that the sign of values after the refinement (interpolation) or coarsening (L2 projection in this case) when the solution is near vacuum. |
For the |
|
Right now this only ensures positivity after the coarsening / refine step. I think in order to fix #2064 we would also need to add positivity-limiting after the mortar projection in For the specialized SWE implementation in trixi-framework/TrixiShallowWater.jl#97 this additional limiting should not be necessary as we obtain positive values from the reconstruction strategy. |
True - but this PR itself is already an improvement compared to the existing code. One option would be that we add the I add this to the agenda of Tuesday's Trixi meeting. |
Passing the limiter to the mortar would also be my favored option. If we do this, the limiter would be available to both the Passing it only once reduces the risk that someone forgets to set the limiter at both places. On the other hand passing it explicitly to the |
|
An option could be to store the limiter inside the mortar similar to how it was drafted for the EC mortars. struct LobattoLegendrePositivityPreservingMortarL2{RealT <: Real, NNODES,
ForwardMatrix <: AbstractMatrix{RealT},
ReverseMatrix <: AbstractMatrix{RealT}, Limiter} <:
AbstractMortarL2{RealT}
forward_upper::ForwardMatrix
forward_lower::ForwardMatrix
reverse_upper::ReverseMatrix
reverse_lower::ReverseMatrix
limiter::Limiter
end |
|
@patrickersing so what I would suggest is to add a limiter to a mortar first and then see if we can extract the limter from the mortar (via |
That sounds like a good plan! If we manage to set the mortar limiter by default, but keep the flexibility to pass different limiters to the AMR callback that would be great. Going forward I would then keep this PR dedicated to the AMR callback, and create a separate PR to implement the new mortar type. |
|
I'm currently working on IDP limiting at mortars, and I'm interested in using the Shu limiter to ensure positivity when transferring the solution while refinement and coarsening. So, I only need this positivity limiter for AMR.
|
Interesting, this sounds like AMR is happening " too late" in some sense.
What do you mean by "simultaneous shifting"? |
With "initialization of the amr callback" I really mean the refinement at the very start of the simulation to get to
I added a remark above to clarify this. Simultaneous shifting is when I use the same (minimum) theta for all just refined child elements:
|
Ah I somehow missed that you were really talking about the init, my bad. Thanks for the explanations, though 👍 |
|
Thanks a lot for taking the initiative on this @bennibolm!
Does this appear with or without the simultaneous shifting technique? I also think without any fixes this is to be expected due to the reinterpolation.
So both approaches lose conservation? Do you already have an idea in which step we lose the conservation? How big are the conservation errors that you observe? |
|
My first implementation in patrickersing#47 is ready. Now, I first compute the (nonnegative) mean solution of elements that are going to be refined before the refinement process and then pass that information to the limiter. Then, all the 4 child elements are shifted w.r.t. this mean and with the same theta. |
This is fixed by using the mean value from the original parent element. So before reinterpolation.
This was because I chose a too small initial refinement level at the start. The solution integrals change slightly during the initial refinement process. When I update those numbers after this initialization prozess I get conservation. |
* Implement synchronized shifting for child elements * Fix increase of `element_id` * Revise implementation with mean value of parent element * Add 1D and 3D * Remove if and use dispatching * Decrease theta by eps() * Implement first suggestions * Compute mean within `limiter !== nothing` * Add suggested comment * Restructure call of limiter for refined elements * Implement suggestions * Implement thread parallel version; Add timer * Remove `@assert` * Add function for coarsened elements * Fix dimension * Add auxiliary functions to compute new elements ids * Fix function name
* Apply limiter after refinement/coarsening with T8codeMesh * Implement suggestions
DanielDoehring
left a comment
There was a problem hiding this comment.
What is the current status here? Are we good apart from coverage?
|
Yes, apart from the missing test coverage the feature should be complete. |
Nice, yeah that is what I also recall 👍 |
|
Coming back to this PR, do you think it would be better to first merge the implementation in 2D? There, we would "only" need a simulation where the actual shifting after coarsening is used to reach full coverage. |
Yeah let's carve this out! |
Sounds good! What do we do with the uncovered lines of the 1D / 3D implementation? |
ranocha
left a comment
There was a problem hiding this comment.
As discussed in the Trixi.jl meeting today, please add a simple test for this implementation in all dimensions, e.g., just initializing a discontinuity within one element, refine it, and fix the arsing negative values.
I created simple test cases. But (at least to my understanding) I need to set up equations, solver, semidiscretization to be able to refine and coarsen properly. So, it's not that short anymore (50 lines per dimension or about 85 for all three since many lines can be used for 1D, 2D and 3D). |
Thanks for taking care of this! |
| # Set `limiter! = positivity_limiter` to apply the positivity-preserving limiter after | ||
| # coarsening and refinement steps. | ||
| amr_callback = AMRCallback(semi, amr_controller, | ||
| interval = 2, | ||
| adapt_initial_condition = true, | ||
| adapt_initial_condition_only_refine = true) | ||
| adapt_initial_condition_only_refine = true, | ||
| limiter! = positivity_limiter) |
There was a problem hiding this comment.
I would probably keep only the positivity limiter in the "positivity" elixirs - the other elixirs were running before as well, right?
There was a problem hiding this comment.
So, you would for instance remove it from all elixirs using HennemannGassner shock capturing? In the end, I don't really care. @patrickersing did you add the limiter to all the elixirs with a reason?
There was a problem hiding this comment.
Hm yeah I would maybe keep this at a minimum for coverage, but not sure
There was a problem hiding this comment.
I added the limiter to all elixirs that were already using the PositivityPreservingLimiterZhangShu with AMR. I think it makes sense to keep the AMR limiter for these examples, since the authors anticipated positvitiy issues.
The only exception I noticed are the t8code examples. I think I just added the limiter here for testing purpose. Here, we could remove the limiter and instead introduce a unit test.
All elixirs did run before, though for some we observed positivity violations.
There was a problem hiding this comment.
Sounds reasonable to me. But as I said, I don't have a strong opinion on this.
I think, the positivity issues occurred mostly directly in the beginning when refining the initial condition.
| refined_original_cells, | ||
| limiter!) |
There was a problem hiding this comment.
Somewhat unrelated to this PR, but this will probably also not really be tested.
Actually, I am not sure if this gets tested at all, as for this the Euler Gravity stuff with AMR would need to be called:
Trixi.jl/src/semidiscretization/semidiscretization_euler_gravity.jl
Lines 592 to 609 in ff6ba9b
| function compute_new_ids_refined_elements(elements_to_refine, | ||
| mesh::Union{TreeMesh, P4estMesh}) | ||
| element_ids_new = copy(elements_to_refine) | ||
| for i in eachindex(element_ids_new) | ||
| # Each refined element increases all ids of the following elements by 2^ndims - 1 | ||
| for j in (i + 1):length(element_ids_new) | ||
| element_ids_new[j] += 2^ndims(mesh) - 1 | ||
| end | ||
| end | ||
|
|
||
| return element_ids_new | ||
| end | ||
|
|
||
| # Auxiliary function to compute the new element ids for coarsened elements | ||
| # Used when applying positivity limiter after coarsening step | ||
| function compute_new_ids_coarsened_elements(elements_to_remove, | ||
| mesh::Union{TreeMesh, P4estMesh}) | ||
| @assert length(elements_to_remove) % (2^ndims(mesh))==0 "The length of `elements_to_remove` must be a multiple of 2^ndims(mesh)." | ||
|
|
||
| element_ids_new = zeros(Int, div(length(elements_to_remove), 2^ndims(mesh))) | ||
| for i in eachindex(element_ids_new) | ||
| # New element id is the id of the first child | ||
| # Additionally, all following ids decrease by (2^ndims - 1) per coarsened element | ||
| element_ids_new[i] = elements_to_remove[2^ndims(mesh) * (i - 1) + 1] - | ||
| (2^ndims(mesh) - 1) * (i - 1) | ||
| end | ||
|
|
||
| return element_ids_new | ||
| end |
There was a problem hiding this comment.
I wonder if these could somehow be re-used for the other AMR stuff
This PR introduces the possibility to pass a positivity-preserving limiter to the
AMRCallback. The limiter is applied at the end of thecoarsen!,refine!oradapt!step to ensure positivity after adaptation in the AMR callback. This generalizes the strategy from trixi-framework/TrixiShallowWater.jl#97 for arbitrary equations.This tackles the first part of #2064. To close this issue we also need to implement positivity-limiting after the mortar projection.