# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67abstract type AbstractTree{NDIMS} <: AbstractContainer end89# Type traits to obtain dimension10@inline Base.ndims(::AbstractTree{NDIMS}) where {NDIMS} = NDIMS1112# Auxiliary methods to allow semantic queries on the tree13# Check whether cell has parent cell14has_parent(t::AbstractTree, cell_id::Int) = t.parent_ids[cell_id] > 01516# Count number of children for a given cell17function n_children(t::AbstractTree, cell_id::Int)18count(x -> (x > 0), @view t.child_ids[:, cell_id])19end2021# Check whether cell has any child cell22has_children(t::AbstractTree, cell_id::Int) = n_children(t, cell_id) > 02324# Check whether cell is leaf cell25is_leaf(t::AbstractTree, cell_id::Int) = !has_children(t, cell_id)2627# Check whether cell has specific child cell28has_child(t::AbstractTree, cell_id::Int, child::Int) = t.child_ids[child, cell_id] > 02930# Check if cell has a neighbor at the same refinement level in the given direction31function has_neighbor(t::AbstractTree, cell_id::Int, direction::Int)32t.neighbor_ids[direction, cell_id] > 033end3435# Check if cell has a coarse neighbor, i.e., with one refinement level lower36function has_coarse_neighbor(t::AbstractTree, cell_id::Int, direction::Int)37return has_parent(t, cell_id) && has_neighbor(t, t.parent_ids[cell_id], direction)38end3940# Check if cell has any neighbor (same-level or lower-level)41function has_any_neighbor(t::AbstractTree, cell_id::Int, direction::Int)42return has_neighbor(t, cell_id, direction) ||43has_coarse_neighbor(t, cell_id, direction)44end4546# Check if cell is own cell, i.e., belongs to this MPI rank47is_own_cell(t::AbstractTree, cell_id) = true4849# Return cell length for a given level50# We know that the `level` is at least 0, so we can safely use `1 << level`51# instead of `2^level` to calculate the cell length.52length_at_level(t::AbstractTree, level::Int) = t.length_level_0 / (1 << level)5354# Return cell length for a given cell55length_at_cell(t::AbstractTree, cell_id::Int) = length_at_level(t, t.levels[cell_id])5657# Return minimum level of any leaf cell58minimum_level(t::AbstractTree) = minimum(t.levels[leaf_cells(t)])5960# Return maximum level of any leaf cell61maximum_level(t::AbstractTree) = maximum(t.levels[leaf_cells(t)])6263# Check if tree is periodic64isperiodic(t::AbstractTree) = all(t.periodicity)65isperiodic(t::AbstractTree, dimension) = t.periodicity[dimension]6667# Auxiliary methods for often-required calculations68# Number of potential child cells69n_children_per_cell(::AbstractTree{NDIMS}) where {NDIMS} = 2^NDIMS7071# Number of directions72#73# Directions are indicated by numbers from 1 to 2*ndims:74# 1 -> -x75# 2 -> +x76# 3 -> -y77# 4 -> +y78# 5 -> -z79# 6 -> +z80@inline n_directions(::AbstractTree{NDIMS}) where {NDIMS} = 2 * NDIMS81# TODO: Taal performance, 1:n_directions(tree) vs. Base.OneTo(n_directions(tree)) vs. SOneTo(n_directions(tree))82"""83eachdirection(tree::AbstractTree)8485Return an iterator over the indices that specify the location in relevant data structures86for the directions in `AbstractTree`.87In particular, not the directions themselves are returned.88"""89@inline eachdirection(tree::AbstractTree) = Base.OneTo(n_directions(tree))9091# For a given direction, return its opposite direction92#93# dir -> opp94# 1 -> 295# 2 -> 196# 3 -> 497# 4 -> 398# 5 -> 699# 6 -> 5100opposite_direction(direction::Int) = direction + 1 - 2 * ((direction + 1) % 2)101102# For a given child position (from 1 to 8) and dimension (from 1 to 3),103# calculate a child cell's position relative to its parent cell.104#105# Essentially calculates the following106# dim=1 dim=2 dim=3107# child x y z108# 1 - - -109# 2 + - -110# 3 - + -111# 4 + + -112# 5 - - +113# 6 + - +114# 7 - + +115# 8 + + +116# child_sign(child::Int, dim::Int) = 1 - 2 * (div(child + 2^(dim - 1) - 1, 2^(dim-1)) % 2)117# Since we use only a fixed number of dimensions, we use a lookup table for improved performance.118const _child_signs = [-1 -1 -1;119+1 -1 -1;120-1 +1 -1;121+1 +1 -1;122-1 -1 +1;123+1 -1 +1;124-1 +1 +1;125+1 +1 +1]126child_sign(child::Int, dim::Int) = _child_signs[child, dim]127128# For each child position (1 to 8) and a given direction (from 1 to 6), return129# neighboring child position.130const _adjacent_child_ids = [2 2 3 3 5 5;1311 1 4 4 6 6;1324 4 1 1 7 7;1333 3 2 2 8 8;1346 6 7 7 1 1;1355 5 8 8 2 2;1368 8 5 5 3 3;1377 7 6 6 4 4]138adjacent_child(child::Int, direction::Int) = _adjacent_child_ids[child, direction]139140# For each child position (1 to 8) and a given direction (from 1 to 6), return141# if neighbor is a sibling142function has_sibling(child::Int, direction::Int)143return (child_sign(child, div(direction + 1, 2)) * (-1)^(direction - 1)) > 0144end145146# Obtain leaf cells that fulfill a given criterion.147#148# The function `f` is passed the cell id of each leaf cell149# as an argument.150function filter_leaf_cells(f, t::AbstractTree)151filtered = Vector{Int}(undef, length(t))152count = 0153for cell_id in 1:length(t)154if is_leaf(t, cell_id) && f(cell_id)155count += 1156filtered[count] = cell_id157end158end159160return filtered[1:count]161end162163# Return an array with the ids of all leaf cells164leaf_cells(t::AbstractTree) = filter_leaf_cells((cell_id) -> true, t)165166# Return an array with the ids of all leaf cells for a given rank167leaf_cells_by_rank(t::AbstractTree, rank) = leaf_cells(t)168169# Return an array with the ids of all local leaf cells170local_leaf_cells(t::AbstractTree) = leaf_cells(t)171172# Count the number of leaf cells.173count_leaf_cells(t::AbstractTree) = length(leaf_cells(t))174175@inline function cell_coordinates(t::AbstractTree{NDIMS}, cell) where {NDIMS}176SVector(ntuple(d -> t.coordinates[d, cell], Val(NDIMS)))177end178179@inline function set_cell_coordinates!(t::AbstractTree{NDIMS}, coords,180cell) where {NDIMS}181for d in 1:NDIMS182t.coordinates[d, cell] = coords[d]183end184end185186# Determine if point is located inside cell187@inline function is_point_in_cell(t::AbstractTree, point_coordinates, cell_id)188cell_length = length_at_cell(t, cell_id)189cell_coordinates_ = cell_coordinates(t, cell_id)190min_coordinates = cell_coordinates_ .- cell_length / 2191max_coordinates = cell_coordinates_ .+ cell_length / 2192193return all(min_coordinates .<= point_coordinates .<= max_coordinates)194end195196# Store cell id in each cell to use for post-AMR analysis197function reset_original_cell_ids!(t::AbstractTree)198t.original_cell_ids[1:length(t)] .= 1:length(t)199end200201# Efficiently perform uniform refinement up to a given level (works only on mesh with a single cell)202function refine_uniformly!(t::AbstractTree, max_level)203@assert length(t)==1 "efficient uniform refinement only works for a newly created tree"204@assert max_level>=0 "the uniform refinement level must be non-zero"205206# Calculate size of final tree and resize tree207total_length = 1208for level in 1:max_level209total_length += n_children_per_cell(t)^level210end211resize!(t, total_length)212213# Traverse tree to set parent-child relationships214init_children!(t, 1, max_level)215216# Set all neighbor relationships217init_neighbors!(t, max_level)218end219220# Recursively initialize children up to level `max_level` in depth-first ordering, starting with221# cell `cell_id` and set all information except neighbor relations (see `init_neighbors!`).222#223# Return the number of offspring of the initialized cell plus one224function init_children!(t::AbstractTree, cell_id, max_level)225# Stop recursion if max_level has been reached226if t.levels[cell_id] >= max_level227return 1228else229# Initialize each child cell, counting the total number of offspring230n_offspring = 0231for child in 1:n_children_per_cell(t)232# Get cell id of child233child_id = cell_id + 1 + n_offspring234235# Initialize child cell (except neighbors)236init_child!(t, cell_id, child, child_id)237238# Recursively initialize child cell239n_offspring += init_children!(t, child_id, max_level)240end241242return n_offspring + 1243end244end245246# Iteratively set all neighbor relations, starting at an initialized level 0 cell. Assume that247# parent-child relations have already been initialized (see `init_children!`).248function init_neighbors!(t::AbstractTree, max_level = maximum_level(t))249@assert all(n >= 0 for n in t.neighbor_ids[:, 1]) "level 0 cell neighbors must be initialized"250251# Initialize neighbors level by level252for level in 1:max_level253# Walk entire tree, starting from level 0 cell254for cell_id in 1:length(t)255# Skip cells whose immediate children are already initialized *or* whose level is too high for this round256if t.levels[cell_id] != level - 1257continue258end259260# Iterate over children and set neighbor information261for child in 1:n_children_per_cell(t)262child_id = t.child_ids[child, cell_id]263init_child_neighbors!(t, cell_id, child, child_id)264end265end266end267268return nothing269end270271# Initialize the neighbors of child cell `child_id` based on parent cell `cell_id`272function init_child_neighbors!(t::AbstractTree, cell_id, child, child_id)273t.neighbor_ids[:, child_id] .= zero(eltype(t.neighbor_ids))274for direction in eachdirection(t)275# If neighbor is a sibling, establish one-sided connectivity276# Note: two-sided is not necessary, as each sibling will do this277if has_sibling(child, direction)278adjacent = adjacent_child(child, direction)279neighbor_id = t.child_ids[adjacent, cell_id]280281t.neighbor_ids[direction, child_id] = neighbor_id282continue283end284285# Skip if original cell does have no neighbor in direction286if !has_neighbor(t, cell_id, direction)287continue288end289290# Otherwise, check if neighbor has children - if not, skip again291neighbor_id = t.neighbor_ids[direction, cell_id]292if !has_children(t, neighbor_id)293continue294end295296# Check if neighbor has corresponding child and if yes, establish connectivity297adjacent = adjacent_child(child, direction)298if has_child(t, neighbor_id, adjacent)299neighbor_child_id = t.child_ids[adjacent, neighbor_id]300opposite = opposite_direction(direction)301302t.neighbor_ids[direction, child_id] = neighbor_child_id303t.neighbor_ids[opposite, neighbor_child_id] = child_id304end305end306307return nothing308end309310# Refine given cells without rebalancing tree.311#312# Note: After a call to this method the tree may be unbalanced!313function refine_unbalanced!(t::AbstractTree, cell_ids,314sorted_unique_cell_ids = sort(unique(cell_ids)))315# Store actual ids refined cells (shifted due to previous insertions)316refined = zeros(Int, length(cell_ids))317318# Loop over all cells that are to be refined319for (count, original_cell_id) in enumerate(sorted_unique_cell_ids)320# Determine actual cell id, taking into account previously inserted cells321n_children = n_children_per_cell(t)322cell_id = original_cell_id + (count - 1) * n_children323refined[count] = cell_id324325@assert !has_children(t, cell_id) "Non-leaf cell $cell_id cannot be refined"326327# Insert new cells directly behind parent (depth-first)328insert!(t, cell_id + 1, n_children)329330# Flip sign of refined cell such that we can easily find it later331t.original_cell_ids[cell_id] = -t.original_cell_ids[cell_id]332333# Initialize child cells (except neighbors)334for child in 1:n_children335child_id = cell_id + child336init_child!(t, cell_id, child, child_id)337end338339# Initialize child cells (only neighbors)340# This separate loop is required since init_child_neighbors requires initialized parent-child341# relationships342for child in 1:n_children343child_id = cell_id + child344init_child_neighbors!(t, cell_id, child, child_id)345end346end347348return refined349end350351# Refine entire tree by one level352function refine!(t::AbstractTree)353cells = @trixi_timeit timer() "collect all leaf cells" leaf_cells(t)354@trixi_timeit timer() "refine!" refine!(t, cells, cells)355end356357# Refine given cells and rebalance tree.358#359# Note 1: Rebalancing is iterative, i.e., neighboring cells are refined if360# otherwise the 2:1 rule would be violated, which can cause more361# refinements.362# Note 2: Rebalancing currently only considers *Cartesian* neighbors, not diagonal neighbors!363function refine!(t::AbstractTree, cell_ids,364sorted_unique_cell_ids = sort(unique(cell_ids)))365# Reset original cell ids such that each cell knows its current id366reset_original_cell_ids!(t)367368# Refine all requested cells369refined = @trixi_timeit timer() "refine_unbalanced!" refine_unbalanced!(t, cell_ids,370sorted_unique_cell_ids)371refinement_count = length(refined)372373# Iteratively rebalance the tree until it does not change anymore374while length(refined) > 0375refined = @trixi_timeit timer() "rebalance!" rebalance!(t, refined)376refinement_count += length(refined)377end378379# Determine list of *original* cell ids that were refined380# Note: original_cell_ids contains the cell_id *before* refinement. At381# refinement, the refined cell's original_cell_ids value has its sign flipped382# to easily find it now.383refined_original_cells = @views(-t.original_cell_ids[1:length(t)][t.original_cell_ids[1:length(t)] .< 0])384385# Check if count of refinement cells matches information in original_cell_ids386@assert refinement_count==length(refined_original_cells) ("Mismatch in number of refined cells")387388return refined_original_cells389end390391@inline function coordinates_min_max_check(coordinates_min, coordinates_max)392for dim in eachindex(coordinates_min)393@assert coordinates_min[dim]<coordinates_max[dim] "coordinates_min[$dim] must be smaller than coordinates_max[$dim]!"394end395end396# For the p4est and the t8code mesh we allow `coordinates_min` and `coordinates_max` to be `nothing`.397# This corresponds to meshes constructed from analytic mapping functions.398coordinates_min_max_check(::Nothing, ::Nothing) = nothing399400# Refine all leaf cells with coordinates in a given rectangular box401function refine_box!(t::AbstractTree{NDIMS},402coordinates_min,403coordinates_max) where {NDIMS}404coordinates_min_max_check(coordinates_min, coordinates_max)405406# Find all leaf cells within box407cells = filter_leaf_cells(t) do cell_id408return (all(coordinates_min .< cell_coordinates(t, cell_id)) &&409all(coordinates_max .> cell_coordinates(t, cell_id)))410end411412# Refine cells413refine!(t, cells)414end415416# Convenience method for 1D (arguments are no arrays)417function refine_box!(t::AbstractTree{1},418coordinates_min::Real,419coordinates_max::Real)420return refine_box!(t, [coordinates_min], [coordinates_max])421end422423# Refine all leaf cells with coordinates in a given sphere424function refine_sphere!(t::AbstractTree{NDIMS}, center::SVector{NDIMS},425radius) where {NDIMS}426@assert radius>=0 "Radius must be positive."427428# Find all leaf cells within sphere429cells = filter_leaf_cells(t) do cell_id430return sum(abs2, cell_coordinates(t, cell_id) - center) < radius^2431end432433# Refine cells434refine!(t, cells)435end436437# Convenience function to allow passing center as a tuple438function refine_sphere!(t::AbstractTree{NDIMS}, center::NTuple{NDIMS},439radius) where {NDIMS}440refine_sphere!(t, SVector(center), radius)441end442443# For the given cell ids, check if neighbors need to be refined to restore a rebalanced tree.444#445# Note 1: Rebalancing currently only considers *Cartesian* neighbors, not diagonal neighbors!446# Note 2: The current algorithm assumes that a previous refinement step has447# created level differences of at most 2. That is, before the previous448# refinement step, the tree was balanced.449function rebalance!(t::AbstractTree, refined_cell_ids)450# Create buffer for newly refined cells451to_refine = zeros(Int, n_directions(t) * length(refined_cell_ids))452count = 0453454# Iterate over cell ids that have previously been refined455for cell_id in refined_cell_ids456# Go over all potential neighbors of child cell457for direction in eachdirection(t)458# Continue if refined cell has a neighbor in that direction459if has_neighbor(t, cell_id, direction)460continue461end462463# Continue if refined cell has no coarse neighbor, since that would464# mean it there is no neighbor in that direction at all (domain465# boundary)466if !has_coarse_neighbor(t, cell_id, direction)467continue468end469470# Otherwise, the coarse neighbor exists and is not refined, thus it must471# be marked for refinement472coarse_neighbor_id = t.neighbor_ids[direction, t.parent_ids[cell_id]]473count += 1474to_refine[count] = coarse_neighbor_id475end476end477478# Finally, refine all marked cells...479refined = refine_unbalanced!(t, unique(to_refine[1:count]))480481# ...and return list of refined cells482return refined483end484485# Refine given cells without rebalancing tree.486#487# Note: After a call to this method the tree may be unbalanced!488# function refine_unbalanced!(t::AbstractTree, cell_ids) end489490# Wrap single-cell refinements such that `sort(...)` does not complain491refine_unbalanced!(t::AbstractTree, cell_id::Int) = refine_unbalanced!(t, [cell_id])492493# Coarsen entire tree by one level494function coarsen!(t::AbstractTree)495# Special case: if there is only one cell (root), there is nothing to do496if length(t) == 1497return Int[]498end499500# Get list of unique parent ids for all leaf cells501parent_ids = unique(t.parent_ids[leaf_cells(t)])502coarsen!(t, parent_ids)503end504505# Coarsen given *parent* cells (= these cells must have children who are all506# leaf cells) while retaining a balanced tree.507#508# A cell to be coarsened might cause an unbalanced tree if the neighboring cell509# was already refined. Since it is generally not desired that cells are510# coarsened without specifically asking for it, these cells will then *not* be511# coarsened.512function coarsen!(t::AbstractTree, cell_ids::AbstractArray{Int})513# Return early if array is empty514if length(cell_ids) == 0515return Int[]516end517518# Reset original cell ids such that each cell knows its current id519reset_original_cell_ids!(t)520521# To maximize the number of cells that may be coarsened, start with the cells at the highest level522sorted_by_level = sort(cell_ids, by = i -> t.levels[i])523524# Keep track of number of cells that were actually coarsened525n_coarsened = 0526527# Local function to adjust cell ids after some cells have been removed528function adjust_cell_ids!(cell_ids, coarsened_cell_id, count)529for (id, cell_id) in enumerate(cell_ids)530if cell_id > coarsened_cell_id531cell_ids[id] = cell_id - count532end533end534end535536# Iterate backwards over cells to coarsen537while true538# Retrieve next cell or quit539if length(sorted_by_level) > 0540coarse_cell_id = pop!(sorted_by_level)541else542break543end544545# Ensure that cell has children (violation is an error)546if !has_children(t, coarse_cell_id)547error("cell is leaf and cannot be coarsened to: $coarse_cell_id")548end549550# Ensure that all child cells are leaf cells (violation is an error)551for child in 1:n_children_per_cell(t)552if has_child(t, coarse_cell_id, child)553if !is_leaf(t, t.child_ids[child, coarse_cell_id])554error("cell $coarse_cell_id has child cell at position $child that is not a leaf cell")555end556end557end558559# Check if coarse cell has refined neighbors that would prevent coarsening560skip = false561# Iterate over all children (which are to be removed)562for child in 1:n_children_per_cell(t)563# Continue if child does not exist564if !has_child(t, coarse_cell_id, child)565continue566end567child_id = t.child_ids[child, coarse_cell_id]568569# Go over all neighbors of child cell. If it has a neighbor that is *not*570# a sibling and that is not a leaf cell, we cannot coarsen its parent571# without creating an unbalanced tree.572for direction in eachdirection(t)573# Continue if neighbor would be a sibling574if has_sibling(child, direction)575continue576end577578# Continue if child cell has no neighbor in that direction579if !has_neighbor(t, child_id, direction)580continue581end582neighbor_id = t.neighbor_ids[direction, child_id]583584if !has_children(t, neighbor_id)585continue586end587588# If neighbor is not a sibling, is existing, and has children, do not coarsen589skip = true590break591end592end593# Skip if a neighboring cell prevents coarsening594if skip595continue596end597598# Flip sign of cell to be coarsened to such that we can easily find it599t.original_cell_ids[coarse_cell_id] = -t.original_cell_ids[coarse_cell_id]600601# If a coarse cell has children that are all leaf cells, they must follow602# immediately due to depth-first ordering of the tree603count = n_children(t, coarse_cell_id)604@assert count==n_children_per_cell(t) "cell $coarse_cell_id does not have all child cells"605remove_shift!(t, coarse_cell_id + 1, coarse_cell_id + count)606607# Take into account shifts in tree that alters cell ids608adjust_cell_ids!(sorted_by_level, coarse_cell_id, count)609610# Keep track of number of coarsened cells611n_coarsened += 1612end613614# Determine list of *original* cell ids that were coarsened to615# Note: original_cell_ids contains the cell_id *before* coarsening. At616# coarsening, the coarsened parent cell's original_cell_ids value has its sign flipped617# to easily find it now.618@views coarsened_original_cells = (-t.original_cell_ids[1:length(t)][t.original_cell_ids[1:length(t)] .< 0])619620# Check if count of coarsened cells matches information in original_cell_ids621@assert n_coarsened==length(coarsened_original_cells) ("Mismatch in number of coarsened cells")622623return coarsened_original_cells624end625626# Wrap single-cell coarsening such that `sort(...)` does not complain627coarsen!(t::AbstractTree, cell_id::Int) = coarsen!(t::AbstractTree, [cell_id])628629# Coarsen all viable parent cells with coordinates in a given rectangular box630function coarsen_box!(t::AbstractTree{NDIMS},631coordinates_min,632coordinates_max) where {NDIMS}633for dim in 1:NDIMS634@assert coordinates_min[dim]<coordinates_max[dim] "Minimum coordinates are not minimum."635end636637# Find all leaf cells within box638leaves = filter_leaf_cells(t) do cell_id639return (all(coordinates_min .< cell_coordinates(t, cell_id)) &&640all(coordinates_max .> cell_coordinates(t, cell_id)))641end642643# Get list of unique parent ids for all leaf cells644parent_ids = unique(t.parent_ids[leaves])645646# Filter parent ids to be within box647parents = filter(parent_ids) do cell_id648return (all(coordinates_min .< cell_coordinates(t, cell_id)) &&649all(coordinates_max .> cell_coordinates(t, cell_id)))650end651652# Coarsen cells653coarsen!(t, parents)654end655656# Convenience method for 1D (arguments are no arrays)657function coarsen_box!(t::AbstractTree{1},658coordinates_min::Real,659coordinates_max::Real)660return coarsen_box!(t, [coordinates_min], [coordinates_max])661end662663# Return coordinates of a child cell based on its relative position to the parent.664function child_coordinates(::AbstractTree{NDIMS}, parent_coordinates,665parent_length::Number, child::Int) where {NDIMS}666# Calculate length of child cells667child_length = parent_length / 2668return SVector(ntuple(d -> parent_coordinates[d] +669child_sign(child, d) * child_length / 2, Val(NDIMS)))670end671672# Reset range of cells to values that are prone to cause errors as soon as they are used.673#674# Rationale: If an invalid cell is accidentally used, we want to know it as soon as possible.675# function invalidate!(t::AbstractTree, first::Int, last::Int) end676invalidate!(t::AbstractTree, id::Int) = invalidate!(t, id, id)677invalidate!(t::AbstractTree) = invalidate!(t, 1, length(t))678679# Delete connectivity with parents/children/neighbors before cells are erased680function delete_connectivity!(t::AbstractTree, first::Int, last::Int)681@assert first > 0682@assert first <= last683@assert last <= t.capacity + 1684685# Iterate over all cells686for cell_id in first:last687# Delete connectivity from parent cell688if has_parent(t, cell_id)689parent_id = t.parent_ids[cell_id]690for child in 1:n_children_per_cell(t)691if t.child_ids[child, parent_id] == cell_id692t.child_ids[child, parent_id] = 0693break694end695end696end697698# Delete connectivity from child cells699for child in 1:n_children_per_cell(t)700if has_child(t, cell_id, child)701t.parent_ids[t._child_ids[child, cell_id]] = 0702end703end704705# Delete connectivity from neighboring cells706for direction in eachdirection(t)707if has_neighbor(t, cell_id, direction)708t.neighbor_ids[opposite_direction(direction), t.neighbor_ids[direction, cell_id]] = 0709end710end711end712end713714# Move connectivity with parents/children/neighbors after cells have been moved715function move_connectivity!(t::AbstractTree, first::Int, last::Int, destination::Int)716@assert first > 0717@assert first <= last718@assert last <= t.capacity + 1719@assert destination > 0720@assert destination <= t.capacity + 1721722# Strategy723# 1) Loop over moved cells (at target location)724# 2) Check if parent/children/neighbors connections are to a cell that was moved725# a) if cell was moved: apply offset to current cell726# b) if cell was not moved: go to connected cell and update connectivity there727728offset = destination - first729has_moved(n) = (first <= n <= last)730731for source in first:last732target = source + offset733734# Update parent735if has_parent(t, target)736# Get parent cell737parent_id = t.parent_ids[target]738if has_moved(parent_id)739# If parent itself was moved, just update parent id accordingly740t.parent_ids[target] += offset741else742# If parent was not moved, update its corresponding child id743for child in 1:n_children_per_cell(t)744if t.child_ids[child, parent_id] == source745t.child_ids[child, parent_id] = target746end747end748end749end750751# Update children752for child in 1:n_children_per_cell(t)753if has_child(t, target, child)754# Get child cell755child_id = t.child_ids[child, target]756if has_moved(child_id)757# If child itself was moved, just update child id accordingly758t.child_ids[child, target] += offset759else760# If child was not moved, update its parent id761t.parent_ids[child_id] = target762end763end764end765766# Update neighbors767for direction in eachdirection(t)768if has_neighbor(t, target, direction)769# Get neighbor cell770neighbor_id = t.neighbor_ids[direction, target]771if has_moved(neighbor_id)772# If neighbor itself was moved, just update neighbor id accordingly773t.neighbor_ids[direction, target] += offset774else775# If neighbor was not moved, update its opposing neighbor id776t.neighbor_ids[opposite_direction(direction), neighbor_id] = target777end778end779end780end781end782783# Raw copy operation for ranges of cells.784#785# This method is used by the higher-level copy operations for AbstractContainer786# function raw_copy!(target::AbstractTree, source::AbstractTree, first::Int, last::Int, destination::Int) end787788# Reset data structures by recreating all internal storage containers and invalidating all elements789# function reset_data_structures!(t::AbstractTree{NDIMS}) where NDIMS end790791end # @muladd792793794