Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement AMR with p4est #618

Merged
merged 27 commits into from
Jun 9, 2021
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a8f8e07
Implement AMR with p4est (needs debugging)
efaulhaber May 27, 2021
4c3fab2
Fix TreeMesh AMR
efaulhaber May 27, 2021
ebd27f6
Fix print_amr_information
efaulhaber May 27, 2021
5350fc5
Fix refinement
efaulhaber May 27, 2021
28bc847
Fix CurvedMesh
efaulhaber May 27, 2021
7ee5a7e
Fix AMR with p4est
efaulhaber May 28, 2021
0f9e1c1
Add general print_amr_information for non-AMR meshes
efaulhaber May 29, 2021
1579c17
Fix boundary conditions with AMR
efaulhaber May 29, 2021
af196c6
Fix AMR on unstructured meshes
efaulhaber May 30, 2021
190ac3e
Add tests for AMR with p4est
efaulhaber Jun 1, 2021
3c94bdb
Use Downloads: download
efaulhaber Jun 1, 2021
78f671a
Improve performance of P4estMesh
efaulhaber Jun 1, 2021
2279806
Prepare PR
efaulhaber Jun 1, 2021
3cabfe9
Fix 2279806
efaulhaber Jun 1, 2021
262e27f
Remove some allocations from count_required functions
efaulhaber Jun 2, 2021
018da92
Remove more allocations
efaulhaber Jun 2, 2021
8b7c0ab
Implement suggestions
efaulhaber Jun 5, 2021
be749ff
Implement more suggestions
efaulhaber Jun 6, 2021
a6ee586
Fix polydeg in show(P4estMesh)
efaulhaber Jun 7, 2021
fbaf92e
Destroy p4est data structures in inner constructor
efaulhaber Jun 8, 2021
ea4d7ff
Update examples/2d/elixir_advection_amr_p4est_unstructured_flag.jl
efaulhaber Jun 8, 2021
975e7ef
Update examples/2d/elixir_advection_p4est_non_conforming_flag.jl
efaulhaber Jun 8, 2021
8ab220e
Make AMR controllers independent of the mesh
efaulhaber Jun 9, 2021
e71913f
Implement suggestions
efaulhaber Jun 9, 2021
5caaa56
Fix e71913f
efaulhaber Jun 9, 2021
6a6a183
Implement suggestions
efaulhaber Jun 9, 2021
f1e342f
Fix 6a6a183
efaulhaber Jun 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions examples/2d/elixir_advection_amr_p4est_unstructured_flag.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

using Downloads: download
using OrdinaryDiffEq
using Trixi

###############################################################################
# semidiscretization of the linear advection equation

advectionvelocity = (0.2, -0.3)
equations = LinearScalarAdvectionEquation2D(advectionvelocity)

initial_condition = initial_condition_gauss

boundary_condition = BoundaryConditionDirichlet(initial_condition)
boundary_conditions = Dict(
:all => boundary_condition
)

solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)

# Deformed rectangle that looks like a waving flag,
# lower and upper faces are sinus curves, left and right are vertical lines.
f1(s) = SVector(-5.0, 5 * s - 5.0)
f2(s) = SVector( 5.0, 5 * s + 5.0)
f3(s) = SVector(5 * s, -5.0 + 5 * sin(0.5 * pi * s))
f4(s) = SVector(5 * s, 5.0 + 5 * sin(0.5 * pi * s))
faces = (f1, f2, f3, f4)

# This creates a mapping that transforms [-1, 1]^2 to the domain with the faces defined above.
# It generally doesn't work for meshes loaded from mesh files because these can be meshes
# of arbitrary domains, but the mesh below is specifically built on the domain [-1, 1]^2.
efaulhaber marked this conversation as resolved.
Show resolved Hide resolved
Trixi.validate_faces(faces)
mapping_flag = Trixi.transfinite_mapping(faces)
efaulhaber marked this conversation as resolved.
Show resolved Hide resolved

# Unstructured mesh with 24 cells of the square domain [-1, 1]^n
mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
mesh_file)

