@muladd begin
include("abstract_tree.jl")
include("serial_tree.jl")
include("parallel_tree.jl")
get_name(mesh::AbstractMesh) = mesh |> typeof |> nameof |> string
"""
TreeMesh{NDIMS} <: AbstractMesh{NDIMS}
A Cartesian mesh based on trees of hypercubes to support adaptive mesh refinement.
"""
mutable struct TreeMesh{NDIMS, TreeType <: AbstractTree{NDIMS}, RealT <: Real} <:
AbstractMesh{NDIMS}
tree::TreeType
current_filename::String
unsaved_changes::Bool
first_cell_by_rank::OffsetVector{Int, Vector{Int}}
n_cells_by_rank::OffsetVector{Int, Vector{Int}}
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer) where {NDIMS,
TreeType <:
AbstractTree{NDIMS},
RealT <:
Real}
m = new()
m.tree = TreeType(n_cells_max)
m.current_filename = ""
m.unsaved_changes = true
m.first_cell_by_rank = OffsetVector(Int[], 0)
m.n_cells_by_rank = OffsetVector(Int[], 0)
return m
end
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer,
domain_center::AbstractArray{RealT},
domain_length::RealT,
periodicity = true) where {NDIMS,
TreeType <:
AbstractTree{NDIMS},
RealT <: Real}
@assert NDIMS isa Integer && NDIMS > 0
m = new()
m.tree = TreeType(n_cells_max, domain_center, domain_length, periodicity)
m.current_filename = ""
m.unsaved_changes = true
m.first_cell_by_rank = OffsetVector(Int[], 0)
m.n_cells_by_rank = OffsetVector(Int[], 0)
return m
end
end
const TreeMesh1D = TreeMesh{1, TreeType} where {TreeType <: AbstractTree{1}}
const TreeMesh2D = TreeMesh{2, TreeType} where {TreeType <: AbstractTree{2}}
const TreeMesh3D = TreeMesh{3, TreeType} where {TreeType <: AbstractTree{3}}
const SerialTreeMesh{NDIMS} = TreeMesh{NDIMS, <:SerialTree{NDIMS}}
const ParallelTreeMesh{NDIMS} = TreeMesh{NDIMS, <:ParallelTree{NDIMS}}
@inline mpi_parallel(mesh::SerialTreeMesh) = False()
@inline mpi_parallel(mesh::ParallelTreeMesh) = True()
partition!(mesh::SerialTreeMesh) = nothing
function TreeMesh(::Type{TreeType}, args...;
RealT = Float64) where {NDIMS, TreeType <: AbstractTree{NDIMS}}
TreeMesh{NDIMS, TreeType, RealT}(args...)
end
function TreeMesh{1, TreeType, RealT}(n::Int, center::RealT, len::RealT,
periodicity = true) where {
TreeType <:
AbstractTree{1},
RealT <: Real}
return TreeMesh{1, TreeType, RealT}(n, SVector{1, RealT}(center), len, periodicity)
end
function TreeMesh{NDIMS, TreeType, RealT}(n_cells_max::Integer,
domain_center::NTuple{NDIMS, RealT},
domain_length::RealT,
periodicity = true) where {NDIMS,
TreeType <:
AbstractTree{NDIMS},
RealT <: Real}
TreeMesh{NDIMS, TreeType, RealT}(n_cells_max, SVector{NDIMS, RealT}(domain_center),
domain_length, periodicity)
end
function TreeMesh(coordinates_min::NTuple{NDIMS, Real},
coordinates_max::NTuple{NDIMS, Real};
n_cells_max,
periodicity = true,
initial_refinement_level,
refinement_patches = (),
coarsening_patches = (),
RealT = Float64) where {NDIMS}
if !(n_cells_max isa Integer && n_cells_max > 0)
throw(ArgumentError("`n_cells_max` must be a positive integer (provided `n_cells_max = $n_cells_max`)"))
end
if !(initial_refinement_level isa Integer && initial_refinement_level >= 0)
throw(ArgumentError("`initial_refinement_level` must be a non-negative integer (provided `initial_refinement_level = $initial_refinement_level`)"))
end
for i in 1:NDIMS
if !(coordinates_min[i] isa RealT)
@warn "Element $i in `coordinates_min` is not of type $RealT (provided `coordinates_min[$i] = $(coordinates_min[i])`)"
end
if !(coordinates_max[i] isa RealT)
@warn "Element $i in `coordinates_max` is not of type $RealT (provided `coordinates_max[$i] = $(coordinates_max[i])`)"
end
end
coordinates_min_max_check(coordinates_min, coordinates_max)
domain_center = @. convert(RealT, (coordinates_min + coordinates_max) / 2)
domain_length = convert(RealT, coordinates_max[1] - coordinates_min[1])
if !all(coordinates_max[i] - coordinates_min[i] ≈ domain_length for i in 2:NDIMS)
throw(ArgumentError("The TreeMesh domain must be a hypercube (provided `coordinates_max` .- `coordinates_min` = $(coordinates_max .- coordinates_min))"))
end
if mpi_isparallel()
if mpi_isroot() && NDIMS != 2
println(stderr,
"ERROR: The TreeMesh supports parallel execution with MPI only in 2 dimensions")
MPI.Abort(mpi_comm(), 1)
end
TreeType = ParallelTree{NDIMS, RealT}
else
TreeType = SerialTree{NDIMS, RealT}
end
mesh = @trixi_timeit timer() "creation" TreeMesh{NDIMS, TreeType, RealT}(n_cells_max,
domain_center,
domain_length,
periodicity)
initialize!(mesh, initial_refinement_level, refinement_patches, coarsening_patches)
return mesh
end
function initialize!(mesh::TreeMesh, initial_refinement_level,
refinement_patches, coarsening_patches)
@trixi_timeit timer() "initial refinement" refine_uniformly!(mesh.tree,
initial_refinement_level)
@trixi_timeit timer() "refinement patches" for patch in refinement_patches
if patch.type == "box"
refine_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
elseif patch.type == "sphere"
refine_sphere!(mesh.tree, patch.center, patch.radius)
else
error("unknown refinement patch type '$(patch.type)'")
end
end
@trixi_timeit timer() "coarsening patches" for patch in coarsening_patches
if patch.type == "box"
coarsen_box!(mesh.tree, patch.coordinates_min, patch.coordinates_max)
else
error("unknown coarsening patch type '$(patch.type)'")
end
end
partition!(mesh)
return nothing
end
function TreeMesh(coordinates_min::Real, coordinates_max::Real;
kwargs...)
TreeMesh((coordinates_min,), (coordinates_max,); kwargs...)
end
function Base.show(io::IO, mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
print(io, "TreeMesh{", NDIMS, ", ", TreeType, "} with length ", mesh.tree.length)
end
function Base.show(io::IO, ::MIME"text/plain",
mesh::TreeMesh{NDIMS, TreeType}) where {NDIMS, TreeType}
if get(io, :compact, false)
show(io, mesh)
else
setup = [
"center" => mesh.tree.center_level_0,
"length" => mesh.tree.length_level_0,
"periodicity" => mesh.tree.periodicity,
"current #cells" => mesh.tree.length,
"#leaf-cells" => count_leaf_cells(mesh.tree),
"maximum #cells" => mesh.tree.capacity
]
summary_box(io, "TreeMesh{" * string(NDIMS) * ", " * string(TreeType) * "}",
setup)
end
end
@inline Base.ndims(mesh::TreeMesh) = ndims(mesh.tree)
function get_restart_mesh_filename(restart_filename, mpi_parallel::False)
dirname, _ = splitdir(restart_filename)
mesh_file = ""
h5open(restart_filename, "r") do file
mesh_file = read(attributes(file)["mesh_file"])
end
return joinpath(dirname, mesh_file)
end
function total_volume(mesh::TreeMesh)
return mesh.tree.length_level_0^ndims(mesh)
end
isperiodic(mesh::TreeMesh) = isperiodic(mesh.tree)
isperiodic(mesh::TreeMesh, dimension) = isperiodic(mesh.tree, dimension)
Base.real(::TreeMesh{NDIMS, TreeType, RealT}) where {NDIMS, TreeType, RealT} = RealT
include("parallel_tree_mesh.jl")
end