mesh = P4estMesh(mesh_file, polydeg=3,
mapping=mapping_flag,
initial_refinement_level=1)


semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver,
boundary_conditions=boundary_conditions)


###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 10.0)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval=analysis_interval,
extra_analysis_integrals=(entropy,))

alive_callback = AliveCallback(analysis_interval=analysis_interval)

save_restart = SaveRestartCallback(interval=100,
save_final_restart=true)

save_solution = SaveSolutionCallback(interval=100,
save_initial_solution=true,
save_final_solution=true,
solution_variables=cons2prim)

amr_controller = ControllerThreeLevel(semi, IndicatorMax(semi, variable=first),
base_level=1,
med_level=2, med_threshold=0.1,
max_level=3, max_threshold=0.6)
amr_callback = AMRCallback(semi, amr_controller,
interval=5,
adapt_initial_condition=true,
adapt_initial_condition_only_refine=true)

stepsize_callback = StepsizeCallback(cfl=0.7)

callbacks = CallbackSet(summary_callback,
analysis_callback, alive_callback,
save_restart, save_solution,
amr_callback, stepsize_callback);


###############################################################################
# run the simulation

sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=1, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep=false, callback=callbacks);

summary_callback() # print the timer summary
8 changes: 4 additions & 4 deletions examples/2d/elixir_advection_amr_solution_independent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ function IndicatorSolutionIndependent(semi)
return IndicatorSolutionIndependent{typeof(cache)}(cache)
end
function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any,4},
equations, dg, cache;
t, kwargs...)
equations, dg, cache;
t, kwargs...)

mesh = indicator.cache.mesh
alpha = indicator.cache.alpha
Expand Down Expand Up @@ -86,8 +86,8 @@ initial_condition = initial_condition_gauss

solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)

coordinates_min = (-5, -5)
coordinates_max = ( 5, 5)
coordinates_min = (-5.0, -5.0)
coordinates_max = ( 5.0, 5.0)
mesh = TreeMesh(coordinates_min, coordinates_max,
initial_refinement_level=4,
n_cells_max=30_000)
Expand Down
153 changes: 153 additions & 0 deletions examples/2d/elixir_advection_amr_solution_independent_p4est.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@

using OrdinaryDiffEq
using Trixi

# define new structs inside a module to allow re-evaluating the file
module TrixiExtension

using Trixi

struct IndicatorSolutionIndependent{Cache<:NamedTuple} <: Trixi.AbstractIndicator
cache::Cache
end

function IndicatorSolutionIndependent(semi)
basis = semi.solver.basis
alpha = Vector{real(basis)}()
cache = (; semi.mesh, alpha)
return IndicatorSolutionIndependent{typeof(cache)}(cache)
end

function (indicator::IndicatorSolutionIndependent)(u::AbstractArray{<:Any,4},
equations, dg, cache;
t, kwargs...)

mesh = indicator.cache.mesh
alpha = indicator.cache.alpha
resize!(alpha, nelements(dg, cache))

#Predict the theoretical center.
advectionvelocity = (1.0, 1.0)
center = t.*advectionvelocity

inner_distance = 1
outer_distance = 1.85

#Iterate over all elements
for element in 1:length(alpha)
# Calculate periodic distance between cell and center.
# This requires an uncurved mesh!
coordinates = SVector(0.5 * (cache.elements.node_coordinates[1, 1, 1, element] +
cache.elements.node_coordinates[1, end, 1, element]),
0.5 * (cache.elements.node_coordinates[2, 1, 1, element] +
cache.elements.node_coordinates[2, 1, end, element]))

#The geometric shape of the amr should be preserved when the base_level is increased.
#This is done by looking at the original coordinates of each cell.
cell_coordinates = original_coordinates(coordinates, 5/8)
cell_distance = periodic_distance_2d(cell_coordinates, center, 10)
if cell_distance < (inner_distance+outer_distance)/2
cell_coordinates = original_coordinates(coordinates, 5/16)
cell_distance = periodic_distance_2d(cell_coordinates, center, 10)
end

#Set alpha according to cells position inside the circles.
target_level = (cell_distance < inner_distance) + (cell_distance < outer_distance)
alpha[element] = target_level/2
end
return alpha
end

# For periodic domains, distance between two points must take into account
# periodic extensions of the domain
function periodic_distance_2d(coordinates, center, domain_length)
dx = coordinates .- center
dx_shifted = abs.(dx .% domain_length)
dx_periodic = min.(dx_shifted, domain_length .- dx_shifted)
return sqrt(sum(dx_periodic.^2))
end

#This takes a cells coordinates and transforms them into the coordinates of a parent-cell it originally refined from.
#It does it so that the parent-cell has given cell_length.
function original_coordinates(coordinates, cell_length)
offset = coordinates .% cell_length
offset_sign = sign.(offset)
border = coordinates - offset
center = border + (offset_sign .* cell_length/2)
return center
end

end # module TrixiExtension

import .TrixiExtension
###############################################################################
# semidiscretization of the linear advection equation

advectionvelocity = (1.0, 1.0)
# advectionvelocity = (0.2, -0.3)
ranocha marked this conversation as resolved.
Show resolved Hide resolved
equations = LinearScalarAdvectionEquation2D(advectionvelocity)

initial_condition = initial_condition_gauss

solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)

coordinates_min = (-5.0, -5.0)
coordinates_max = ( 5.0, 5.0)

trees_per_dimension = (1, 1)

mesh = P4estMesh(trees_per_dimension, polydeg=3,
coordinates_min=coordinates_min, coordinates_max=coordinates_max,
initial_refinement_level=4)


semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver)


###############################################################################
# ODE solvers, callbacks etc.

tspan = (0.0, 10.0)
ode = semidiscretize(semi, tspan)

summary_callback = SummaryCallback()

analysis_interval = 100
analysis_callback = AnalysisCallback(semi, interval=analysis_interval,
extra_analysis_integrals=(entropy,))

alive_callback = AliveCallback(analysis_interval=analysis_interval)

save_restart = SaveRestartCallback(interval=100,
save_final_restart=true)

save_solution = SaveSolutionCallback(interval=100,
save_initial_solution=true,
save_final_solution=true,
solution_variables=cons2prim)

amr_controller = ControllerThreeLevel(semi, TrixiExtension.IndicatorSolutionIndependent(semi),
base_level=4,
med_level=5, med_threshold=0.1,
max_level=6, max_threshold=0.6)

amr_callback = AMRCallback(semi, amr_controller,
interval=5,
adapt_initial_condition=true,
adapt_initial_condition_only_refine=true)

stepsize_callback = StepsizeCallback(cfl=1.6)

callbacks = CallbackSet(summary_callback,
analysis_callback, alive_callback,
save_restart, save_solution,
amr_callback, stepsize_callback);


###############################################################################
# run the simulation

sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep=false, callback=callbacks);
summary_callback() # print the timer summary
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@ f2(s) = SVector( 1.0, s + 1.0)
f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s))
f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s))

# Create P4estMesh with 3 x 2 trees and 6 x 4 elements
trees_per_dimension = (3, 2)

# Create P4estMesh with 8 x 8 trees and 16 x 16 elements
mesh = P4estMesh(trees_per_dimension, polydeg=3,
faces=(f1, f2, f3, f4),
initial_refinement_level=1)

# Refine bottom left quadrant of each forest to level 4
# Refine bottom left quadrant of each tree to level 4
function refine_fn(p4est, which_tree, quadrant)
if quadrant.x == 0 && quadrant.y == 0 && quadrant.level < 4
# return true (refine)
Expand All @@ -37,7 +36,7 @@ function refine_fn(p4est, which_tree, quadrant)
end
end

# Refine recursively until each bottom left quadrant of a forest has level 4
# Refine recursively until each bottom left quadrant of a tree has level 4
# The mesh will be rebalanced before the simulation starts
refine_fn_c = @cfunction(refine_fn, Cint, (Ptr{Trixi.p4est_t}, Ptr{Trixi.p4est_topidx_t}, Ptr{Trixi.p4est_quadrant_t}))
Trixi.p4est_refine(mesh.p4est, true, refine_fn_c, C_NULL)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

using Downloads: download
using OrdinaryDiffEq
using Trixi


###############################################################################
# semidiscretization of the linear advection equation

advectionvelocity = (1.0, 1.0)
equations = LinearScalarAdvectionEquation2D(advectionvelocity)

initial_condition = initial_condition_convergence_test

boundary_condition = BoundaryConditionDirichlet(initial_condition)
boundary_conditions = Dict(
:all => boundary_condition
)

# Create DG solver with polynomial degree = 3 and (local) Lax-Friedrichs/Rusanov flux as surface flux
solver = DGSEM(polydeg=3, surface_flux=flux_lax_friedrichs)

# Deformed rectangle that looks like a waving flag,
# lower and upper faces are sinus curves, left and right are vertical lines.
f1(s) = SVector(-1.0, s - 1.0)
f2(s) = SVector( 1.0, s + 1.0)
f3(s) = SVector(s, -1.0 + sin(0.5 * pi * s))
f4(s) = SVector(s, 1.0 + sin(0.5 * pi * s))
faces = (f1, f2, f3, f4)

Trixi.validate_faces(faces)
mapping_flag = Trixi.transfinite_mapping(faces)

# Unstructured mesh with 24 cells of the square domain [-1, 1]^n
mesh_file = joinpath(@__DIR__, "square_unstructured_2.inp")
isfile(mesh_file) || download("https://gist.githubusercontent.com/efaulhaber/63ff2ea224409e55ee8423b3a33e316a/raw/7db58af7446d1479753ae718930741c47a3b79b7/square_unstructured_2.inp",
mesh_file)

mesh = P4estMesh(mesh_file, polydeg=3,
mapping=mapping_flag,
initial_refinement_level=0)

# Refine bottom left quadrant of each tree to level 3
function refine_fn(p4est, which_tree, quadrant)
if quadrant.x == 0 && quadrant.y == 0 && quadrant.level < 3
# return true (refine)
return Cint(1)
else
# return false (don't refine)
return Cint(0)
end
end

# Refine recursively until each bottom left quadrant of a tree has level 4
# The mesh will be rebalanced before the simulation starts
refine_fn_c = @cfunction(refine_fn, Cint, (Ptr{Trixi.p4est_t}, Ptr{Trixi.p4est_topidx_t}, Ptr{Trixi.p4est_quadrant_t}))
Trixi.p4est_refine(mesh.p4est, true, refine_fn_c, C_NULL)

# A semidiscretization collects data structures and functions for the spatial discretization
semi = SemidiscretizationHyperbolic(mesh, equations, initial_condition, solver, boundary_conditions=boundary_conditions)


###############################################################################
# ODE solvers, callbacks etc.

# Create ODE problem with time span from 0.0 to 1.0
ode = semidiscretize(semi, (0.0, 0.2));

# At the beginning of the main loop, the SummaryCallback prints a summary of the simulation setup
# and resets the timers
summary_callback = SummaryCallback()

# The AnalysisCallback allows to analyse the solution in regular intervals and prints the results
analysis_callback = AnalysisCallback(semi, interval=100)

# The SaveSolutionCallback allows to save the solution to a file in regular intervals
save_solution = SaveSolutionCallback(interval=100,
solution_variables=cons2prim)

# The StepsizeCallback handles the re-calculcation of the maximum Δt after each time step
stepsize_callback = StepsizeCallback(cfl=1.6)

# Create a CallbackSet to collect all callbacks such that they can be passed to the ODE solver
callbacks = CallbackSet(summary_callback, analysis_callback, save_solution, stepsize_callback)


###############################################################################
# run the simulation

# OrdinaryDiffEq's `solve` method evolves the solution in time and executes the passed callbacks
sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false),
dt=1.0, # solve needs some value here but it will be overwritten by the stepsize_callback
save_everystep=false, callback=callbacks);

# Print the timer summary
summary_callback()
Loading