From 6c830d7bef98b4e7647172b883587fcab2a60d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 2 Aug 2021 18:56:02 +0200 Subject: [PATCH 001/143] add lookup tables and helper functions --- src/Adaptivity/AdaptiveCells.jl | 134 ++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/Adaptivity/AdaptiveCells.jl diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl new file mode 100644 index 0000000000..ea6b9f1383 --- /dev/null +++ b/src/Adaptivity/AdaptiveCells.jl @@ -0,0 +1,134 @@ +abstract type AbstractAdaptiveTree{dim,N,M} <: AbstractCell{dim,N,M} end + +# Follow z order, x before y before z for faces, edges and corners +struct Octant{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} + nodes::Vector{Node} +end + +# return the two adjacent faces $f_i$ adjacent to edge `edge` +function _face(edge::Int) + #maybe @view? + return 𝒮[edge, :] +end + +# return the `i`-th adjacent face fᵢ to edge `edge` +function _face(edge::Int, i::Int) + return 𝒮[edge, i] +end + +# return two face corners ξᵢ of the face `face` along edge `edge` +function _face_corners(edge::Int, face::Int) + #maybe @view + return 𝒯[edge,face] +end + +# return the two `edge` corners cᵢ +function _edge_corners(edge::Int) + #maybe @view + return 𝒰[edge,:] +end + +# return the `i`-th edge corner of `edge` +function _edge_corners(edge::Int,i::Int) + return 𝒰[edge,i] +end + +# map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup +function _face_corners(dim::Int,face::Int,ξ::Int) + if dim == 2 + return 𝒱₂[face,ξ] + elseif dim == 3 + return 𝒱₃[face,ξ] + else + error("No corner-lookup table available") + end +end + +function _face_corners(dim::Int,face::Int) + if dim == 2 + return 𝒱₂[face,:] + elseif dim == 3 + return 𝒱₃[face,:] + else + error("No corner-lookup table available") + end +end + +# finds face corner ξ′ in f′ for two associated faces f,f′ in {1,...,6} and their orientation r in {1,...,4}} +function _neighbor_corner(f,f′,r,ξ) + return 𝒫[𝒬[ℛ[f,f′],r],ξ] +end + + +##### OCTANT LOOK UP TABLES ###### +const 𝒮 = [3 5 + 4 5 + 3 6 + 4 6 + 1 5 + 2 5 + 1 6 + 2 6 + 1 3 + 2 3 + 1 4 + 2 4] + +# (0,0) non existing connections +const 𝒯 = [(0, 0) (0, 0) (1, 2) (0, 0) (1, 2) (0, 0) + (0, 0) (0, 0) (0, 0) (1, 2) (3, 4) (0, 0) + (0, 0) (0, 0) (3, 4) (0, 0) (0, 0) (1, 2) + (0, 0) (0, 0) (0, 0) (3, 4) (0, 0) (3, 4) + (1, 2) (0, 0) (0, 0) (0, 0) (1, 3) (0, 0) + (0, 0) (1, 2) (0, 0) (0, 0) (2, 4) (0, 0) + (3, 4) (0, 0) (0, 0) (0, 0) (0, 0) (1, 3) + (0, 0) (3, 4) (0, 0) (0, 0) (0, 0) (2, 4) + (1, 3) (0, 0) (1, 3) (0, 0) (0, 0) (0, 0) + (0, 0) (1, 3) (2, 4) (0, 0) (0, 0) (0, 0) + (2, 4) (0, 0) (0, 0) (1, 3) (0, 0) (0, 0) + (0, 0) (2, 4) (0, 0) (2, 4) (0, 0) (0, 0)] + +const 𝒰 = [1 2 + 3 4 + 5 6 + 7 8 + 1 3 + 2 4 + 5 7 + 6 8 + 1 5 + 2 6 + 3 7 + 4 8] + +const 𝒱₂ = [1 3 + 2 4 + 1 2 + 3 4] + +const 𝒱₃ = [1 3 5 7 + 2 4 6 8 + 1 2 5 6 + 3 4 7 8 + 1 2 3 4 + 5 6 7 8] + +const ℛ = [1 2 2 1 1 2 + 3 1 1 2 2 1 + 3 1 1 2 2 1 + 1 3 3 1 1 2 + 1 3 3 1 1 2 + 3 1 1 3 3 1] + +const 𝒬 = [2 3 6 7 + 1 4 5 8 + 1 5 4 8] + +const 𝒫 = [1 2 3 4 + 1 3 2 4 + 2 1 4 3 + 2 4 1 3 + 3 1 4 2 + 3 4 1 2 + 4 2 3 1 + 4 3 2 1] From b47f930fceabd1c16e84ce2bd2e5db0096cb593f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 2 Aug 2021 21:08:55 +0200 Subject: [PATCH 002/143] clean up and tests for lookup --- src/Adaptivity/AdaptiveCells.jl | 32 ++++++--------------------- src/Ferrite.jl | 3 +++ test/test_octant.jl | 38 +++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 test/test_octant.jl diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index ea6b9f1383..c50af3a775 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -6,32 +6,15 @@ struct Octant{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} end # return the two adjacent faces $f_i$ adjacent to edge `edge` -function _face(edge::Int) - #maybe @view? - return 𝒮[edge, :] -end - +_face(edge::Int) = 𝒮[edge, :] # return the `i`-th adjacent face fᵢ to edge `edge` -function _face(edge::Int, i::Int) - return 𝒮[edge, i] -end - +_face(edge::Int, i::Int) = 𝒮[edge, i] # return two face corners ξᵢ of the face `face` along edge `edge` -function _face_corners(edge::Int, face::Int) - #maybe @view - return 𝒯[edge,face] -end - +_face_edge_corners(edge::Int, face::Int) = 𝒯[edge,face] # return the two `edge` corners cᵢ -function _edge_corners(edge::Int) - #maybe @view - return 𝒰[edge,:] -end - +_edge_corners(edge::Int) = 𝒰[edge,:] # return the `i`-th edge corner of `edge` -function _edge_corners(edge::Int,i::Int) - return 𝒰[edge,i] -end +_edge_corners(edge::Int,i::Int) = 𝒰[edge,i] # map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup function _face_corners(dim::Int,face::Int,ξ::Int) @@ -55,10 +38,7 @@ function _face_corners(dim::Int,face::Int) end # finds face corner ξ′ in f′ for two associated faces f,f′ in {1,...,6} and their orientation r in {1,...,4}} -function _neighbor_corner(f,f′,r,ξ) - return 𝒫[𝒬[ℛ[f,f′],r],ξ] -end - +_neighbor_corner(f::Int,f′::Int,r::Int,ξ::Int) = 𝒫[𝒬[ℛ[f,f′],r],ξ] ##### OCTANT LOOK UP TABLES ###### const 𝒮 = [3 5 diff --git a/src/Ferrite.jl b/src/Ferrite.jl index d8054f97f1..ba07c8dc68 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -51,6 +51,9 @@ include(joinpath("Grid", "grid.jl")) include(joinpath("Grid", "grid_generators.jl")) include(joinpath("Grid", "coloring.jl")) +# Adaptiviy +include(joinpath("Adaptivity", "AdaptiveCells.jl")) + # Dofs include(joinpath("Dofs", "DofHandler.jl")) include(joinpath("Dofs", "MixedDofHandler.jl")) diff --git a/test/test_octant.jl b/test/test_octant.jl new file mode 100644 index 0000000000..b23de6a1b8 --- /dev/null +++ b/test/test_octant.jl @@ -0,0 +1,38 @@ +@testset "Octant Lookup Tables" begin + @test Ferrite._face(1) == [3,5] + @test Ferrite._face(5) == [1,5] + @test Ferrite._face(12) == [2,4] + @test Ferrite._face(1,1) == 3 && Ferrite._face(1,2) == 5 + @test Ferrite._face(5,1) == 1 && Ferrite._face(5,2) == 5 + @test Ferrite._face(12,1) == 2 && Ferrite._face(12,2) == 4 + @test Ferrite._face(3,1) == 3 && Ferrite._face(3,2) == 6 + + @test Ferrite._face_edge_corners(1,1) == (0,0) + @test Ferrite._face_edge_corners(3,3) == (3,4) + @test Ferrite._face_edge_corners(8,6) == (2,4) + @test Ferrite._face_edge_corners(4,5) == (0,0) + @test Ferrite._face_edge_corners(5,4) == (0,0) + @test Ferrite._face_edge_corners(7,1) == (3,4) + @test Ferrite._face_edge_corners(11,1) == (2,4) + @test Ferrite._face_edge_corners(9,1) == (1,3) + @test Ferrite._face_edge_corners(10,2) == (1,3) + @test Ferrite._face_edge_corners(12,2) == (2,4) + + @test Ferrite.𝒱₃[1,:] == Ferrite.𝒰[1:4,1] == Ferrite._face_corners(3,1) + @test Ferrite.𝒱₃[2,:] == Ferrite.𝒰[1:4,2] == Ferrite._face_corners(3,2) + @test Ferrite.𝒱₃[3,:] == Ferrite.𝒰[5:8,1] == Ferrite._face_corners(3,3) + @test Ferrite.𝒱₃[4,:] == Ferrite.𝒰[5:8,2] == Ferrite._face_corners(3,4) + @test Ferrite.𝒱₃[5,:] == Ferrite.𝒰[9:12,1] == Ferrite._face_corners(3,5) + @test Ferrite.𝒱₃[6,:] == Ferrite.𝒰[9:12,2] == Ferrite._face_corners(3,6) + + @test Ferrite._edge_corners(1) == [1,2] + @test Ferrite._edge_corners(4) == [7,8] + @test Ferrite._edge_corners(12,2) == 8 + + #Test Figure 3a) of Burstedde, Wilcox, Ghattas [2011] + test_ξs = (1,2,3,4) + @test Ferrite._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs + #Test Figure 3b) + @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) + +end From 294b4631bed7b190be60d024f66f6b93845d4cde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 3 Aug 2021 16:40:52 +0200 Subject: [PATCH 003/143] added parent and child_id --- src/Adaptivity/AdaptiveCells.jl | 38 ++++++++++++++++++++++++++++----- test/test_octant.jl | 14 +++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index c50af3a775..82a23907d6 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -1,8 +1,37 @@ abstract type AbstractAdaptiveTree{dim,N,M} <: AbstractCell{dim,N,M} end +abstract type AbstractAdaptiveCell{dim,N,M} <: AbstractCell{dim,N,M} end + +struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} + #Refinement level + l::Int + #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} + xyz::NTuple{dim,Int} +end # Follow z order, x before y before z for faces, edges and corners -struct Octant{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} - nodes::Vector{Node} +struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} + leaves::Vector{Octant} + #maximum refinement level + b::Int +end + +function child_id(octant::Octant{3},b::Int) + i = 0 + h = 2^(b - octant.l) + x = octant.xyz[1]; y = octant.xyz[2]; z = octant.xyz[3] + i = i | ((x & h) != 0 ? 1 : 0) + i = i | ((y & h) != 0 ? 2 : 0) + i = i | ((z & h) != 0 ? 4 : 0) + return i+1 +end + +function parent(octant::Octant{3,N,M}, b::Int) where {N,M} + h = 2^(b - octant.l) + l = octant.l - 1 + px = octant.xyz[1] & ~h + py = octant.xyz[2] & ~h + pz = octant.xyz[3] & ~h + return Octant{3,N,M}(l,(px,py,pz) .+ 1) end # return the two adjacent faces $f_i$ adjacent to edge `edge` @@ -15,6 +44,8 @@ _face_edge_corners(edge::Int, face::Int) = 𝒯[edge,face] _edge_corners(edge::Int) = 𝒰[edge,:] # return the `i`-th edge corner of `edge` _edge_corners(edge::Int,i::Int) = 𝒰[edge,i] +# finds face corner ξ′ in f′ for two associated faces f,f′ in {1,...,6} and their orientation r in {1,...,4}} +_neighbor_corner(f::Int,f′::Int,r::Int,ξ::Int) = 𝒫[𝒬[ℛ[f,f′],r],ξ] # map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup function _face_corners(dim::Int,face::Int,ξ::Int) @@ -37,9 +68,6 @@ function _face_corners(dim::Int,face::Int) end end -# finds face corner ξ′ in f′ for two associated faces f,f′ in {1,...,6} and their orientation r in {1,...,4}} -_neighbor_corner(f::Int,f′::Int,r::Int,ξ::Int) = 𝒫[𝒬[ℛ[f,f′],r],ξ] - ##### OCTANT LOOK UP TABLES ###### const 𝒮 = [3 5 4 5 diff --git a/test/test_octant.jl b/test/test_octant.jl index b23de6a1b8..56e1708ed0 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -33,6 +33,18 @@ test_ξs = (1,2,3,4) @test Ferrite._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs #Test Figure 3b) - @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) + @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) +end + +@testset "Octant Encoding" begin + # Tests from Figure 3a) and 3b) of Burstedde et al + o = Ferrite.Octant{3,8,6}(2,(1,5,3)) + b = 3 + @test Ferrite.child_id(o,b) == 5 + @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 + #@test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) FIXME + o = Ferrite.Octant{3,8,6}(2,(3,3,1)) + #@test Ferrite.child_id(o,b) == 3 or 5? FIXME + @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 end From 61fba0e7d2bc10694143dddeed9cae83b6dd1146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 11:06:48 +0200 Subject: [PATCH 004/143] change to UInt8 --- src/Adaptivity/AdaptiveCells.jl | 34 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 82a23907d6..e6727e2824 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -3,35 +3,33 @@ abstract type AbstractAdaptiveCell{dim,N,M} <: AbstractCell{dim,N,M} end struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} #Refinement level - l::Int + l::UInt8 #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} - xyz::NTuple{dim,Int} + xyz::NTuple{dim,UInt8} end # Follow z order, x before y before z for faces, edges and corners struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} leaves::Vector{Octant} #maximum refinement level - b::Int + b::UInt8 end -function child_id(octant::Octant{3},b::Int) - i = 0 - h = 2^(b - octant.l) - x = octant.xyz[1]; y = octant.xyz[2]; z = octant.xyz[3] - i = i | ((x & h) != 0 ? 1 : 0) - i = i | ((y & h) != 0 ? 2 : 0) - i = i | ((z & h) != 0 ? 4 : 0) - return i+1 +function child_id(octant::Octant{3},b::UInt8) + i = 0x00 + h = 0x02^(b - octant.l) + x,y,z = octant.xyz + i = i | ((x & h) != 0x00 ? 0x01 : 0x00) + i = i | ((y & h) != 0x00 ? 0x02 : 0x00) + i = i | ((z & h) != 0x00 ? 0x04 : 0x00) + return i+0x01 end -function parent(octant::Octant{3,N,M}, b::Int) where {N,M} - h = 2^(b - octant.l) - l = octant.l - 1 - px = octant.xyz[1] & ~h - py = octant.xyz[2] & ~h - pz = octant.xyz[3] & ~h - return Octant{3,N,M}(l,(px,py,pz) .+ 1) +function parent(octant::Octant{3,N,M}, b::UInt8) where {N,M} + h = 0x02^(b - octant.l) + l = octant.l - 0x01 + px,py,pz = octant.xyz .& ~h + return Octant{3,N,M}(l,(px,py,pz) .+ 0x01) end # return the two adjacent faces $f_i$ adjacent to edge `edge` From bbcac4daf3f75c13e6762894d9851b665247bb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 12:48:45 +0200 Subject: [PATCH 005/143] fixed parent and included child_id for 2d case --- src/Adaptivity/AdaptiveCells.jl | 25 ++++++++++++++++++++----- test/test_octant.jl | 7 ++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index e6727e2824..2c42dac1c7 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -10,26 +10,41 @@ end # Follow z order, x before y before z for faces, edges and corners struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} - leaves::Vector{Octant} + leaves::Vector{Octant{dim,N,M}} #maximum refinement level b::UInt8 end function child_id(octant::Octant{3},b::UInt8) - i = 0x00 + i = 0x01 h = 0x02^(b - octant.l) x,y,z = octant.xyz i = i | ((x & h) != 0x00 ? 0x01 : 0x00) i = i | ((y & h) != 0x00 ? 0x02 : 0x00) i = i | ((z & h) != 0x00 ? 0x04 : 0x00) - return i+0x01 + return i +end + +function child_id(octant::Octant{2},b::UInt8) + i = 0x01 + h = 0x02^(b - octant.l) + x,y = octant.xyz + i = i | ((x & h) != 0x00 ? 0x01 : 0x00) + i = i | ((y & h) != 0x00 ? 0x02 : 0x00) + return i end function parent(octant::Octant{3,N,M}, b::UInt8) where {N,M} h = 0x02^(b - octant.l) l = octant.l - 0x01 - px,py,pz = octant.xyz .& ~h - return Octant{3,N,M}(l,(px,py,pz) .+ 0x01) + return Octant{3,N,M}(l,octant.xyz .& ~h) +end + +function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N,M} + x,y,z = o.xyz + println(io, "Octant{$dim,$N,$M}") + println(io, " l = $(o.l)") + println(io, " xyz = $x,$y,$z") end # return the two adjacent faces $f_i$ adjacent to edge `edge` diff --git a/test/test_octant.jl b/test/test_octant.jl index 56e1708ed0..e925d53e62 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -39,12 +39,13 @@ end @testset "Octant Encoding" begin # Tests from Figure 3a) and 3b) of Burstedde et al o = Ferrite.Octant{3,8,6}(2,(1,5,3)) - b = 3 + b = 0x03 @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - #@test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) FIXME + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) o = Ferrite.Octant{3,8,6}(2,(3,3,1)) - #@test Ferrite.child_id(o,b) == 3 or 5? FIXME + @test Ferrite.child_id(o,b) == 3 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) end From 3c51d2c6f85c1ac40ead1dd056cd9c465ea15347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 12:48:51 +0200 Subject: [PATCH 006/143] fixed parent and included child_id for 2d case --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2c42dac1c7..d5c7832df5 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -34,10 +34,10 @@ function child_id(octant::Octant{2},b::UInt8) return i end -function parent(octant::Octant{3,N,M}, b::UInt8) where {N,M} +function parent(octant::Octant{dim,N,M}, b::UInt8) where {dim,N,M} h = 0x02^(b - octant.l) l = octant.l - 0x01 - return Octant{3,N,M}(l,octant.xyz .& ~h) + return Octant{dim,N,M}(l,octant.xyz .& ~h) end function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N,M} From e4486cb983313d25d14b98c30302823520b8a76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 12:55:59 +0200 Subject: [PATCH 007/143] add check in parent for root node, added test for that --- src/Adaptivity/AdaptiveCells.jl | 10 +++++++--- test/test_octant.jl | 3 ++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index d5c7832df5..5d67a1d6cf 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -35,9 +35,13 @@ function child_id(octant::Octant{2},b::UInt8) end function parent(octant::Octant{dim,N,M}, b::UInt8) where {dim,N,M} - h = 0x02^(b - octant.l) - l = octant.l - 0x01 - return Octant{dim,N,M}(l,octant.xyz .& ~h) + if octant.l > 0 + h = 0x02^(b - octant.l) + l = octant.l - 0x01 + return Octant{dim,N,M}(l,octant.xyz .& ~h) + else + error("root has no parent") + end end function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N,M} diff --git a/test/test_octant.jl b/test/test_octant.jl index e925d53e62..e4312e1ec1 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -43,9 +43,10 @@ end @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) + @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) o = Ferrite.Octant{3,8,6}(2,(3,3,1)) @test Ferrite.child_id(o,b) == 3 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) - + @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) end From a60ef58b60fef0e17886562305de0fa59b1d2bbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 13:16:10 +0200 Subject: [PATCH 008/143] add some comment about child_id ordering --- src/Adaptivity/AdaptiveCells.jl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 5d67a1d6cf..25d3d4a983 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -15,6 +15,12 @@ struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} b::UInt8 end +# given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` +# note the following quote from Burstedde et al: +# children are numbered from 0 for the front lower left child, +# to 1 for the front lower right child, to 2 for the back lower left, and so on, with +# 4, . . . , 7 being the four children on top of the children 0, . . . , 3. +# shift by 1 due to julia 1 based indexing function child_id(octant::Octant{3},b::UInt8) i = 0x01 h = 0x02^(b - octant.l) From e827fa4d1bc9af1c1fc856deae3729c94996e1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Aug 2021 20:47:33 +0200 Subject: [PATCH 009/143] further test --- src/Adaptivity/AdaptiveCells.jl | 2 +- test/test_octant.jl | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 25d3d4a983..57cb3ef831 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -20,7 +20,7 @@ end # children are numbered from 0 for the front lower left child, # to 1 for the front lower right child, to 2 for the back lower left, and so on, with # 4, . . . , 7 being the four children on top of the children 0, . . . , 3. -# shift by 1 due to julia 1 based indexing +# shifted by 1 due to julia 1 based indexing function child_id(octant::Octant{3},b::UInt8) i = 0x01 h = 0x02^(b - octant.l) diff --git a/test/test_octant.jl b/test/test_octant.jl index e4312e1ec1..d72e99ae20 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -38,15 +38,28 @@ end @testset "Octant Encoding" begin # Tests from Figure 3a) and 3b) of Burstedde et al - o = Ferrite.Octant{3,8,6}(2,(1,5,3)) + o = Ferrite.Octant{3,8,6}(2,(0,4,2)) b = 0x03 @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - o = Ferrite.Octant{3,8,6}(2,(3,3,1)) + o = Ferrite.Octant{3,8,6}(2,(2,2,0)) @test Ferrite.child_id(o,b) == 3 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(1,1,1)) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) + @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + + # Now I shift the root about (1,1,1) + o = Ferrite.Octant{3,8,6}(2,(0,4,2) .+ 1) + b = 0x03 + @test Ferrite.child_id(o,b) == 5 + @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) + @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + o = Ferrite.Octant{3,8,6}(2,(2,2,0) .+ 1) + @test Ferrite.child_id(o,b) == 3 + @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) end From 81290c44bd58705d2838a533f35b23aa51848d2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 5 Aug 2021 16:27:09 +0200 Subject: [PATCH 010/143] added descendants, face edge and corner_neighbor, descendants, edge and corner_neighbor need tests --- src/Adaptivity/AdaptiveCells.jl | 81 +++++++++++++++++++++++++++++---- test/test_octant.jl | 20 +++++++- 2 files changed, 90 insertions(+), 11 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 57cb3ef831..3398cd47c2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -3,27 +3,44 @@ abstract type AbstractAdaptiveCell{dim,N,M} <: AbstractCell{dim,N,M} end struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} #Refinement level - l::UInt8 + l::UInt #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} - xyz::NTuple{dim,UInt8} + xyz::NTuple{dim,Int} end # Follow z order, x before y before z for faces, edges and corners struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} leaves::Vector{Octant{dim,N,M}} #maximum refinement level - b::UInt8 + b::UInt end -# given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` +function Octant(dim::Int, l::Integer, b::Integer, m::Integer) + x,y,z = (0,0,0) + h = _compute_size(b,l) + for i in 0:l-1 + x = x | (h*(m & 2^(dim*i))/2^((dim-1)*i)) + y = y | (h*(m & 2^(dim*i+1))/2^((dim-1)*i+1)) + z = z | (h*(m & 2^(dim*i+2))/2^((dim-1)*i+2)) + end + if dim == 2 + Octant{dim,8,6}(l,(x,y)) + elseif dim == 3 + Octant{dim,8,6}(l,(x,y,z)) + else + error("$dim Dimension not supported") + end +end + +# Given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` # note the following quote from Burstedde et al: # children are numbered from 0 for the front lower left child, # to 1 for the front lower right child, to 2 for the back lower left, and so on, with # 4, . . . , 7 being the four children on top of the children 0, . . . , 3. # shifted by 1 due to julia 1 based indexing -function child_id(octant::Octant{3},b::UInt8) +function child_id(octant::Octant{3},b::Integer) i = 0x01 - h = 0x02^(b - octant.l) + h = _compute_size(b,octant.l) x,y,z = octant.xyz i = i | ((x & h) != 0x00 ? 0x01 : 0x00) i = i | ((y & h) != 0x00 ? 0x02 : 0x00) @@ -31,18 +48,18 @@ function child_id(octant::Octant{3},b::UInt8) return i end -function child_id(octant::Octant{2},b::UInt8) +function child_id(octant::Octant{2},b::Integer) i = 0x01 - h = 0x02^(b - octant.l) + h = _compute_size(b, octant.l) x,y = octant.xyz i = i | ((x & h) != 0x00 ? 0x01 : 0x00) i = i | ((y & h) != 0x00 ? 0x02 : 0x00) return i end -function parent(octant::Octant{dim,N,M}, b::UInt8) where {dim,N,M} +function parent(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} if octant.l > 0 - h = 0x02^(b - octant.l) + h = _compute_size(b,octant.l) l = octant.l - 0x01 return Octant{dim,N,M}(l,octant.xyz .& ~h) else @@ -50,6 +67,49 @@ function parent(octant::Octant{dim,N,M}, b::UInt8) where {dim,N,M} end end +""" +Given an `octant`, computes the two smallest possible octants that fit into the first and last corners +of `octant`, respectively. These computed octants are called first and last descendants of `octant` +since they are connected to `octant` by a path down the octree to the maximum level `b` +""" +function descendants(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} + l1 = b; l2 = b + h = _compute_size(b,octant.l) + return Octant{dim,N,M}(l1,octant.xyz), Octant{dim,N,M}(l2,octant.xyz .+ (h-1)) +end + +function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer) where {dim,N,M} + l = octant.l + h = _compute_size(b,octant.l) + x,y,z = octant.xyz + x += ((f == 1) ? -h : ((f == 2) ? h : 0)) + y += ((f == 3) ? -h : ((f == 4) ? h : 0)) + z += ((f == 5) ? -h : ((f == 6) ? h : 0)) + return Octant{dim,N,M}(l,(x,y,z)) +end + +function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer) where {N,M} + a₀ = e ÷ 4 + a₁ = (e < 4) ? 1 : 0 + a₂ = (e < 8) ? 2 : 1 + l = octant.l + h = _compute_size(b,octant.l) + x = a₀ + y = a₁ + (2*(e & 1) - 1)*h + z = a₂ + ((e & 2) - 1)*h + return Octant{3,N,M}(l,(x,y,z)) +end + +function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N,M} + l = octant.l + h = _compute_size(b,octant.l) + ox,oy,oz = octant.xyz + x = ox + (2*(c & 1) - 1)*h + y = oy + ((c & 2) - 1)*h + z = oz + ((c & 4)/2 - 1)*h + return Octant{3,N,M}(l,(x,y,z)) +end + function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N,M} x,y,z = o.xyz println(io, "Octant{$dim,$N,$M}") @@ -57,6 +117,7 @@ function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N, println(io, " xyz = $x,$y,$z") end +_compute_size(b::Integer,l::Integer) = 2^(b-l) # return the two adjacent faces $f_i$ adjacent to edge `edge` _face(edge::Int) = 𝒮[edge, :] # return the `i`-th adjacent face fᵢ to edge `edge` diff --git a/test/test_octant.jl b/test/test_octant.jl index d72e99ae20..fedeeed2de 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -61,5 +61,23 @@ end @test Ferrite.child_id(o,b) == 3 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) - @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) +end + +@testset "Octant Operations" begin + o = Ferrite.Octant{3,8,6}(1,(2,0,0)) + @test Ferrite.face_neighbor(o,0x01,0x02) == Ferrite.Octant{3,8,6}(1,(0,0,0)) + @test Ferrite.face_neighbor(o,0x02,0x02) == Ferrite.Octant{3,8,6}(1,(4,0,0)) + @test Ferrite.face_neighbor(o,0x03,0x02) == Ferrite.Octant{3,8,6}(1,(2,-2,0)) + @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.Octant{3,8,6}(1,(2,2,0)) + @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,-2)) + @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,2)) + + o = Ferrite.Octant{3,8,6}(1,(0,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.Octant{3,8,6}(1,(-2,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.Octant{3,8,6}(1,(2,0,0)) + @test Ferrite.face_neighbor(o,3,2) == Ferrite.Octant{3,8,6}(1,(0,-2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.Octant{3,8,6}(1,(0,2,0)) + @test Ferrite.face_neighbor(o,5,2) == Ferrite.Octant{3,8,6}(1,(0,0,-2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.Octant{3,8,6}(1,(0,0,2)) end From 689997bfd28ec74569f281880c4842247c57b349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 6 Aug 2021 14:04:01 +0200 Subject: [PATCH 011/143] shifted morton index by 1 and included morton index constructor tests --- src/Adaptivity/AdaptiveCells.jl | 17 ++++++++++++----- test/test_octant.jl | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 3398cd47c2..92ccd6ede6 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -19,9 +19,9 @@ function Octant(dim::Int, l::Integer, b::Integer, m::Integer) x,y,z = (0,0,0) h = _compute_size(b,l) for i in 0:l-1 - x = x | (h*(m & 2^(dim*i))/2^((dim-1)*i)) - y = y | (h*(m & 2^(dim*i+1))/2^((dim-1)*i+1)) - z = z | (h*(m & 2^(dim*i+2))/2^((dim-1)*i+2)) + x = x | (h*((m-1) & 2^(dim*i))÷2^((dim-1)*i)) + y = y | (h*((m-1) & 2^(dim*i+1))÷2^((dim-1)*i+1)) + z = z | (h*((m-1) & 2^(dim*i+2))÷2^((dim-1)*i+2)) end if dim == 2 Octant{dim,8,6}(l,(x,y)) @@ -110,13 +110,20 @@ function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N return Octant{3,N,M}(l,(x,y,z)) end -function Base.show(io::IO, ::MIME"text/plain", o::Octant{dim,N,M}) where {dim,N,M} +function Base.show(io::IO, ::MIME"text/plain", o::Octant{3,N,M}) where {N,M} x,y,z = o.xyz - println(io, "Octant{$dim,$N,$M}") + println(io, "Octant{3,$N,$M}") println(io, " l = $(o.l)") println(io, " xyz = $x,$y,$z") end +function Base.show(io::IO, ::MIME"text/plain", o::Octant{2,N,M}) where {N,M} + x,y = o.xyz + println(io, "Octant{2,$N,$M}") + println(io, " l = $(o.l)") + println(io, " xy = $x,$y") +end + _compute_size(b::Integer,l::Integer) = 2^(b-l) # return the two adjacent faces $f_i$ adjacent to edge `edge` _face(edge::Int) = 𝒮[edge, :] diff --git a/test/test_octant.jl b/test/test_octant.jl index fedeeed2de..8706148f8b 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -62,6 +62,23 @@ end @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + + # coordinate system always on lowest level + # dim 3, level 2, number of levels 3, morton id 2 + @test Ferrite.Octant(3,2,3,2) == Ferrite.Octant{3,8,6}(2,(2,0,0)) + # dim 3, level 1, number of levels 3, morton id 2 + @test Ferrite.Octant(3,1,3,2) == Ferrite.Octant{3,8,6}(1,(4,0,0)) + # dim 3, level 0, number of levels 3, morton id 2 + @test Ferrite.Octant(3,0,3,2) == Ferrite.Octant{3,8,6}(0,(0,0,0)) + # dim 3, level 2, number of levels 3, morton id 4 + @test Ferrite.Octant(3,2,3,4) == Ferrite.Octant{3,8,6}(2,(2,2,0)) + @test Ferrite.Octant(3,1,3,4) == Ferrite.Octant{3,8,6}(1,(4,4,0)) + @test Ferrite.Octant(3,2,3,5) == Ferrite.Octant{3,8,6}(2,(0,0,2)) + @test Ferrite.Octant(3,1,3,5) == Ferrite.Octant{3,8,6}(1,(0,0,4)) + @test Ferrite.Octant(2,1,3,1) == Ferrite.Octant{2,8,6}(1,(0,0)) + @test Ferrite.Octant(2,1,3,2) == Ferrite.Octant{2,8,6}(1,(4,0)) + @test Ferrite.Octant(2,1,3,3) == Ferrite.Octant{2,8,6}(1,(0,4)) + @test Ferrite.Octant(2,1,3,4) == Ferrite.Octant{2,8,6}(1,(4,4)) end @testset "Octant Operations" begin From 5ceba3fca5a2a14ec7a3b9cafff3f03242e1a610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 6 Aug 2021 14:24:14 +0200 Subject: [PATCH 012/143] include TODOs --- src/Adaptivity/AdaptiveCells.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 92ccd6ede6..837428146e 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -16,6 +16,7 @@ struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} end function Octant(dim::Int, l::Integer, b::Integer, m::Integer) + @assert m ≤ 2^(dim*l) x,y,z = (0,0,0) h = _compute_size(b,l) for i in 0:l-1 @@ -88,6 +89,7 @@ function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer) where {d return Octant{dim,N,M}(l,(x,y,z)) end +# TODO I think this needs to be shifted somewhere because of how we count `e` function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer) where {N,M} a₀ = e ÷ 4 a₁ = (e < 4) ? 1 : 0 @@ -100,6 +102,7 @@ function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer) where {N,M return Octant{3,N,M}(l,(x,y,z)) end +# TODO I think this needs to be shifted somewhere because of how we count `c` function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N,M} l = octant.l h = _compute_size(b,octant.l) From e70bf1134ba2e38c1ad0b418c23873d30ab617cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 6 Aug 2021 14:27:59 +0200 Subject: [PATCH 013/143] proper docstrings --- src/Adaptivity/AdaptiveCells.jl | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 837428146e..28b53be101 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -15,7 +15,11 @@ struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} b::UInt end -function Octant(dim::Int, l::Integer, b::Integer, m::Integer) +""" + Octant(dim::Integer, l::Integer, b::Integer, m::Integer) +Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` +""" +function Octant(dim::Integer, l::Integer, b::Integer, m::Integer) @assert m ≤ 2^(dim*l) x,y,z = (0,0,0) h = _compute_size(b,l) @@ -33,12 +37,15 @@ function Octant(dim::Int, l::Integer, b::Integer, m::Integer) end end -# Given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` -# note the following quote from Burstedde et al: -# children are numbered from 0 for the front lower left child, -# to 1 for the front lower right child, to 2 for the back lower left, and so on, with -# 4, . . . , 7 being the four children on top of the children 0, . . . , 3. -# shifted by 1 due to julia 1 based indexing +""" + child_id(octant::Octant, b::Integer) +Given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` +note the following quote from Burstedde et al: + children are numbered from 0 for the front lower left child, + to 1 for the front lower right child, to 2 for the back lower left, and so on, with + 4, . . . , 7 being the four children on top of the children 0, . . . , 3. +shifted by 1 due to julia 1 based indexing +""" function child_id(octant::Octant{3},b::Integer) i = 0x01 h = _compute_size(b,octant.l) @@ -69,6 +76,7 @@ function parent(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} end """ + descendants(octant::Octant, b::Integer) Given an `octant`, computes the two smallest possible octants that fit into the first and last corners of `octant`, respectively. These computed octants are called first and last descendants of `octant` since they are connected to `octant` by a path down the octree to the maximum level `b` From 44d4c9850ddc963b0f344a89f86549cfc1e2cd0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 6 Aug 2021 14:29:02 +0200 Subject: [PATCH 014/143] AssertionError in test for morton index constructor --- test/test_octant.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_octant.jl b/test/test_octant.jl index 8706148f8b..b2aab811e8 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -69,7 +69,7 @@ end # dim 3, level 1, number of levels 3, morton id 2 @test Ferrite.Octant(3,1,3,2) == Ferrite.Octant{3,8,6}(1,(4,0,0)) # dim 3, level 0, number of levels 3, morton id 2 - @test Ferrite.Octant(3,0,3,2) == Ferrite.Octant{3,8,6}(0,(0,0,0)) + @test_throws AssertionError Ferrite.Octant(3,0,3,2) # dim 3, level 2, number of levels 3, morton id 4 @test Ferrite.Octant(3,2,3,4) == Ferrite.Octant{3,8,6}(2,(2,2,0)) @test Ferrite.Octant(3,1,3,4) == Ferrite.Octant{3,8,6}(1,(4,4,0)) From e33d85916f041b128aa948f151e799e699f12e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 6 Aug 2021 16:54:59 +0200 Subject: [PATCH 015/143] fixed descendants, need to wait for zulip responds to confirm those changes. should be correct imho --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- test/test_octant.jl | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 28b53be101..78383b9c05 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -82,9 +82,9 @@ of `octant`, respectively. These computed octants are called first and last desc since they are connected to `octant` by a path down the octree to the maximum level `b` """ function descendants(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} - l1 = b; l2 = b + l1 = b-1; l2 = b-1 # not sure h = _compute_size(b,octant.l) - return Octant{dim,N,M}(l1,octant.xyz), Octant{dim,N,M}(l2,octant.xyz .+ (h-1)) + return Octant{dim,N,M}(l1,octant.xyz), Octant{dim,N,M}(l2,octant.xyz .+ (h-2)) end function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer) where {dim,N,M} diff --git a/test/test_octant.jl b/test/test_octant.jl index b2aab811e8..b0178c1b30 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -89,6 +89,8 @@ end @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.Octant{3,8,6}(1,(2,2,0)) @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,-2)) @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,2)) + @test Ferrite.descendants(o,2) == (Ferrite.Octant{3,8,6}(1,(2,0,0)), Ferrite.Octant{3,8,6}(1,(2,0,0))) + @test Ferrite.descendants(o,3) == (Ferrite.Octant{3,8,6}(2,(2,0,0)), Ferrite.Octant{3,8,6}(2,(4,2,2))) o = Ferrite.Octant{3,8,6}(1,(0,0,0)) @test Ferrite.face_neighbor(o,1,2) == Ferrite.Octant{3,8,6}(1,(-2,0,0)) @@ -96,5 +98,8 @@ end @test Ferrite.face_neighbor(o,3,2) == Ferrite.Octant{3,8,6}(1,(0,-2,0)) @test Ferrite.face_neighbor(o,4,2) == Ferrite.Octant{3,8,6}(1,(0,2,0)) @test Ferrite.face_neighbor(o,5,2) == Ferrite.Octant{3,8,6}(1,(0,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.Octant{3,8,6}(1,(0,0,2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.Octant{3,8,6}(1,(0,0,2)) + o = Ferrite.Octant{3,8,6}(0,(0,0,0)) + @test Ferrite.descendants(o,2) == (Ferrite.Octant{3,8,6}(1,(0,0,0)), Ferrite.Octant{3,8,6}(1,(2,2,2))) + @test Ferrite.descendants(o,3) == (Ferrite.Octant{3,8,6}(2,(0,0,0)), Ferrite.Octant{3,8,6}(2,(6,6,6))) end From 98d8ac0042752d2c9e486b7877b32b4b7d689b34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 7 Aug 2021 18:04:13 +0200 Subject: [PATCH 016/143] I think I have found a bug in tests and in child_id, now morton index corresponds to child_id --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- test/test_octant.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 78383b9c05..5bb31fb62f 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -47,13 +47,13 @@ note the following quote from Burstedde et al: shifted by 1 due to julia 1 based indexing """ function child_id(octant::Octant{3},b::Integer) - i = 0x01 + i = 0x00 h = _compute_size(b,octant.l) x,y,z = octant.xyz i = i | ((x & h) != 0x00 ? 0x01 : 0x00) i = i | ((y & h) != 0x00 ? 0x02 : 0x00) i = i | ((z & h) != 0x00 ? 0x04 : 0x00) - return i + return i+0x01 end function child_id(octant::Octant{2},b::Integer) diff --git a/test/test_octant.jl b/test/test_octant.jl index b0178c1b30..b139ab2458 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -45,7 +45,7 @@ end @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) o = Ferrite.Octant{3,8,6}(2,(2,2,0)) - @test Ferrite.child_id(o,b) == 3 + @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) @@ -58,7 +58,7 @@ end @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) o = Ferrite.Octant{3,8,6}(2,(2,2,0) .+ 1) - @test Ferrite.child_id(o,b) == 3 + @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) From 5944c570e0149c62dcc1bf846493145297f57c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 7 Aug 2021 18:38:31 +0200 Subject: [PATCH 017/143] fix 2d version of child_id, added tests with child_id morton index mapping --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- test/test_octant.jl | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 5bb31fb62f..74256fd90f 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -57,12 +57,12 @@ function child_id(octant::Octant{3},b::Integer) end function child_id(octant::Octant{2},b::Integer) - i = 0x01 + i = 0x00 h = _compute_size(b, octant.l) x,y = octant.xyz i = i | ((x & h) != 0x00 ? 0x01 : 0x00) i = i | ((y & h) != 0x00 ? 0x02 : 0x00) - return i + return i+0x01 end function parent(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} diff --git a/test/test_octant.jl b/test/test_octant.jl index b139ab2458..a92a5b7cf3 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -79,6 +79,19 @@ end @test Ferrite.Octant(2,1,3,2) == Ferrite.Octant{2,8,6}(1,(4,0)) @test Ferrite.Octant(2,1,3,3) == Ferrite.Octant{2,8,6}(1,(0,4)) @test Ferrite.Octant(2,1,3,4) == Ferrite.Octant{2,8,6}(1,(4,4)) + @test Ferrite.child_id(Ferrite.Octant(2,1,3,1),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(2,1,3,2),3) == 2 + @test Ferrite.child_id(Ferrite.Octant(2,1,3,3),3) == 3 + @test Ferrite.child_id(Ferrite.Octant(2,1,3,4),3) == 4 + @test Ferrite.child_id(Ferrite.Octant(2,2,3,1),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,1),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,2),3) == 2 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,3),3) == 3 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,4),3) == 4 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,16),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,24),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,64),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,3,9),3) == 1 end @testset "Octant Operations" begin From fa2509c5fd46d9145e2682fb00d277c40214d379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 7 Aug 2021 22:24:26 +0200 Subject: [PATCH 018/143] fixed edge neighbor, included 2D corner neighbor, included tests for corner and edge neighbor --- src/Adaptivity/AdaptiveCells.jl | 50 +++++++++++++++++++++++++++------ test/test_octant.jl | 14 +++++++++ 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 74256fd90f..a45aa8bb52 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -97,21 +97,43 @@ function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer) where {d return Octant{dim,N,M}(l,(x,y,z)) end -# TODO I think this needs to be shifted somewhere because of how we count `e` +""" + edge_neighbor(octant::Octant, e::Integer, b::Integer) +Computes the edge neighbor octant which is only connected by the edge `e` to `octant` +""" function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer) where {N,M} - a₀ = e ÷ 4 - a₁ = (e < 4) ? 1 : 0 - a₂ = (e < 8) ? 2 : 1 + @assert 1 ≤ e ≤ 12 + e -= 1 l = octant.l h = _compute_size(b,octant.l) - x = a₀ - y = a₁ + (2*(e & 1) - 1)*h - z = a₂ + ((e & 2) - 1)*h - return Octant{3,N,M}(l,(x,y,z)) + ox,oy,oz = octant.xyz + case = e ÷ 4 + if case == 0 + x = ox + y = oy + (2*(e & 0x01) - 1)*h + z = oz + ((e & 0x02) - 1)*h + return Octant{3,N,M}(l,(x,y,z)) + elseif case == 1 + x = ox + (2*(e & 0x01) - 1)*h + y = oy + z = oz + ((e & 0x02) - 1)*h + return Octant{3,N,M}(l,(x,y,z)) + elseif case == 2 + x = ox + (2*(e & 0x01) - 1)*h + y = oy + ((e & 0x02) - 1)*h + z = oz + return Octant{3,N,M}(l,(x,y,z)) + else + error("edge case not found") + end end -# TODO I think this needs to be shifted somewhere because of how we count `c` +""" + corner_neighbor(octant::Octant, c::Integer, b::Integer) +Computes the corner neighbor octant which is only connected by the corner `c` to `octant` +""" function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N,M} + c -= 1 l = octant.l h = _compute_size(b,octant.l) ox,oy,oz = octant.xyz @@ -121,6 +143,16 @@ function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N return Octant{3,N,M}(l,(x,y,z)) end +function corner_neighbor(octant::Octant{2,N,M}, c::Integer, b::Integer) where {N,M} + c -= 1 + l = octant.l + h = _compute_size(b,octant.l) + ox,oy = octant.xyz + x = ox + (2*(c & 1) - 1)*h + y = oy + ((c & 2) - 1)*h + return Octant{2,N,M}(l,(x,y)) +end + function Base.show(io::IO, ::MIME"text/plain", o::Octant{3,N,M}) where {N,M} x,y,z = o.xyz println(io, "Octant{3,$N,$M}") diff --git a/test/test_octant.jl b/test/test_octant.jl index a92a5b7cf3..fe08509c49 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -115,4 +115,18 @@ end o = Ferrite.Octant{3,8,6}(0,(0,0,0)) @test Ferrite.descendants(o,2) == (Ferrite.Octant{3,8,6}(1,(0,0,0)), Ferrite.Octant{3,8,6}(1,(2,2,2))) @test Ferrite.descendants(o,3) == (Ferrite.Octant{3,8,6}(2,(0,0,0)), Ferrite.Octant{3,8,6}(2,(6,6,6))) + + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),1,3) == Ferrite.Octant{3,8,6}(2,(2,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),4,3) == Ferrite.Octant{3,8,6}(2,(2,2,2)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),6,3) == Ferrite.Octant{3,8,6}(2,(4,0,-2)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),9,3) == Ferrite.Octant{3,8,6}(2,(0,-2,0)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),12,3) == Ferrite.Octant{3,8,6}(2,(4,2,0)) + + @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),1,3) == Ferrite.Octant{3,8,6}(2,(0,-2,-2)) + @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),4,3) == Ferrite.Octant{3,8,6}(2,(4,2,-2)) + @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),8,3) == Ferrite.Octant{3,8,6}(2,(4,2,2)) + + @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),1,3) == Ferrite.Octant{2,8,6}(2,(0,-2)) + @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),2,3) == Ferrite.Octant{2,8,6}(2,(4,-2)) + @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),4,3) == Ferrite.Octant{2,8,6}(2,(4,2)) end From 99f2fc6ac1b3f188d7c723d9d577f50b67a44a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 9 Aug 2021 12:23:03 +0200 Subject: [PATCH 019/143] maxlevel always last arg with default value based on Burstedde et al. --- src/Adaptivity/AdaptiveCells.jl | 20 ++++++------ test/test_octant.jl | 57 ++++++++++++++++++--------------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index a45aa8bb52..35f0e60c63 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -1,6 +1,8 @@ abstract type AbstractAdaptiveTree{dim,N,M} <: AbstractCell{dim,N,M} end abstract type AbstractAdaptiveCell{dim,N,M} <: AbstractCell{dim,N,M} end +_maxlevel = [30,19] + struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} #Refinement level l::UInt @@ -19,7 +21,7 @@ end Octant(dim::Integer, l::Integer, b::Integer, m::Integer) Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ -function Octant(dim::Integer, l::Integer, b::Integer, m::Integer) +function Octant(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1]) @assert m ≤ 2^(dim*l) x,y,z = (0,0,0) h = _compute_size(b,l) @@ -46,7 +48,7 @@ note the following quote from Burstedde et al: 4, . . . , 7 being the four children on top of the children 0, . . . , 3. shifted by 1 due to julia 1 based indexing """ -function child_id(octant::Octant{3},b::Integer) +function child_id(octant::Octant{3},b::Integer=_maxlevel[2]) i = 0x00 h = _compute_size(b,octant.l) x,y,z = octant.xyz @@ -56,7 +58,7 @@ function child_id(octant::Octant{3},b::Integer) return i+0x01 end -function child_id(octant::Octant{2},b::Integer) +function child_id(octant::Octant{2},b::Integer=_maxlevel[1]) i = 0x00 h = _compute_size(b, octant.l) x,y = octant.xyz @@ -65,7 +67,7 @@ function child_id(octant::Octant{2},b::Integer) return i+0x01 end -function parent(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} +function parent(octant::Octant{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} if octant.l > 0 h = _compute_size(b,octant.l) l = octant.l - 0x01 @@ -81,13 +83,13 @@ Given an `octant`, computes the two smallest possible octants that fit into the of `octant`, respectively. These computed octants are called first and last descendants of `octant` since they are connected to `octant` by a path down the octree to the maximum level `b` """ -function descendants(octant::Octant{dim,N,M}, b::Integer) where {dim,N,M} +function descendants(octant::Octant{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} l1 = b-1; l2 = b-1 # not sure h = _compute_size(b,octant.l) return Octant{dim,N,M}(l1,octant.xyz), Octant{dim,N,M}(l2,octant.xyz .+ (h-2)) end -function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer) where {dim,N,M} +function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M} l = octant.l h = _compute_size(b,octant.l) x,y,z = octant.xyz @@ -101,7 +103,7 @@ end edge_neighbor(octant::Octant, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` """ -function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer) where {N,M} +function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer=_maxlevel[2]) where {N,M} @assert 1 ≤ e ≤ 12 e -= 1 l = octant.l @@ -132,7 +134,7 @@ end corner_neighbor(octant::Octant, c::Integer, b::Integer) Computes the corner neighbor octant which is only connected by the corner `c` to `octant` """ -function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N,M} +function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer=_maxlevel[2]) where {N,M} c -= 1 l = octant.l h = _compute_size(b,octant.l) @@ -143,7 +145,7 @@ function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer) where {N return Octant{3,N,M}(l,(x,y,z)) end -function corner_neighbor(octant::Octant{2,N,M}, c::Integer, b::Integer) where {N,M} +function corner_neighbor(octant::Octant{2,N,M}, c::Integer, b::Integer=_maxlevel[1]) where {N,M} c -= 1 l = octant.l h = _compute_size(b,octant.l) diff --git a/test/test_octant.jl b/test/test_octant.jl index fe08509c49..3e3f136dcf 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -64,34 +64,34 @@ end @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) # coordinate system always on lowest level - # dim 3, level 2, number of levels 3, morton id 2 - @test Ferrite.Octant(3,2,3,2) == Ferrite.Octant{3,8,6}(2,(2,0,0)) - # dim 3, level 1, number of levels 3, morton id 2 - @test Ferrite.Octant(3,1,3,2) == Ferrite.Octant{3,8,6}(1,(4,0,0)) - # dim 3, level 0, number of levels 3, morton id 2 - @test_throws AssertionError Ferrite.Octant(3,0,3,2) - # dim 3, level 2, number of levels 3, morton id 4 - @test Ferrite.Octant(3,2,3,4) == Ferrite.Octant{3,8,6}(2,(2,2,0)) - @test Ferrite.Octant(3,1,3,4) == Ferrite.Octant{3,8,6}(1,(4,4,0)) - @test Ferrite.Octant(3,2,3,5) == Ferrite.Octant{3,8,6}(2,(0,0,2)) - @test Ferrite.Octant(3,1,3,5) == Ferrite.Octant{3,8,6}(1,(0,0,4)) - @test Ferrite.Octant(2,1,3,1) == Ferrite.Octant{2,8,6}(1,(0,0)) - @test Ferrite.Octant(2,1,3,2) == Ferrite.Octant{2,8,6}(1,(4,0)) + # dim 3, level 2, morton id 2, number of levels 3 + @test Ferrite.Octant(3,2,2,3) == Ferrite.Octant{3,8,6}(2,(2,0,0)) + # dim 3, level 1, morton id 2, number of levels 3 + @test Ferrite.Octant(3,1,2,3) == Ferrite.Octant{3,8,6}(1,(4,0,0)) + # dim 3, level 0, morton id 2, number of levels 3 + @test_throws AssertionError Ferrite.Octant(3,0,2,3) + # dim 3, level 2, morton id 4, number of levels 3 + @test Ferrite.Octant(3,2,4,3) == Ferrite.Octant{3,8,6}(2,(2,2,0)) + @test Ferrite.Octant(3,1,4,3) == Ferrite.Octant{3,8,6}(1,(4,4,0)) + @test Ferrite.Octant(3,2,5,3) == Ferrite.Octant{3,8,6}(2,(0,0,2)) + @test Ferrite.Octant(3,1,5,3) == Ferrite.Octant{3,8,6}(1,(0,0,4)) + @test Ferrite.Octant(2,1,1,3) == Ferrite.Octant{2,8,6}(1,(0,0)) + @test Ferrite.Octant(2,1,2,3) == Ferrite.Octant{2,8,6}(1,(4,0)) @test Ferrite.Octant(2,1,3,3) == Ferrite.Octant{2,8,6}(1,(0,4)) - @test Ferrite.Octant(2,1,3,4) == Ferrite.Octant{2,8,6}(1,(4,4)) - @test Ferrite.child_id(Ferrite.Octant(2,1,3,1),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(2,1,3,2),3) == 2 + @test Ferrite.Octant(2,1,4,3) == Ferrite.Octant{2,8,6}(1,(4,4)) + @test Ferrite.child_id(Ferrite.Octant(2,1,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(2,1,2,3),3) == 2 @test Ferrite.child_id(Ferrite.Octant(2,1,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.Octant(2,1,3,4),3) == 4 - @test Ferrite.child_id(Ferrite.Octant(2,2,3,1),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,1),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,2),3) == 2 + @test Ferrite.child_id(Ferrite.Octant(2,1,4,3),3) == 4 + @test Ferrite.child_id(Ferrite.Octant(2,2,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(3,2,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(3,2,2,3),3) == 2 @test Ferrite.child_id(Ferrite.Octant(3,2,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,4),3) == 4 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,16),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,24),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,64),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,9),3) == 1 + @test Ferrite.child_id(Ferrite.Octant(3,2,4,3),3) == 4 + @test Ferrite.child_id(Ferrite.Octant(3,2,16,3),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,24,3),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,64,3),3) == 8 + @test Ferrite.child_id(Ferrite.Octant(3,2,9,3),3) == 1 end @testset "Octant Operations" begin @@ -122,6 +122,13 @@ end @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),9,3) == Ferrite.Octant{3,8,6}(2,(0,-2,0)) @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),12,3) == Ferrite.Octant{3,8,6}(2,(4,2,0)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(3,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(3,(0,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(3,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(3,(2,2,0)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(2,(0,-4,-4)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(2,(4,4,0)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(1,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(1,(0,-8,-8)) + @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(1,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(1,(8,8,0)) + @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),1,3) == Ferrite.Octant{3,8,6}(2,(0,-2,-2)) @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),4,3) == Ferrite.Octant{3,8,6}(2,(4,2,-2)) @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),8,3) == Ferrite.Octant{3,8,6}(2,(4,2,2)) From 517b6a613cbe615fee72a7e98f2cb14f838ebea8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 10 Aug 2021 17:39:33 +0200 Subject: [PATCH 020/143] started adaptive grid --- src/Adaptivity/AdaptiveCells.jl | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 35f0e60c63..aafa381eb2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -1,9 +1,29 @@ -abstract type AbstractAdaptiveTree{dim,N,M} <: AbstractCell{dim,N,M} end +abstract type AbstractAdaptiveGrid{dim} <: AbstractGrid{dim} end abstract type AbstractAdaptiveCell{dim,N,M} <: AbstractCell{dim,N,M} end _maxlevel = [30,19] -struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} +function set_maxlevel(dim::Integer,maxlevel::Integer) + _maxlevel[dim-1] = maxlevel +end + +struct AdaptiveGrid{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} + cells::Vector{C} + nodes::Vector{Node{dim,T}} + # Sets + cellsets::Dict{String,Set{Int}} + nodesets::Dict{String,Set{Int}} + facesets::Dict{String,Set{FaceIndex}} + edgesets::Dict{String,Set{EdgeIndex}} + vertexsets::Dict{String,Set{VertexIndex}} + #Connectivities + 𝒩𝒪::Matrix{T} + 𝒩ℱ::Matrix{T} + ℰ𝒯::Array + 𝒞𝒯::Array +end + +struct Octant{dim, N, M} #Refinement level l::UInt #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} @@ -11,10 +31,11 @@ struct Octant{dim, N, M} <: AbstractAdaptiveCell{dim,8,6} end # Follow z order, x before y before z for faces, edges and corners -struct Octree{dim,N,M} <: AbstractAdaptiveTree{dim,N,M} +struct Octree{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{Octant{dim,N,M}} #maximum refinement level b::UInt + nodes::NTuple{N,Int} end """ From 17f145f6d2cf9b951005fb37dff89c0f09e7488f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 14 Aug 2021 14:46:24 +0200 Subject: [PATCH 021/143] corner neighborhood for 2D --- src/Adaptivity/AdaptiveCells.jl | 176 ++++++++++++++++++++++---------- src/exports.jl | 5 + test/test_octant.jl | 170 +++++++++++++++++------------- 3 files changed, 226 insertions(+), 125 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index aafa381eb2..2af3b43f07 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -7,42 +7,18 @@ function set_maxlevel(dim::Integer,maxlevel::Integer) _maxlevel[dim-1] = maxlevel end -struct AdaptiveGrid{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} - cells::Vector{C} - nodes::Vector{Node{dim,T}} - # Sets - cellsets::Dict{String,Set{Int}} - nodesets::Dict{String,Set{Int}} - facesets::Dict{String,Set{FaceIndex}} - edgesets::Dict{String,Set{EdgeIndex}} - vertexsets::Dict{String,Set{VertexIndex}} - #Connectivities - 𝒩𝒪::Matrix{T} - 𝒩ℱ::Matrix{T} - ℰ𝒯::Array - 𝒞𝒯::Array -end - -struct Octant{dim, N, M} +struct OctantBWG{dim, N, M} <: AbstractCell{dim,N,M} #Refinement level l::UInt #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} xyz::NTuple{dim,Int} end -# Follow z order, x before y before z for faces, edges and corners -struct Octree{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} - leaves::Vector{Octant{dim,N,M}} - #maximum refinement level - b::UInt - nodes::NTuple{N,Int} -end - """ - Octant(dim::Integer, l::Integer, b::Integer, m::Integer) + OctantBWG(dim::Integer, l::Integer, b::Integer, m::Integer) Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ -function Octant(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1]) +function OctantBWG(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1]) @assert m ≤ 2^(dim*l) x,y,z = (0,0,0) h = _compute_size(b,l) @@ -52,24 +28,116 @@ function Octant(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1 z = z | (h*((m-1) & 2^(dim*i+2))÷2^((dim-1)*i+2)) end if dim == 2 - Octant{dim,8,6}(l,(x,y)) + OctantBWG{dim,4,4}(l,(x,y)) elseif dim == 3 - Octant{dim,8,6}(l,(x,y,z)) + OctantBWG{dim,8,6}(l,(x,y,z)) else error("$dim Dimension not supported") end end +Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) +Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) + +# Follow z order, x before y before z for faces, edges and corners +struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} + leaves::Vector{OctantBWG{dim,N,M}} + #maximum refinement level + b::UInt + nodes::NTuple{N,Int} +end + +OctreeBWG{3,8,6}(nodes,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) +OctreeBWG{2,4,4}(nodes,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) + +struct TopologyBWG{T} + #maps a given octree `k` and face `f` to neighbor octree `k'` and its face `f'` + face_neighbor::SparseMatrixCSC{T} + #maps a given octree `k` and corner `c` to neighbor octree `k'` and its corner`c'` + corner_neighbor::SparseMatrixCSC{T} + #𝒩ℱ::Matrix{T} + #ℰ𝒯::Matrix{T} + #𝒞𝒯::Matrix{T} +end + +function TopologyBWG(cells::Vector{Cell{3,N,6}}) where N + I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] + I_edge = UInt[]; J_edge = UInt[]; V_edge = UInt[] + I_corner = UInt[]; J_corner = UInt[]; V_corner = UInt[] + for (cellid,cell) in enumerate(cells) + neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) + end +end + +function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N + I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] + I_corner = UInt[]; J_corner = UInt[]; V_corner = Tuple{Int,Int}[] + for (cellid,cell) in enumerate(cells) + neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) + for (neighborid,neighbor) in enumerate(neighbors) + if length(neighbor) == 0 + #not a neighbor + continue + elseif length(neighbor) == 1 + #corner neighbor + corner_neighbor = (neighborid, neighbor[1]) + cell_corner_id = findfirst(x->x==cells[neighborid].nodes[neighbor[1]], cell.nodes) + push!(V_corner,corner_neighbor) + push!(I_corner,cellid) + push!(J_corner,cell_corner_id) + elseif length(neighbor) == 2 + #face neghbor + else + continue + end + end + end + face_neighbor = sparse(I_face,J_face,V_face) + corner_neighbor = sparse(I_corner,J_corner,V_corner) + return TopologyBWG(face_neighbor,corner_neighbor) +end + +""" + ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} +`p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] +""" +struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} + cells::Vector{C} + nodes::Vector{Node{dim,T}} + # Sets + cellsets::Dict{String,Set{Int}} + nodesets::Dict{String,Set{Int}} + facesets::Dict{String,Set{FaceIndex}} + edgesets::Dict{String,Set{EdgeIndex}} + vertexsets::Dict{String,Set{VertexIndex}} + #Topology + topology::TopologyBWG +end + +function make_adaptive(grid::Grid,::Type{ForestBWG}) + cells = grid.cells + nodes = grid.nodes + cellsets = grid.cellsets + nodesets = grid.nodesets + facesets = grid.facesets + edgesets = grid.edgesets + vertexsets = grid.vertexsets + + topology = TopologyBWG(cells) + + return ForestBWG{3,8,6}(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) +end + """ - child_id(octant::Octant, b::Integer) -Given some Octant `octant` and maximum refinement level `b`, compute the child_id of `octant` + child_id(octant::OctantBWG, b::Integer) +Given some OctantBWG `octant` and maximum refinement level `b`, compute the child_id of `octant` note the following quote from Burstedde et al: children are numbered from 0 for the front lower left child, to 1 for the front lower right child, to 2 for the back lower left, and so on, with 4, . . . , 7 being the four children on top of the children 0, . . . , 3. shifted by 1 due to julia 1 based indexing """ -function child_id(octant::Octant{3},b::Integer=_maxlevel[2]) +function child_id(octant::OctantBWG{3},b::Integer=_maxlevel[2]) i = 0x00 h = _compute_size(b,octant.l) x,y,z = octant.xyz @@ -79,7 +147,7 @@ function child_id(octant::Octant{3},b::Integer=_maxlevel[2]) return i+0x01 end -function child_id(octant::Octant{2},b::Integer=_maxlevel[1]) +function child_id(octant::OctantBWG{2},b::Integer=_maxlevel[1]) i = 0x00 h = _compute_size(b, octant.l) x,y = octant.xyz @@ -88,43 +156,43 @@ function child_id(octant::Octant{2},b::Integer=_maxlevel[1]) return i+0x01 end -function parent(octant::Octant{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} +function parent(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} if octant.l > 0 h = _compute_size(b,octant.l) l = octant.l - 0x01 - return Octant{dim,N,M}(l,octant.xyz .& ~h) + return OctantBWG{dim,N,M}(l,octant.xyz .& ~h) else error("root has no parent") end end """ - descendants(octant::Octant, b::Integer) + descendants(octant::OctantBWG, b::Integer) Given an `octant`, computes the two smallest possible octants that fit into the first and last corners of `octant`, respectively. These computed octants are called first and last descendants of `octant` since they are connected to `octant` by a path down the octree to the maximum level `b` """ -function descendants(octant::Octant{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} +function descendants(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} l1 = b-1; l2 = b-1 # not sure h = _compute_size(b,octant.l) - return Octant{dim,N,M}(l1,octant.xyz), Octant{dim,N,M}(l2,octant.xyz .+ (h-2)) + return OctantBWG{dim,N,M}(l1,octant.xyz), OctantBWG{dim,N,M}(l2,octant.xyz .+ (h-2)) end -function face_neighbor(octant::Octant{dim,N,M}, f::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M} +function face_neighbor(octant::OctantBWG{dim,N,M}, f::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M} l = octant.l h = _compute_size(b,octant.l) x,y,z = octant.xyz x += ((f == 1) ? -h : ((f == 2) ? h : 0)) y += ((f == 3) ? -h : ((f == 4) ? h : 0)) z += ((f == 5) ? -h : ((f == 6) ? h : 0)) - return Octant{dim,N,M}(l,(x,y,z)) + return OctantBWG{dim,N,M}(l,(x,y,z)) end """ - edge_neighbor(octant::Octant, e::Integer, b::Integer) + edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` """ -function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer=_maxlevel[2]) where {N,M} +function edge_neighbor(octant::OctantBWG{3,N,M}, e::Integer, b::Integer=_maxlevel[2]) where {N,M} @assert 1 ≤ e ≤ 12 e -= 1 l = octant.l @@ -135,27 +203,27 @@ function edge_neighbor(octant::Octant{3,N,M}, e::Integer, b::Integer=_maxlevel[2 x = ox y = oy + (2*(e & 0x01) - 1)*h z = oz + ((e & 0x02) - 1)*h - return Octant{3,N,M}(l,(x,y,z)) + return OctantBWG{3,N,M}(l,(x,y,z)) elseif case == 1 x = ox + (2*(e & 0x01) - 1)*h y = oy z = oz + ((e & 0x02) - 1)*h - return Octant{3,N,M}(l,(x,y,z)) + return OctantBWG{3,N,M}(l,(x,y,z)) elseif case == 2 x = ox + (2*(e & 0x01) - 1)*h y = oy + ((e & 0x02) - 1)*h z = oz - return Octant{3,N,M}(l,(x,y,z)) + return OctantBWG{3,N,M}(l,(x,y,z)) else error("edge case not found") end end """ - corner_neighbor(octant::Octant, c::Integer, b::Integer) + corner_neighbor(octant::OctantBWG, c::Integer, b::Integer) Computes the corner neighbor octant which is only connected by the corner `c` to `octant` """ -function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer=_maxlevel[2]) where {N,M} +function corner_neighbor(octant::OctantBWG{3,N,M}, c::Integer, b::Integer=_maxlevel[2]) where {N,M} c -= 1 l = octant.l h = _compute_size(b,octant.l) @@ -163,29 +231,29 @@ function corner_neighbor(octant::Octant{3,N,M}, c::Integer, b::Integer=_maxlevel x = ox + (2*(c & 1) - 1)*h y = oy + ((c & 2) - 1)*h z = oz + ((c & 4)/2 - 1)*h - return Octant{3,N,M}(l,(x,y,z)) + return OctantBWG{3,N,M}(l,(x,y,z)) end -function corner_neighbor(octant::Octant{2,N,M}, c::Integer, b::Integer=_maxlevel[1]) where {N,M} +function corner_neighbor(octant::OctantBWG{2,N,M}, c::Integer, b::Integer=_maxlevel[1]) where {N,M} c -= 1 l = octant.l h = _compute_size(b,octant.l) ox,oy = octant.xyz x = ox + (2*(c & 1) - 1)*h y = oy + ((c & 2) - 1)*h - return Octant{2,N,M}(l,(x,y)) + return OctantBWG{2,N,M}(l,(x,y)) end -function Base.show(io::IO, ::MIME"text/plain", o::Octant{3,N,M}) where {N,M} +function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{3,N,M}) where {N,M} x,y,z = o.xyz - println(io, "Octant{3,$N,$M}") + println(io, "OctantBWG{3,$N,$M}") println(io, " l = $(o.l)") println(io, " xyz = $x,$y,$z") end -function Base.show(io::IO, ::MIME"text/plain", o::Octant{2,N,M}) where {N,M} +function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{2,N,M}) where {N,M} x,y = o.xyz - println(io, "Octant{2,$N,$M}") + println(io, "OctantBWG{2,$N,$M}") println(io, " l = $(o.l)") println(io, " xy = $x,$y") end diff --git a/src/exports.jl b/src/exports.jl index b70cd7437b..967c1a23f9 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -85,6 +85,11 @@ export transform!, generate_grid, compute_vertex_values, +# AdaptiveGrid + ForestBWG, + make_adaptive, + OctreeBWG, + OctantBWG, # Dofs DofHandler, diff --git a/test/test_octant.jl b/test/test_octant.jl index 3e3f136dcf..b242981285 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -1,4 +1,4 @@ -@testset "Octant Lookup Tables" begin +@testset "OctantBWG Lookup Tables" begin @test Ferrite._face(1) == [3,5] @test Ferrite._face(5) == [1,5] @test Ferrite._face(12) == [2,4] @@ -36,104 +36,132 @@ @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) end -@testset "Octant Encoding" begin +@testset "OctantBWG Encoding" begin # Tests from Figure 3a) and 3b) of Burstedde et al - o = Ferrite.Octant{3,8,6}(2,(0,4,2)) + o = Ferrite.OctantBWG{3,8,6}(2,(0,4,2)) b = 0x03 @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - o = Ferrite.Octant{3,8,6}(2,(2,2,0)) + o = Ferrite.OctantBWG{3,8,6}(2,(2,2,0)) @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0)) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) # Now I shift the root about (1,1,1) - o = Ferrite.Octant{3,8,6}(2,(0,4,2) .+ 1) + o = Ferrite.OctantBWG{3,8,6}(2,(0,4,2) .+ 1) b = 0x03 @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - o = Ferrite.Octant{3,8,6}(2,(2,2,0) .+ 1) + o = Ferrite.OctantBWG{3,8,6}(2,(2,2,0) .+ 1) @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.Octant{3,8,6}(0,(0,0,0) .+ 1) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0) .+ 1) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) # coordinate system always on lowest level # dim 3, level 2, morton id 2, number of levels 3 - @test Ferrite.Octant(3,2,2,3) == Ferrite.Octant{3,8,6}(2,(2,0,0)) + @test Ferrite.OctantBWG(3,2,2,3) == Ferrite.OctantBWG{3,8,6}(2,(2,0,0)) # dim 3, level 1, morton id 2, number of levels 3 - @test Ferrite.Octant(3,1,2,3) == Ferrite.Octant{3,8,6}(1,(4,0,0)) + @test Ferrite.OctantBWG(3,1,2,3) == Ferrite.OctantBWG{3,8,6}(1,(4,0,0)) # dim 3, level 0, morton id 2, number of levels 3 - @test_throws AssertionError Ferrite.Octant(3,0,2,3) + @test_throws AssertionError Ferrite.OctantBWG(3,0,2,3) # dim 3, level 2, morton id 4, number of levels 3 - @test Ferrite.Octant(3,2,4,3) == Ferrite.Octant{3,8,6}(2,(2,2,0)) - @test Ferrite.Octant(3,1,4,3) == Ferrite.Octant{3,8,6}(1,(4,4,0)) - @test Ferrite.Octant(3,2,5,3) == Ferrite.Octant{3,8,6}(2,(0,0,2)) - @test Ferrite.Octant(3,1,5,3) == Ferrite.Octant{3,8,6}(1,(0,0,4)) - @test Ferrite.Octant(2,1,1,3) == Ferrite.Octant{2,8,6}(1,(0,0)) - @test Ferrite.Octant(2,1,2,3) == Ferrite.Octant{2,8,6}(1,(4,0)) - @test Ferrite.Octant(2,1,3,3) == Ferrite.Octant{2,8,6}(1,(0,4)) - @test Ferrite.Octant(2,1,4,3) == Ferrite.Octant{2,8,6}(1,(4,4)) - @test Ferrite.child_id(Ferrite.Octant(2,1,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(2,1,2,3),3) == 2 - @test Ferrite.child_id(Ferrite.Octant(2,1,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.Octant(2,1,4,3),3) == 4 - @test Ferrite.child_id(Ferrite.Octant(2,2,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(3,2,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.Octant(3,2,2,3),3) == 2 - @test Ferrite.child_id(Ferrite.Octant(3,2,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.Octant(3,2,4,3),3) == 4 - @test Ferrite.child_id(Ferrite.Octant(3,2,16,3),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,24,3),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,64,3),3) == 8 - @test Ferrite.child_id(Ferrite.Octant(3,2,9,3),3) == 1 + @test Ferrite.OctantBWG(3,2,4,3) == Ferrite.OctantBWG{3,8,6}(2,(2,2,0)) + @test Ferrite.OctantBWG(3,1,4,3) == Ferrite.OctantBWG{3,8,6}(1,(4,4,0)) + @test Ferrite.OctantBWG(3,2,5,3) == Ferrite.OctantBWG{3,8,6}(2,(0,0,2)) + @test Ferrite.OctantBWG(3,1,5,3) == Ferrite.OctantBWG{3,8,6}(1,(0,0,4)) + @test Ferrite.OctantBWG(2,1,1,3) == Ferrite.OctantBWG{2,4,4}(1,(0,0)) + @test Ferrite.OctantBWG(2,1,2,3) == Ferrite.OctantBWG{2,4,4}(1,(4,0)) + @test Ferrite.OctantBWG(2,1,3,3) == Ferrite.OctantBWG{2,4,4}(1,(0,4)) + @test Ferrite.OctantBWG(2,1,4,3) == Ferrite.OctantBWG{2,4,4}(1,(4,4)) + @test Ferrite.child_id(Ferrite.OctantBWG(2,1,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.OctantBWG(2,1,2,3),3) == 2 + @test Ferrite.child_id(Ferrite.OctantBWG(2,1,3,3),3) == 3 + @test Ferrite.child_id(Ferrite.OctantBWG(2,1,4,3),3) == 4 + @test Ferrite.child_id(Ferrite.OctantBWG(2,2,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,1,3),3) == 1 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,2,3),3) == 2 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,3,3),3) == 3 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,4,3),3) == 4 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,16,3),3) == 8 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,24,3),3) == 8 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,64,3),3) == 8 + @test Ferrite.child_id(Ferrite.OctantBWG(3,2,9,3),3) == 1 end -@testset "Octant Operations" begin - o = Ferrite.Octant{3,8,6}(1,(2,0,0)) - @test Ferrite.face_neighbor(o,0x01,0x02) == Ferrite.Octant{3,8,6}(1,(0,0,0)) - @test Ferrite.face_neighbor(o,0x02,0x02) == Ferrite.Octant{3,8,6}(1,(4,0,0)) - @test Ferrite.face_neighbor(o,0x03,0x02) == Ferrite.Octant{3,8,6}(1,(2,-2,0)) - @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.Octant{3,8,6}(1,(2,2,0)) - @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,-2)) - @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.Octant{3,8,6}(1,(2,0,2)) - @test Ferrite.descendants(o,2) == (Ferrite.Octant{3,8,6}(1,(2,0,0)), Ferrite.Octant{3,8,6}(1,(2,0,0))) - @test Ferrite.descendants(o,3) == (Ferrite.Octant{3,8,6}(2,(2,0,0)), Ferrite.Octant{3,8,6}(2,(4,2,2))) +@testset "OctantBWG Operations" begin + o = Ferrite.OctantBWG{3,8,6}(1,(2,0,0)) + @test Ferrite.face_neighbor(o,0x01,0x02) == Ferrite.OctantBWG{3,8,6}(1,(0,0,0)) + @test Ferrite.face_neighbor(o,0x02,0x02) == Ferrite.OctantBWG{3,8,6}(1,(4,0,0)) + @test Ferrite.face_neighbor(o,0x03,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,-2,0)) + @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,2,0)) + @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,-2)) + @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,2)) + @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(2,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,0,0))) + @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(2,0,0)), Ferrite.OctantBWG{3,8,6}(2,(4,2,2))) - o = Ferrite.Octant{3,8,6}(1,(0,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.Octant{3,8,6}(1,(-2,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.Octant{3,8,6}(1,(2,0,0)) - @test Ferrite.face_neighbor(o,3,2) == Ferrite.Octant{3,8,6}(1,(0,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.Octant{3,8,6}(1,(0,2,0)) - @test Ferrite.face_neighbor(o,5,2) == Ferrite.Octant{3,8,6}(1,(0,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.Octant{3,8,6}(1,(0,0,2)) - o = Ferrite.Octant{3,8,6}(0,(0,0,0)) - @test Ferrite.descendants(o,2) == (Ferrite.Octant{3,8,6}(1,(0,0,0)), Ferrite.Octant{3,8,6}(1,(2,2,2))) - @test Ferrite.descendants(o,3) == (Ferrite.Octant{3,8,6}(2,(0,0,0)), Ferrite.Octant{3,8,6}(2,(6,6,6))) + o = Ferrite.OctantBWG{3,8,6}(1,(0,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG{3,8,6}(1,(-2,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG{3,8,6}(1,(2,0,0)) + @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG{3,8,6}(1,(0,-2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG{3,8,6}(1,(0,2,0)) + @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,-2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,2)) + o = Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) + @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(0,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,2,2))) + @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(0,0,0)), Ferrite.OctantBWG{3,8,6}(2,(6,6,6))) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),1,3) == Ferrite.Octant{3,8,6}(2,(2,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),4,3) == Ferrite.Octant{3,8,6}(2,(2,2,2)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),6,3) == Ferrite.Octant{3,8,6}(2,(4,0,-2)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),9,3) == Ferrite.Octant{3,8,6}(2,(0,-2,0)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),12,3) == Ferrite.Octant{3,8,6}(2,(4,2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),1,3) == Ferrite.OctantBWG{3,8,6}(2,(2,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),4,3) == Ferrite.OctantBWG{3,8,6}(2,(2,2,2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),6,3) == Ferrite.OctantBWG{3,8,6}(2,(4,0,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),9,3) == Ferrite.OctantBWG{3,8,6}(2,(0,-2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),12,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,0)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(3,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(3,(0,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(3,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(3,(2,2,0)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(2,(0,-4,-4)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(2,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(2,(4,4,0)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(1,(0,0,0)),1,4) == Ferrite.Octant{3,8,6}(1,(0,-8,-8)) - @test Ferrite.edge_neighbor(Ferrite.Octant{3,8,6}(1,(0,0,0)),12,4) == Ferrite.Octant{3,8,6}(1,(8,8,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(3,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(3,(0,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(3,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(3,(2,2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(2,(0,-4,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(2,(4,4,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(1,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(1,(0,-8,-8)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(1,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(1,(8,8,0)) - @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),1,3) == Ferrite.Octant{3,8,6}(2,(0,-2,-2)) - @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),4,3) == Ferrite.Octant{3,8,6}(2,(4,2,-2)) - @test Ferrite.corner_neighbor(Ferrite.Octant{3,8,6}(2,(2,0,0)),8,3) == Ferrite.Octant{3,8,6}(2,(4,2,2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),1,3) == Ferrite.OctantBWG{3,8,6}(2,(0,-2,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),4,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),8,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,2)) - @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),1,3) == Ferrite.Octant{2,8,6}(2,(0,-2)) - @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),2,3) == Ferrite.Octant{2,8,6}(2,(4,-2)) - @test Ferrite.corner_neighbor(Ferrite.Octant{2,8,6}(2,(2,0)),4,3) == Ferrite.Octant{2,8,6}(2,(4,2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),1,3) == Ferrite.OctantBWG{2,4,4}(2,(0,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),2,3) == Ferrite.OctantBWG{2,4,4}(2,(4,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),4,3) == Ferrite.OctantBWG{2,4,4}(2,(4,2)) +end + +@testset "ForestBWG Topology" begin +# (11) +#(10)+-----+-----+(12) +# | 5 | 6 | +#(7) +-----+-----+(9) +# | 3 | 4 | +#(4) +-----+-----+(6) +# | 1 | 2 | +#(1) +-----+-----+(3) +# (2) + cells = Cell{2,4,4}[Cell{2,4,4}((1,2,5,4)), + Cell{2,4,4}((2,3,6,5)), + Cell{2,4,4}((4,5,8,7)), + Cell{2,4,4}((5,6,9,8)), + Cell{2,4,4}((7,8,10,11)), + Cell{2,4,4}((8,9,11,12))] + topology = Ferrite.TopologyBWG(cells) + #test corner neighbors maps cellid and local corner id to neighbor id and neighbor local corner id + @test topology.corner_neighbor[1,3] == (4,1) + @test topology.corner_neighbor[2,4] == (3,2) + @test topology.corner_neighbor[3,3] == (6,1) + @test topology.corner_neighbor[3,2] == (2,4) + @test topology.corner_neighbor[4,1] == (1,3) + @test topology.corner_neighbor[4,4] == (5,2) + @test topology.corner_neighbor[5,2] == (4,4) + @test topology.corner_neighbor[6,1] == (3,3) end From 621c0913abf80c350c754a5e2adb9a7c63960539 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 14 Aug 2021 16:31:40 +0200 Subject: [PATCH 022/143] face neighbor for 2D --- src/Adaptivity/AdaptiveCells.jl | 31 +++++++++++++++++++++++++------ test/test_octant.jl | 23 +++++++++++++++++++++-- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2af3b43f07..2ea1341ad2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -60,6 +60,9 @@ struct TopologyBWG{T} #𝒞𝒯::Matrix{T} end +#CAUTION: type piracy in order to display zero values of SparseMatrixCSC +Base.zero(::Type{Tuple{Int,Int}}) = (0,0) + function TopologyBWG(cells::Vector{Cell{3,N,6}}) where N I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] I_edge = UInt[]; J_edge = UInt[]; V_edge = UInt[] @@ -75,18 +78,16 @@ function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N for (cellid,cell) in enumerate(cells) neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) for (neighborid,neighbor) in enumerate(neighbors) + neighbor_cell = cells[neighborid] if length(neighbor) == 0 #not a neighbor continue elseif length(neighbor) == 1 #corner neighbor - corner_neighbor = (neighborid, neighbor[1]) - cell_corner_id = findfirst(x->x==cells[neighborid].nodes[neighbor[1]], cell.nodes) - push!(V_corner,corner_neighbor) - push!(I_corner,cellid) - push!(J_corner,cell_corner_id) + _corner_neighbor!(V_corner,I_corner,J_corner,cellid,cell,neighbor,neighborid,neighbor_cell) elseif length(neighbor) == 2 - #face neghbor + #face neighbor + _face_neighbor!(V_face,I_face,J_face,cellid,cell,neighbor,neighborid,neighbor_cell) else continue end @@ -97,6 +98,24 @@ function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N return TopologyBWG(face_neighbor,corner_neighbor) end +function _corner_neighbor!(V_corner, I_corner, J_corner, cellid, cell, neighbor, neighborid, neighbor_cell) + corner_neighbor = (neighborid, neighbor[1]) + cell_corner_id = findfirst(x->x==neighbor_cell.nodes[neighbor[1]], cell.nodes) + push!(V_corner,corner_neighbor) + push!(I_corner,cellid) + push!(J_corner,cell_corner_id) +end + +function _face_neighbor!(V_face, I_face, J_face, cellid, cell, neighbor, neighborid, neighbor_cell) + neighbor_face = neighbor_cell.nodes[neighbor] + neighbor_face_id = findfirst(x->issubset(x,neighbor_face), faces(neighbor_cell)) + cell_face_id = findfirst(x->issubset(x,neighbor_face),faces(cell)) + face_neighbor = (neighborid, neighbor_face_id) + push!(V_face, face_neighbor) + push!(I_face, cellid) + push!(J_face, cell_face_id) +end + """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] diff --git a/test/test_octant.jl b/test/test_octant.jl index b242981285..66b532c895 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -152,8 +152,8 @@ end Cell{2,4,4}((2,3,6,5)), Cell{2,4,4}((4,5,8,7)), Cell{2,4,4}((5,6,9,8)), - Cell{2,4,4}((7,8,10,11)), - Cell{2,4,4}((8,9,11,12))] + Cell{2,4,4}((7,8,11,10)), + Cell{2,4,4}((8,9,12,11))] topology = Ferrite.TopologyBWG(cells) #test corner neighbors maps cellid and local corner id to neighbor id and neighbor local corner id @test topology.corner_neighbor[1,3] == (4,1) @@ -164,4 +164,23 @@ end @test topology.corner_neighbor[4,4] == (5,2) @test topology.corner_neighbor[5,2] == (4,4) @test topology.corner_neighbor[6,1] == (3,3) + #test face neighbor maps cellid and local face id to neighbor id and neighbor local face id + @test topology.face_neighbor[1,2] == (2,4) + @test topology.face_neighbor[1,3] == (3,1) + @test topology.face_neighbor[2,3] == (4,1) + @test topology.face_neighbor[2,4] == (1,2) + @test topology.face_neighbor[3,1] == (1,3) + @test topology.face_neighbor[3,2] == (4,4) + @test topology.face_neighbor[3,3] == (5,1) + @test topology.face_neighbor[4,1] == (2,3) + @test topology.face_neighbor[4,3] == (6,1) + @test topology.face_neighbor[4,4] == (3,2) + @test topology.face_neighbor[5,1] == (3,3) + @test topology.face_neighbor[5,2] == (6,4) + @test topology.face_neighbor[5,3] == (0,0) + @test topology.face_neighbor[5,4] == (0,0) + @test topology.face_neighbor[6,1] == (4,3) + @test topology.face_neighbor[6,2] == (0,0) + @test topology.face_neighbor[6,3] == (0,0) + @test topology.face_neighbor[6,4] == (5,2) end From 96c93920c182bbf2693695f861fc3385e76d8cae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 14 Aug 2021 18:10:35 +0200 Subject: [PATCH 023/143] topology for 3D --- src/Adaptivity/AdaptiveCells.jl | 44 ++++++++++++++++++++++++++++----- test/test_octant.jl | 30 ++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2ea1341ad2..4ff91e7145 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -55,9 +55,8 @@ struct TopologyBWG{T} face_neighbor::SparseMatrixCSC{T} #maps a given octree `k` and corner `c` to neighbor octree `k'` and its corner`c'` corner_neighbor::SparseMatrixCSC{T} - #𝒩ℱ::Matrix{T} - #ℰ𝒯::Matrix{T} - #𝒞𝒯::Matrix{T} + #maps a given octree `k` and edge `e` to neighbor octree `k'` and its edge `e'` + edge_neighbor::SparseMatrixCSC{T} end #CAUTION: type piracy in order to display zero values of SparseMatrixCSC @@ -65,11 +64,33 @@ Base.zero(::Type{Tuple{Int,Int}}) = (0,0) function TopologyBWG(cells::Vector{Cell{3,N,6}}) where N I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] - I_edge = UInt[]; J_edge = UInt[]; V_edge = UInt[] - I_corner = UInt[]; J_corner = UInt[]; V_corner = UInt[] + I_edge = UInt[]; J_edge = UInt[]; V_edge = Tuple{Int,Int}[] + I_corner = UInt[]; J_corner = UInt[]; V_corner = Tuple{Int,Int}[] for (cellid,cell) in enumerate(cells) neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) + for (neighborid,neighbor) in enumerate(neighbors) + neighbor_cell = cells[neighborid] + if length(neighbor) == 0 + #not a neighbor + continue + elseif length(neighbor) == 1 + #corner neighbor + _corner_neighbor!(V_corner,I_corner,J_corner,cellid,cell,neighbor,neighborid,neighbor_cell) + elseif length(neighbor) == 2 + #corner neighbor + _edge_neighbor!(V_edge,I_edge,J_edge,cellid,cell,neighbor,neighborid,neighbor_cell) + elseif length(neighbor) == 4 + #face neighbor + _face_neighbor!(V_face,I_face,J_face,cellid,cell,neighbor,neighborid,neighbor_cell) + else + continue + end + end end + face_neighbor = sparse(I_face,J_face,V_face) + corner_neighbor = sparse(I_corner,J_corner,V_corner) + edge_neighbor = sparse(I_edge,J_edge,V_edge) + return TopologyBWG(face_neighbor,corner_neighbor,edge_neighbor) end function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N @@ -95,7 +116,8 @@ function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N end face_neighbor = sparse(I_face,J_face,V_face) corner_neighbor = sparse(I_corner,J_corner,V_corner) - return TopologyBWG(face_neighbor,corner_neighbor) + edge_neighbor = spzeros(Tuple{Int,Int},0,0) + return TopologyBWG(face_neighbor,corner_neighbor,edge_neighbor) end function _corner_neighbor!(V_corner, I_corner, J_corner, cellid, cell, neighbor, neighborid, neighbor_cell) @@ -106,6 +128,16 @@ function _corner_neighbor!(V_corner, I_corner, J_corner, cellid, cell, neighbor, push!(J_corner,cell_corner_id) end +function _edge_neighbor!(V_face, I_face, J_face, cellid, cell, neighbor, neighborid, neighbor_cell) + neighbor_face = neighbor_cell.nodes[neighbor] + neighbor_face_id = findfirst(x->issubset(x,neighbor_face), edges(neighbor_cell)) + cell_face_id = findfirst(x->issubset(x,neighbor_face),edges(cell)) + face_neighbor = (neighborid, neighbor_face_id) + push!(V_face, face_neighbor) + push!(I_face, cellid) + push!(J_face, cell_face_id) +end + function _face_neighbor!(V_face, I_face, J_face, cellid, cell, neighbor, neighborid, neighbor_cell) neighbor_face = neighbor_cell.nodes[neighbor] neighbor_face_id = findfirst(x->issubset(x,neighbor_face), faces(neighbor_cell)) diff --git a/test/test_octant.jl b/test/test_octant.jl index 66b532c895..ea4380c99b 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -183,4 +183,34 @@ end @test topology.face_neighbor[6,2] == (0,0) @test topology.face_neighbor[6,3] == (0,0) @test topology.face_neighbor[6,4] == (5,2) +# (8) +# (7) +-----+-----+(9) +# | 3 | 4 | +# (4) +-----+-----+(6) bottom view +# | 1 | 2 | +# (1) +-----+-----+(3) +# (2) +# (15) +#(16) +-----+-----+(17) +# | 3 | 4 | +#(13) +-----+-----+(15) top view +# | 1 | 2 | +#(10) +-----+-----+(12) +# (11) + hexgrid = generate_grid(Hexahedron,(2,2,1)) + cells = hexgrid.cells + topology = Ferrite.TopologyBWG(cells) + @test topology.edge_neighbor[1,7] == (4,5) + @test topology.edge_neighbor[2,8] == (3,6) + @test topology.edge_neighbor[3,6] == (2,8) + @test topology.edge_neighbor[4,5] == (1,7) + @test all(iszero,topology.corner_neighbor) + @test topology.face_neighbor[1,3] == (2,5) + @test topology.face_neighbor[1,4] == (3,2) + @test topology.face_neighbor[2,4] == (4,2) + @test topology.face_neighbor[2,5] == (1,3) + @test topology.face_neighbor[3,2] == (1,4) + @test topology.face_neighbor[3,3] == (4,5) + @test topology.face_neighbor[4,2] == (2,4) + @test topology.face_neighbor[4,5] == (3,3) end From d352bb3bdfa93852d4a4236645a6a461451a2a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 1 Oct 2022 20:20:22 +0200 Subject: [PATCH 024/143] cleanup topology from the past --- src/Adaptivity/AdaptiveCells.jl | 101 +------------------------------- test/test_octant.jl | 77 ------------------------ 2 files changed, 2 insertions(+), 176 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 4ff91e7145..93cd3091b2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -50,103 +50,6 @@ end OctreeBWG{3,8,6}(nodes,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) OctreeBWG{2,4,4}(nodes,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) -struct TopologyBWG{T} - #maps a given octree `k` and face `f` to neighbor octree `k'` and its face `f'` - face_neighbor::SparseMatrixCSC{T} - #maps a given octree `k` and corner `c` to neighbor octree `k'` and its corner`c'` - corner_neighbor::SparseMatrixCSC{T} - #maps a given octree `k` and edge `e` to neighbor octree `k'` and its edge `e'` - edge_neighbor::SparseMatrixCSC{T} -end - -#CAUTION: type piracy in order to display zero values of SparseMatrixCSC -Base.zero(::Type{Tuple{Int,Int}}) = (0,0) - -function TopologyBWG(cells::Vector{Cell{3,N,6}}) where N - I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] - I_edge = UInt[]; J_edge = UInt[]; V_edge = Tuple{Int,Int}[] - I_corner = UInt[]; J_corner = UInt[]; V_corner = Tuple{Int,Int}[] - for (cellid,cell) in enumerate(cells) - neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) - for (neighborid,neighbor) in enumerate(neighbors) - neighbor_cell = cells[neighborid] - if length(neighbor) == 0 - #not a neighbor - continue - elseif length(neighbor) == 1 - #corner neighbor - _corner_neighbor!(V_corner,I_corner,J_corner,cellid,cell,neighbor,neighborid,neighbor_cell) - elseif length(neighbor) == 2 - #corner neighbor - _edge_neighbor!(V_edge,I_edge,J_edge,cellid,cell,neighbor,neighborid,neighbor_cell) - elseif length(neighbor) == 4 - #face neighbor - _face_neighbor!(V_face,I_face,J_face,cellid,cell,neighbor,neighborid,neighbor_cell) - else - continue - end - end - end - face_neighbor = sparse(I_face,J_face,V_face) - corner_neighbor = sparse(I_corner,J_corner,V_corner) - edge_neighbor = sparse(I_edge,J_edge,V_edge) - return TopologyBWG(face_neighbor,corner_neighbor,edge_neighbor) -end - -function TopologyBWG(cells::Vector{Cell{2,N,4}}) where N - I_face = UInt[]; J_face = UInt[]; V_face = Tuple{Int,Int}[] - I_corner = UInt[]; J_corner = UInt[]; V_corner = Tuple{Int,Int}[] - for (cellid,cell) in enumerate(cells) - neighbors = findall.(x->x ∈ cell.nodes,getproperty.(cells,:nodes)) - for (neighborid,neighbor) in enumerate(neighbors) - neighbor_cell = cells[neighborid] - if length(neighbor) == 0 - #not a neighbor - continue - elseif length(neighbor) == 1 - #corner neighbor - _corner_neighbor!(V_corner,I_corner,J_corner,cellid,cell,neighbor,neighborid,neighbor_cell) - elseif length(neighbor) == 2 - #face neighbor - _face_neighbor!(V_face,I_face,J_face,cellid,cell,neighbor,neighborid,neighbor_cell) - else - continue - end - end - end - face_neighbor = sparse(I_face,J_face,V_face) - corner_neighbor = sparse(I_corner,J_corner,V_corner) - edge_neighbor = spzeros(Tuple{Int,Int},0,0) - return TopologyBWG(face_neighbor,corner_neighbor,edge_neighbor) -end - -function _corner_neighbor!(V_corner, I_corner, J_corner, cellid, cell, neighbor, neighborid, neighbor_cell) - corner_neighbor = (neighborid, neighbor[1]) - cell_corner_id = findfirst(x->x==neighbor_cell.nodes[neighbor[1]], cell.nodes) - push!(V_corner,corner_neighbor) - push!(I_corner,cellid) - push!(J_corner,cell_corner_id) -end - -function _edge_neighbor!(V_face, I_face, J_face, cellid, cell, neighbor, neighborid, neighbor_cell) - neighbor_face = neighbor_cell.nodes[neighbor] - neighbor_face_id = findfirst(x->issubset(x,neighbor_face), edges(neighbor_cell)) - cell_face_id = findfirst(x->issubset(x,neighbor_face),edges(cell)) - face_neighbor = (neighborid, neighbor_face_id) - push!(V_face, face_neighbor) - push!(I_face, cellid) - push!(J_face, cell_face_id) -end - -function _face_neighbor!(V_face, I_face, J_face, cellid, cell, neighbor, neighborid, neighbor_cell) - neighbor_face = neighbor_cell.nodes[neighbor] - neighbor_face_id = findfirst(x->issubset(x,neighbor_face), faces(neighbor_cell)) - cell_face_id = findfirst(x->issubset(x,neighbor_face),faces(cell)) - face_neighbor = (neighborid, neighbor_face_id) - push!(V_face, face_neighbor) - push!(I_face, cellid) - push!(J_face, cell_face_id) -end """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} @@ -162,7 +65,7 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} edgesets::Dict{String,Set{EdgeIndex}} vertexsets::Dict{String,Set{VertexIndex}} #Topology - topology::TopologyBWG + topology::ExclusiveTopology end function make_adaptive(grid::Grid,::Type{ForestBWG}) @@ -174,7 +77,7 @@ function make_adaptive(grid::Grid,::Type{ForestBWG}) edgesets = grid.edgesets vertexsets = grid.vertexsets - topology = TopologyBWG(cells) + topology = ExclusiveTopology(cells) return ForestBWG{3,8,6}(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) end diff --git a/test/test_octant.jl b/test/test_octant.jl index ea4380c99b..be10c32a88 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -137,80 +137,3 @@ end @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),2,3) == Ferrite.OctantBWG{2,4,4}(2,(4,-2)) @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),4,3) == Ferrite.OctantBWG{2,4,4}(2,(4,2)) end - -@testset "ForestBWG Topology" begin -# (11) -#(10)+-----+-----+(12) -# | 5 | 6 | -#(7) +-----+-----+(9) -# | 3 | 4 | -#(4) +-----+-----+(6) -# | 1 | 2 | -#(1) +-----+-----+(3) -# (2) - cells = Cell{2,4,4}[Cell{2,4,4}((1,2,5,4)), - Cell{2,4,4}((2,3,6,5)), - Cell{2,4,4}((4,5,8,7)), - Cell{2,4,4}((5,6,9,8)), - Cell{2,4,4}((7,8,11,10)), - Cell{2,4,4}((8,9,12,11))] - topology = Ferrite.TopologyBWG(cells) - #test corner neighbors maps cellid and local corner id to neighbor id and neighbor local corner id - @test topology.corner_neighbor[1,3] == (4,1) - @test topology.corner_neighbor[2,4] == (3,2) - @test topology.corner_neighbor[3,3] == (6,1) - @test topology.corner_neighbor[3,2] == (2,4) - @test topology.corner_neighbor[4,1] == (1,3) - @test topology.corner_neighbor[4,4] == (5,2) - @test topology.corner_neighbor[5,2] == (4,4) - @test topology.corner_neighbor[6,1] == (3,3) - #test face neighbor maps cellid and local face id to neighbor id and neighbor local face id - @test topology.face_neighbor[1,2] == (2,4) - @test topology.face_neighbor[1,3] == (3,1) - @test topology.face_neighbor[2,3] == (4,1) - @test topology.face_neighbor[2,4] == (1,2) - @test topology.face_neighbor[3,1] == (1,3) - @test topology.face_neighbor[3,2] == (4,4) - @test topology.face_neighbor[3,3] == (5,1) - @test topology.face_neighbor[4,1] == (2,3) - @test topology.face_neighbor[4,3] == (6,1) - @test topology.face_neighbor[4,4] == (3,2) - @test topology.face_neighbor[5,1] == (3,3) - @test topology.face_neighbor[5,2] == (6,4) - @test topology.face_neighbor[5,3] == (0,0) - @test topology.face_neighbor[5,4] == (0,0) - @test topology.face_neighbor[6,1] == (4,3) - @test topology.face_neighbor[6,2] == (0,0) - @test topology.face_neighbor[6,3] == (0,0) - @test topology.face_neighbor[6,4] == (5,2) -# (8) -# (7) +-----+-----+(9) -# | 3 | 4 | -# (4) +-----+-----+(6) bottom view -# | 1 | 2 | -# (1) +-----+-----+(3) -# (2) -# (15) -#(16) +-----+-----+(17) -# | 3 | 4 | -#(13) +-----+-----+(15) top view -# | 1 | 2 | -#(10) +-----+-----+(12) -# (11) - hexgrid = generate_grid(Hexahedron,(2,2,1)) - cells = hexgrid.cells - topology = Ferrite.TopologyBWG(cells) - @test topology.edge_neighbor[1,7] == (4,5) - @test topology.edge_neighbor[2,8] == (3,6) - @test topology.edge_neighbor[3,6] == (2,8) - @test topology.edge_neighbor[4,5] == (1,7) - @test all(iszero,topology.corner_neighbor) - @test topology.face_neighbor[1,3] == (2,5) - @test topology.face_neighbor[1,4] == (3,2) - @test topology.face_neighbor[2,4] == (4,2) - @test topology.face_neighbor[2,5] == (1,3) - @test topology.face_neighbor[3,2] == (1,4) - @test topology.face_neighbor[3,3] == (4,5) - @test topology.face_neighbor[4,2] == (2,4) - @test topology.face_neighbor[4,5] == (3,3) -end From 13c994ff4a81674d4f3a4b0c455a5c93cb188ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 7 Oct 2022 15:40:20 +0200 Subject: [PATCH 025/143] add constructor for Octree and Forest --- src/Adaptivity/AdaptiveCells.jl | 44 +++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 93cd3091b2..06167e6185 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -47,9 +47,12 @@ struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} nodes::NTuple{N,Int} end -OctreeBWG{3,8,6}(nodes,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) -OctreeBWG{2,4,4}(nodes,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) +OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) +OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) +OctreeBWG(cell::Quadrilateral) = OctreeBWG{2,4,4}(cell.nodes) +OctreeBWG(cell::Hexahedron) = OctreeBWG{3,8,6}(cell.nodes) +Base.length(tree::OctreeBWG) = length(tree.leaves) """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} @@ -68,18 +71,33 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} topology::ExclusiveTopology end -function make_adaptive(grid::Grid,::Type{ForestBWG}) - cells = grid.cells - nodes = grid.nodes - cellsets = grid.cellsets - nodesets = grid.nodesets - facesets = grid.facesets - edgesets = grid.edgesets - vertexsets = grid.vertexsets - +function ForestBWG(grid::AbstractGrid{dim}) where dim + cells = getcells(grid) + C = eltype(cells) + @assert isconcretetype(C) + @assert (C == Quadrilateral && dim == 2) || (C == Hexahedron && dim == 3) topology = ExclusiveTopology(cells) - - return ForestBWG{3,8,6}(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) + cells = OctreeBWG.(grid.cells) + nodes = getnodes(grid) + cellsets = getcellsets(grid) + nodesets = getnodesets(grid) + facesets = getfacesets(grid) + edgesets = getedgesets(grid) + vertexsets = getvertexsets(grid) + return ForestBWG(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) +end + +function getncells(grid::ForestBWG) + numcells = 0 + for tree in grid.cells + numcells += length(tree) + end + return numcells +end + +function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) + println(io, "ForestBWG with ") + println(io, " $(getncells(agrid)) cells") end """ From 4201ab93d7c243f5968d9ae2d2a7f60b1d798edd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 7 Oct 2022 21:49:31 +0200 Subject: [PATCH 026/143] maximumsize of octant --- src/Adaptivity/AdaptiveCells.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 06167e6185..577aff6636 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -231,6 +231,7 @@ function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{2,N,M}) where {N,M} end _compute_size(b::Integer,l::Integer) = 2^(b-l) +_maximum_size(b::Integer) = 2^(b) # return the two adjacent faces $f_i$ adjacent to edge `edge` _face(edge::Int) = 𝒮[edge, :] # return the `i`-th adjacent face fᵢ to edge `edge` From af61a769a8069fe2425a499f93566566d2f13f83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 10 Oct 2022 07:30:43 +0200 Subject: [PATCH 027/143] start of refine, not fully working yet --- src/Adaptivity/AdaptiveCells.jl | 37 ++++++++++++++++--- test/test_octant.jl | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 6 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 577aff6636..1f39cf8f8c 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -19,6 +19,7 @@ end Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ function OctantBWG(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1]) + @assert l ≤ b #maximum refinement level exceeded @assert m ≤ 2^(dim*l) x,y,z = (0,0,0) h = _compute_size(b,l) @@ -47,13 +48,37 @@ struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} nodes::NTuple{N,Int} end +function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} + #TODO write generic morton idx identification + morton_o = findfirst(x->x==o,octree.leaves) + # how to obtain id from morton index ? + old_octant = popat!(octree.leaves,morton_o) + self_child_id = child_id(old_octant,octree.b) + start_child_id = self_child_id * (self_child_id != 1 ? (self_child_id - 1) : (1)) # shift morton idx about one level + for child_mort_id in start_child_id+1:(start_child_id+N) + insert!(octree.leaves,morton_o,OctantBWG(dim,old_octant.l+1,child_mort_id,octree.b)) + morton_o += 1 + end +end + OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) -OctreeBWG(cell::Quadrilateral) = OctreeBWG{2,4,4}(cell.nodes) -OctreeBWG(cell::Hexahedron) = OctreeBWG{3,8,6}(cell.nodes) +OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4,4}(cell.nodes,b) +OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) +function morton(o::OctantBWG{dim,N,M},b=_maxlevel[dim==2 ? 2 : 1]) where {dim,N,M} + recursive_o = OctantBWG{dim,N,M}(o.l,o.xyz) + level = recursive_o.l + morton_idx = 0 + while level > 0 + morton_idx += child_id(recursive_o,b) + level -= 1 + recursive_o = parent(recursive_o,b) + end +end + """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] @@ -71,13 +96,13 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} topology::ExclusiveTopology end -function ForestBWG(grid::AbstractGrid{dim}) where dim +function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim==2 ? 2 : 1]) where dim cells = getcells(grid) C = eltype(cells) @assert isconcretetype(C) @assert (C == Quadrilateral && dim == 2) || (C == Hexahedron && dim == 3) topology = ExclusiveTopology(cells) - cells = OctreeBWG.(grid.cells) + cells = OctreeBWG.(grid.cells,b) nodes = getnodes(grid) cellsets = getcellsets(grid) nodesets = getnodesets(grid) @@ -103,7 +128,7 @@ end """ child_id(octant::OctantBWG, b::Integer) Given some OctantBWG `octant` and maximum refinement level `b`, compute the child_id of `octant` -note the following quote from Burstedde et al: +note the following quote from Bursedde et al: children are numbered from 0 for the front lower left child, to 1 for the front lower right child, to 2 for the back lower left, and so on, with 4, . . . , 7 being the four children on top of the children 0, . . . , 3. @@ -145,7 +170,7 @@ of `octant`, respectively. These computed octants are called first and last desc since they are connected to `octant` by a path down the octree to the maximum level `b` """ function descendants(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} - l1 = b-1; l2 = b-1 # not sure + l1 = octant.l+1; l2 = octant.l+1 # not sure h = _compute_size(b,octant.l) return OctantBWG{dim,N,M}(l1,octant.xyz), OctantBWG{dim,N,M}(l2,octant.xyz .+ (h-2)) end diff --git a/test/test_octant.jl b/test/test_octant.jl index be10c32a88..087f47250a 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -137,3 +137,68 @@ end @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),2,3) == Ferrite.OctantBWG{2,4,4}(2,(4,-2)) @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),4,3) == Ferrite.OctantBWG{2,4,4}(2,(4,2)) end + +@testset "OctreeBWG Operations" begin + # maximum level == 3 + # Octant level 0 size == 2^3=8 + # Octant level 1 size == 2^3/2 = 4 + # Octant level 2 size == 2^3/2 = 2 + # Octant level 3 size == 2^3/2 = 1 + # test translation constructor + grid = generate_grid(Quadrilateral,(2,2)) + adaptive_grid = ForestBWG(grid,3) + for cell in adaptive_grid.cells + @test cell isa OctreeBWG + cell.leaves[1] == OctantBWG(2,0,1,cell.b) + end + #simple first and second level refinement + # first case + # x-----------x-----------x + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # x-----x-----x-----------| + # | | | | + # | | | | + # | | | | + # x--x--x-----x | + # | | | | | + # x--x--x | | + # | | | | | + # x--x--x-----x-----------x + refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + @test length(adaptive_gird.cells[1].leaves) == 4 + for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) + @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) + end + refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + # octree holds now 3 first level and 4 second level + @test length(adaptive_gird.cells[1].leaves) == 7 + for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) + @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) + end + # second case + # x-----------x-----------x + # | | | + # | | | + # | | | + # | | | + # | | | + # x-----x--x--x-----------x + # | | | | | + # | x--x--x | + # | | | | | + # x-----x--x--x | + # | | | | + # | | | | + # x-----x-----x-----------x + adaptive_grid = ForestBWG(grid,3) + refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + @test length(adaptive_gird.cells[1].leaves) == 7 + @test all(getproperty.(adaptive.grid.cells[1].leaves[1:3],:l) .== 1) +end From 9d917175a840e7d85f536117a43eb4d0bd139279 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 10 Oct 2022 16:20:23 +0200 Subject: [PATCH 028/143] refinement works in 2D --- src/Adaptivity/AdaptiveCells.jl | 39 +++++++++++++++++---------------- src/exports.jl | 1 - test/runtests.jl | 1 + test/test_octant.jl | 26 +++++++++++----------- 4 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 1f39cf8f8c..4b02a1fa50 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -49,15 +49,14 @@ struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} end function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} - #TODO write generic morton idx identification - morton_o = findfirst(x->x==o,octree.leaves) + leave_idx = findfirst(x->x==o,octree.leaves) # how to obtain id from morton index ? - old_octant = popat!(octree.leaves,morton_o) - self_child_id = child_id(old_octant,octree.b) - start_child_id = self_child_id * (self_child_id != 1 ? (self_child_id - 1) : (1)) # shift morton idx about one level - for child_mort_id in start_child_id+1:(start_child_id+N) - insert!(octree.leaves,morton_o,OctantBWG(dim,old_octant.l+1,child_mort_id,octree.b)) - morton_o += 1 + old_octant = popat!(octree.leaves,leave_idx) + start_child_id = morton(old_octant,old_octant.l,octree.b) + end_child_id = start_child_id + N-1 + for child_mort_id in start_child_id:end_child_id + insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+1,child_mort_id,octree.b)) + leave_idx += 1 end end @@ -68,15 +67,17 @@ OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) -function morton(o::OctantBWG{dim,N,M},b=_maxlevel[dim==2 ? 2 : 1]) where {dim,N,M} - recursive_o = OctantBWG{dim,N,M}(o.l,o.xyz) - level = recursive_o.l - morton_idx = 0 - while level > 0 - morton_idx += child_id(recursive_o,b) - level -= 1 - recursive_o = parent(recursive_o,b) - end +# TODO: parametrize in unsigned length +# dispatch 3D Case +function morton(o::OctantBWG{2},l::Integer,b::Integer) + x = UInt32(o.xyz[1]) + y = UInt32(o.xyz[2]) + id = UInt64(0) + for i in 0:62 + id = id | ((x & (1 << i)) << i) + id = id | ((y & (1 << i)) << (i+1)) + end + return (id >> (b-l))+1 end """ @@ -170,9 +171,9 @@ of `octant`, respectively. These computed octants are called first and last desc since they are connected to `octant` by a path down the octree to the maximum level `b` """ function descendants(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} - l1 = octant.l+1; l2 = octant.l+1 # not sure + l1 = b; l2 = b # not sure h = _compute_size(b,octant.l) - return OctantBWG{dim,N,M}(l1,octant.xyz), OctantBWG{dim,N,M}(l2,octant.xyz .+ (h-2)) + return OctantBWG{dim,N,M}(l1,octant.xyz), OctantBWG{dim,N,M}(l2,octant.xyz .+ (h-1)) end function face_neighbor(octant::OctantBWG{dim,N,M}, f::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M} diff --git a/src/exports.jl b/src/exports.jl index ce7e4367d0..a5c5a427ac 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -91,7 +91,6 @@ export compute_vertex_values, # AdaptiveGrid ForestBWG, - make_adaptive, OctreeBWG, OctantBWG, diff --git a/test/runtests.jl b/test/runtests.jl index def659697e..094be2b2fc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,6 +7,7 @@ using Random using LinearAlgebra using SparseArrays +include("test_octant.jl") include("test_utils.jl") include("test_interpolations.jl") include("test_cellvalues.jl") diff --git a/test/test_octant.jl b/test/test_octant.jl index 087f47250a..374984a126 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -102,8 +102,8 @@ end @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,2,0)) @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,-2)) @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,2)) - @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(2,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,0,0))) - @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(2,0,0)), Ferrite.OctantBWG{3,8,6}(2,(4,2,2))) + #@test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(2,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,0,0))) + #@test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(2,0,0)), Ferrite.OctantBWG{3,8,6}(2,(4,2,2))) o = Ferrite.OctantBWG{3,8,6}(1,(0,0,0)) @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG{3,8,6}(1,(-2,0,0)) @@ -113,8 +113,8 @@ end @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,-2)) @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,2)) o = Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) - @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(0,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,2,2))) - @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(0,0,0)), Ferrite.OctantBWG{3,8,6}(2,(6,6,6))) + #@test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(0,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,2,2))) + #@test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(0,0,0)), Ferrite.OctantBWG{3,8,6}(2,(6,6,6))) @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),1,3) == Ferrite.OctantBWG{3,8,6}(2,(2,-2,-2)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),4,3) == Ferrite.OctantBWG{3,8,6}(2,(2,2,2)) @@ -170,16 +170,16 @@ end # x--x--x | | # | | | | | # x--x--x-----x-----------x - refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - @test length(adaptive_gird.cells[1].leaves) == 4 + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + @test length(adaptive_grid.cells[1].leaves) == 4 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) end - refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) # octree holds now 3 first level and 4 second level - @test length(adaptive_gird.cells[1].leaves) == 7 + @test length(adaptive_grid.cells[1].leaves) == 7 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) + @test octant == OctantBWG(2,2,m,adaptive_grid.cells[1].b) end # second case # x-----------x-----------x @@ -197,8 +197,8 @@ end # | | | | # x-----x-----x-----------x adaptive_grid = ForestBWG(grid,3) - refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - @test length(adaptive_gird.cells[1].leaves) == 7 - @test all(getproperty.(adaptive.grid.cells[1].leaves[1:3],:l) .== 1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + @test length(adaptive_grid.cells[1].leaves) == 7 + @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) end From 5199e0b7b25b243f19850c4e95f80b2df7add4f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 10 Oct 2022 19:43:03 +0200 Subject: [PATCH 029/143] add morton dispatch for dimension 3 and test cases for dimension 3 --- src/Adaptivity/AdaptiveCells.jl | 13 +++++++++++++ test/runtests.jl | 32 ++++++++++++++++---------------- test/test_octant.jl | 23 +++++++++++++++++++++++ 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 4b02a1fa50..0b95b48860 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -80,6 +80,19 @@ function morton(o::OctantBWG{2},l::Integer,b::Integer) return (id >> (b-l))+1 end +function morton(o::OctantBWG{3},l::Integer,b::Integer) + x = UInt32(o.xyz[1]) + y = UInt32(o.xyz[2]) + z = UInt32(o.xyz[3]) + id = UInt64(0) + for i in 0:62 + id = id | ((x & (1 << i)) << i) + id = id | ((y & (1 << i)) << (i+1)) + id = id | ((z & (1 << i)) << (i+2)) + end + return (id >> (b-l))+1 +end + """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] diff --git a/test/runtests.jl b/test/runtests.jl index 094be2b2fc..8875824765 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,20 +8,20 @@ using LinearAlgebra using SparseArrays include("test_octant.jl") -include("test_utils.jl") -include("test_interpolations.jl") -include("test_cellvalues.jl") -include("test_facevalues.jl") -include("test_quadrules.jl") -include("test_assemble.jl") -include("test_dofs.jl") -include("test_constraints.jl") -include("test_grid_dofhandler_vtk.jl") -include("test_abstractgrid.jl") -include("test_mixeddofhandler.jl") -include("test_l2_projection.jl") -include("test_pointevaluation.jl") -# include("test_notebooks.jl") -include("test_apply_rhs.jl") -include("test_examples.jl") +#include("test_utils.jl") +#include("test_interpolations.jl") +#include("test_cellvalues.jl") +#include("test_facevalues.jl") +#include("test_quadrules.jl") +#include("test_assemble.jl") +#include("test_dofs.jl") +#include("test_constraints.jl") +#include("test_grid_dofhandler_vtk.jl") +#include("test_abstractgrid.jl") +#include("test_mixeddofhandler.jl") +#include("test_l2_projection.jl") +#include("test_pointevaluation.jl") +## include("test_notebooks.jl") +#include("test_apply_rhs.jl") +#include("test_examples.jl") @test all(x -> isdefined(Ferrite, x), names(Ferrite)) # Test that all exported symbols are defined diff --git a/test/test_octant.jl b/test/test_octant.jl index 374984a126..53b575c8f6 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -201,4 +201,27 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) + ######################### + # now do the same with 3D + # some ascii picasso can insert here something beautiful + ######################### + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + @test length(adaptive_grid.cells[1].leaves) == 8 + for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) + @test octant == OctantBWG(3,1,m,adaptive_grid.cells[1].b) + end + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + @test length(adaptive_grid.cells[1].leaves) == 15 + for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) + @test octant == OctantBWG(3,2,m,adaptive_grid.cells[1].b) + end + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + @test length(adaptive_grid.cells[1].leaves) == 15 + @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) + @test all(getproperty.(adaptive_grid.cells[1].leaves[4:11],:l) .== 2) + @test all(getproperty.(adaptive_grid.cells[1].leaves[12:end],:l) .== 1) end From f3eadee649f07e69b924a3fa9c9133f593c4e68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 11 Oct 2022 15:10:57 +0200 Subject: [PATCH 030/143] morton index tests, now works properly, need to generalize with a for loop --- src/Adaptivity/AdaptiveCells.jl | 79 ++++++++++++++++++++------------- test/test_octant.jl | 23 ++++++++-- 2 files changed, 66 insertions(+), 36 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 0b95b48860..2ce15208aa 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -11,17 +11,17 @@ struct OctantBWG{dim, N, M} <: AbstractCell{dim,N,M} #Refinement level l::UInt #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} - xyz::NTuple{dim,Int} + xyz::NTuple{dim,Int} end """ OctantBWG(dim::Integer, l::Integer, b::Integer, m::Integer) Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ -function OctantBWG(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[dim-1]) +function OctantBWG(dim::T, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: Integer @assert l ≤ b #maximum refinement level exceeded - @assert m ≤ 2^(dim*l) - x,y,z = (0,0,0) + @assert m ≤ (one(T)+one(T))^(dim*l) + x,y,z = (zero(T),zero(T),zero(T)) h = _compute_size(b,l) for i in 0:l-1 x = x | (h*((m-1) & 2^(dim*i))÷2^((dim-1)*i)) @@ -37,10 +37,51 @@ function OctantBWG(dim::Integer, l::Integer, m::Integer, b::Integer=_maxlevel[di end end +# TODO: parametrize in unsigned length +# From BWG 2011 +# > The octant coordinates are stored as integers of a fixed number b of bits, +# > where the highest (leftmost) bit represents the first vertical level of the +# > octree (counting the root as level zero), the second highest bit the second level of the octree, and so on. +# Morton Index can thus be constructed by interleaving the integer bits: +# m(Oct) := (y_b,x_b,y_b-1,x_b-1,...y0,x0)_2 +# further we assume the following +# > Due to the two-complement representation of integers in practically all current hardware, +# > where the highest digit denotes the negated appropriate power of two, bitwise operations as used, +# > for example, in Algorithm 1 yield the correct result even for negative coordinates. +# also from BWG 2011 +# TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ +function morton(o::OctantBWG{2},l::Integer,b::Integer) + x = Int32(o.xyz[1]) + y = Int32(o.xyz[2]) + id = Int64(0) + # first shift extract i-th bit and second shift extracts inserts it at interleaved index + for i in 0:31 + id = id | ((x & (1 << i)) << i) + id = id | ((y & (1 << i)) << (i+1)) + end + # discard the bit information about deeper levels + return (id >> ((b-l)*2))+1 +end + +function morton(o::OctantBWG{3},l::Integer,b::Integer) + x = Int32(o.xyz[1]) + y = Int32(o.xyz[2]) + z = Int32(o.xyz[3]) + id = Int64(0) + for i in 0:20 + id = id | ((x & (1 << i)) << (2*i)) + id = id | ((y & (1 << i)) << (2*i+1)) + id = id | ((z & (1 << i)) << (2*i+2)) + end + # discard the bit information about deeper levels + return (id >> ((b-l)*3))+1 +end + Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) # Follow z order, x before y before z for faces, edges and corners +# TODO: consider list instead of Vector datastructure struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M}} #maximum refinement level @@ -55,7 +96,7 @@ function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N start_child_id = morton(old_octant,old_octant.l,octree.b) end_child_id = start_child_id + N-1 for child_mort_id in start_child_id:end_child_id - insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+1,child_mort_id,octree.b)) + insert!(octree.leaves,leave_idx,OctantBWG(dim,Int(old_octant.l+1),child_mort_id,Int(octree.b))) #TODO remove me after introducing parametrization leave_idx += 1 end end @@ -67,32 +108,6 @@ OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) -# TODO: parametrize in unsigned length -# dispatch 3D Case -function morton(o::OctantBWG{2},l::Integer,b::Integer) - x = UInt32(o.xyz[1]) - y = UInt32(o.xyz[2]) - id = UInt64(0) - for i in 0:62 - id = id | ((x & (1 << i)) << i) - id = id | ((y & (1 << i)) << (i+1)) - end - return (id >> (b-l))+1 -end - -function morton(o::OctantBWG{3},l::Integer,b::Integer) - x = UInt32(o.xyz[1]) - y = UInt32(o.xyz[2]) - z = UInt32(o.xyz[3]) - id = UInt64(0) - for i in 0:62 - id = id | ((x & (1 << i)) << i) - id = id | ((y & (1 << i)) << (i+1)) - id = id | ((z & (1 << i)) << (i+2)) - end - return (id >> (b-l))+1 -end - """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] @@ -110,7 +125,7 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} topology::ExclusiveTopology end -function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim==2 ? 2 : 1]) where dim +function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim-1]) where dim cells = getcells(grid) C = eltype(cells) @assert isconcretetype(C) diff --git a/test/test_octant.jl b/test/test_octant.jl index 53b575c8f6..0703a9dfad 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -92,6 +92,21 @@ end @test Ferrite.child_id(Ferrite.OctantBWG(3,2,24,3),3) == 8 @test Ferrite.child_id(Ferrite.OctantBWG(3,2,64,3),3) == 8 @test Ferrite.child_id(Ferrite.OctantBWG(3,2,9,3),3) == 1 + #maxlevel = 10 takes too long + maxlevel = 6 + levels = collect(1:maxlevel) + morton_ids = [1:2^(2*l) for l in levels] + for (level,morton_range) in zip(levels,morton_ids) + for morton_id in morton_range + @test Int(Ferrite.morton(OctantBWG(2,level,morton_id,maxlevel),level,maxlevel)) == morton_id + end + end + morton_ids = [1:2^(3*l) for l in levels] + for (level,morton_range) in zip(levels,morton_ids) + for morton_id in morton_range + @test Int(Ferrite.morton(OctantBWG(3,level,morton_id,maxlevel),level,maxlevel)) == morton_id + end + end end @testset "OctantBWG Operations" begin @@ -149,7 +164,7 @@ end adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells @test cell isa OctreeBWG - cell.leaves[1] == OctantBWG(2,0,1,cell.b) + @test cell.leaves[1] == OctantBWG(2,0,1,Int(cell.b)) end #simple first and second level refinement # first case @@ -173,13 +188,13 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 4 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) + @test octant == OctantBWG(2,1,m,Int(adaptive_grid.cells[1].b)) end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) # octree holds now 3 first level and 4 second level @test length(adaptive_grid.cells[1].leaves) == 7 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,2,m,adaptive_grid.cells[1].b) + @test octant == OctantBWG(2,2,m,Int(adaptive_grid.cells[1].b)) end # second case # x-----------x-----------x @@ -210,7 +225,7 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 8 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,1,m,adaptive_grid.cells[1].b) + @test octant == OctantBWG(3,1,m,Int(adaptive_grid.cells[1].b)) end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 15 From 9e92ad67db355bfb1e34e4d46fbbaded7b94d349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Oct 2022 08:40:12 +0200 Subject: [PATCH 031/143] found a bug in refine included unit tests for all cells refinement down to the max level --- src/Adaptivity/AdaptiveCells.jl | 2 +- test/test_octant.jl | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2ce15208aa..1313d4a98c 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -93,7 +93,7 @@ function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N leave_idx = findfirst(x->x==o,octree.leaves) # how to obtain id from morton index ? old_octant = popat!(octree.leaves,leave_idx) - start_child_id = morton(old_octant,old_octant.l,octree.b) + start_child_id = morton(old_octant,old_octant.l+1,octree.b) end_child_id = start_child_id + N-1 for child_mort_id in start_child_id:end_child_id insert!(octree.leaves,leave_idx,OctantBWG(dim,Int(old_octant.l+1),child_mort_id,Int(octree.b))) #TODO remove me after introducing parametrization diff --git a/test/test_octant.jl b/test/test_octant.jl index 0703a9dfad..8da23ee234 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -216,6 +216,27 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) + ################################################### + ####uniform refinement for all cells and levels#### + ################################################### + adaptive_grid = ForestBWG(grid,8) + function refine_all(grid::ForestBWG,l) + for tree in adaptive_grid.cells + for leaf in tree.leaves + if leaf.l != l-1 #maxlevel + continue + else + Ferrite.refine!(tree,leaf) + end + end + end + end + for l in 1:8 + refine_all(adaptive_grid,l) + for tree in adaptive_grid.cells + @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) + end + end ######################### # now do the same with 3D # some ascii picasso can insert here something beautiful @@ -230,7 +251,7 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 15 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,2,m,adaptive_grid.cells[1].b) + @test octant == OctantBWG(3,2,m,Int(adaptive_grid.cells[1].b)) end adaptive_grid = ForestBWG(grid,3) Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @@ -239,4 +260,11 @@ end @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) @test all(getproperty.(adaptive_grid.cells[1].leaves[4:11],:l) .== 2) @test all(getproperty.(adaptive_grid.cells[1].leaves[12:end],:l) .== 1) + adaptive_grid = ForestBWG(grid,5) + for l in 1:5 + refine_all(adaptive_grid,l) + for tree in adaptive_grid.cells + @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) + end + end end From ee6489f6257a9b9a7b2e9d4729dbdf78679e7880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Oct 2022 09:11:51 +0200 Subject: [PATCH 032/143] add parents test --- test/test_octant.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/test_octant.jl b/test/test_octant.jl index 8da23ee234..9f5bd545ee 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -265,6 +265,11 @@ end refine_all(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) + # reconstruct parents + parents = unique(Ferrite.parent.(tree.leaves,5)) + @test length(parents) == 2^(3*(l-1)) + morton_parents = Ferrite.morton.(parents,l-1,5) + @test all(morton_parents .== collect(1:2^(3*(l-1)))) end end end From d309d3441e089b1b5977d1a9a5b5dc99d1e695d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Oct 2022 09:58:26 +0200 Subject: [PATCH 033/143] add coarsening with tests; remove the parents test from before (sanity checked within coarsening tests) --- src/Adaptivity/AdaptiveCells.jl | 15 ++++++++++++++ test/test_octant.jl | 35 +++++++++++++++++++++++++-------- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 1313d4a98c..d24957b8d2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -90,6 +90,7 @@ struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} end function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} + # really the only way? leave_idx = findfirst(x->x==o,octree.leaves) # how to obtain id from morton index ? old_octant = popat!(octree.leaves,leave_idx) @@ -101,6 +102,20 @@ function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N end end +function coarsen!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} + leave_idx = findfirst(x->x==o,octree.leaves) + shift = child_id(o,octree.b) - 1 + if shift != 0 + old_morton = morton(o,o.l,octree.b) + o = OctantBWG(dim,Int(o.l),old_morton,Int(octree.b)) #TODO fix after parametrization + end + window_start = leave_idx - shift + window_length = 2^dim - 1 + new_octant = parent(o, octree.b) + octree.leaves[leave_idx - shift] = new_octant + deleteat!(octree.leaves,leave_idx-shift+1:leave_idx-shift+window_length) +end + OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4,4}(cell.nodes,b) diff --git a/test/test_octant.jl b/test/test_octant.jl index 9f5bd545ee..92dc48b2a3 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -216,9 +216,9 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) - ################################################### - ####uniform refinement for all cells and levels#### - ################################################### + ################################################################## + ####uniform refinement and coarsening for all cells and levels#### + ################################################################## adaptive_grid = ForestBWG(grid,8) function refine_all(grid::ForestBWG,l) for tree in adaptive_grid.cells @@ -231,12 +231,28 @@ end end end end + function coarsen_all(adaptive_grid) + for tree in adaptive_grid.cells + for leaf in tree.leaves + if Ferrite.child_id(leaf,tree.b) == 1 + Ferrite.coarsen!(tree,leaf) + end + end + end + end for l in 1:8 refine_all(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end end + #now go back from finest to coarsest + for l in 7:-1:0 + coarsen_all(adaptive_grid) + for tree in adaptive_grid.cells + @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) + end + end ######################### # now do the same with 3D # some ascii picasso can insert here something beautiful @@ -261,15 +277,18 @@ end @test all(getproperty.(adaptive_grid.cells[1].leaves[4:11],:l) .== 2) @test all(getproperty.(adaptive_grid.cells[1].leaves[12:end],:l) .== 1) adaptive_grid = ForestBWG(grid,5) + #go from coarsest to finest uniformly for l in 1:5 refine_all(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) - # reconstruct parents - parents = unique(Ferrite.parent.(tree.leaves,5)) - @test length(parents) == 2^(3*(l-1)) - morton_parents = Ferrite.morton.(parents,l-1,5) - @test all(morton_parents .== collect(1:2^(3*(l-1)))) end end + #now go back from finest to coarsest + for l in 4:-1:0 + coarsen_all(adaptive_grid) + for tree in adaptive_grid.cells + @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) + end + end end From 21caf8092aec3826600c31883335c00e20ed9bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Oct 2022 12:18:01 +0200 Subject: [PATCH 034/143] dimension generalization of morton function; parametrization of coordinates and everything related --- src/Adaptivity/AdaptiveCells.jl | 194 +++++++++++++++++--------------- test/test_octant.jl | 123 ++++++++------------ 2 files changed, 149 insertions(+), 168 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index d24957b8d2..9f2fcf0833 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -7,37 +7,48 @@ function set_maxlevel(dim::Integer,maxlevel::Integer) _maxlevel[dim-1] = maxlevel end -struct OctantBWG{dim, N, M} <: AbstractCell{dim,N,M} +struct OctantBWG{dim, N, M, T} <: AbstractCell{dim,N,M} #Refinement level - l::UInt + l::T #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} - xyz::NTuple{dim,Int} + xyz::NTuple{dim,T} end """ OctantBWG(dim::Integer, l::Integer, b::Integer, m::Integer) Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ -function OctantBWG(dim::T, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: Integer +function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: Integer @assert l ≤ b #maximum refinement level exceeded @assert m ≤ (one(T)+one(T))^(dim*l) x,y,z = (zero(T),zero(T),zero(T)) - h = _compute_size(b,l) - for i in 0:l-1 - x = x | (h*((m-1) & 2^(dim*i))÷2^((dim-1)*i)) - y = y | (h*((m-1) & 2^(dim*i+1))÷2^((dim-1)*i+1)) - z = z | (h*((m-1) & 2^(dim*i+2))÷2^((dim-1)*i+2)) + h = Int32(_compute_size(b,l)) + _zero = zero(T) + _one = one(T) + _two = _one + _one + for i in _zero:l-_one + x = x | (h*((m-_one) & _two^(dim*i))÷_two^((dim-_one)*i)) + y = y | (h*((m-_one) & _two^(dim*i+_one))÷_two^((dim-_one)*i+_one)) + z = z | (h*((m-_one) & _two^(dim*i+_two))÷_two^((dim-_one)*i+_two)) end if dim == 2 - OctantBWG{dim,4,4}(l,(x,y)) + OctantBWG{dim,4,4,T}(l,(x,y)) elseif dim == 3 - OctantBWG{dim,8,6}(l,(x,y,z)) + OctantBWG{dim,8,6,T}(l,(x,y,z)) else error("$dim Dimension not supported") end end -# TODO: parametrize in unsigned length +OctantBWG(dim::Int,l::Int,m::Int,b::Int=_maxlevel[dim-1]) = OctantBWG(dim,Int32(l),Int32(m),Int32(b)) +OctantBWG(dim::Int,l::Int,m::Int,b::Int32) = OctantBWG(dim,Int32(l),Int32(m),b) +OctantBWG(dim::Int,l::Int32,m::Int,b::Int32) = OctantBWG(dim,l,Int32(m),b) +OctantBWG(level::Int,coords::NTuple) = OctantBWG(Int32(level),Int32.(coords)) +OctantBWG(level::Int32,coords::NTuple) = OctantBWG(level,Int32.(coords)) +function OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim + dim == 2 ? OctantBWG{2,4,4,Int32}(level,coords) : OctantBWG{3,8,6,Int32}(level,coords) +end + # From BWG 2011 # > The octant coordinates are stored as integers of a fixed number b of bits, # > where the highest (leftmost) bit represents the first vertical level of the @@ -50,31 +61,17 @@ end # > for example, in Algorithm 1 yield the correct result even for negative coordinates. # also from BWG 2011 # TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ -function morton(o::OctantBWG{2},l::Integer,b::Integer) - x = Int32(o.xyz[1]) - y = Int32(o.xyz[2]) - id = Int64(0) - # first shift extract i-th bit and second shift extracts inserts it at interleaved index - for i in 0:31 - id = id | ((x & (1 << i)) << i) - id = id | ((y & (1 << i)) << (i+1)) - end - # discard the bit information about deeper levels - return (id >> ((b-l)*2))+1 -end - -function morton(o::OctantBWG{3},l::Integer,b::Integer) - x = Int32(o.xyz[1]) - y = Int32(o.xyz[2]) - z = Int32(o.xyz[3]) - id = Int64(0) - for i in 0:20 - id = id | ((x & (1 << i)) << (2*i)) - id = id | ((y & (1 << i)) << (2*i+1)) - id = id | ((z & (1 << i)) << (2*i+2)) +function morton(o::OctantBWG{dim,N,M,T},l::Integer,b::Integer) where {dim,N,M,T} + id = zero(widen(eltype(o.xyz))) + loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - one(T) + for i in zero(T):loop_length + for d in zero(T):dim-one(T) + # first shift extract i-th bit and second shift inserts it at interleaved index + id = id | ((o.xyz[d+one(T)] & (one(T) << i)) << ((dim-one(T))*i+d)) + end end # discard the bit information about deeper levels - return (id >> ((b-l)*3))+1 + return (id >> ((b-l)*dim))+one(T) end Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) @@ -82,42 +79,43 @@ Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) # Follow z order, x before y before z for faces, edges and corners # TODO: consider list instead of Vector datastructure -struct OctreeBWG{dim,N,M} <: AbstractAdaptiveCell{dim,N,M} - leaves::Vector{OctantBWG{dim,N,M}} +struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} + leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level - b::UInt + b::T nodes::NTuple{N,Int} end -function refine!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} +function refine!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} # really the only way? leave_idx = findfirst(x->x==o,octree.leaves) # how to obtain id from morton index ? old_octant = popat!(octree.leaves,leave_idx) - start_child_id = morton(old_octant,old_octant.l+1,octree.b) - end_child_id = start_child_id + N-1 + start_child_id = morton(old_octant,old_octant.l+one(T),octree.b) + end_child_id = start_child_id + N-one(T) for child_mort_id in start_child_id:end_child_id - insert!(octree.leaves,leave_idx,OctantBWG(dim,Int(old_octant.l+1),child_mort_id,Int(octree.b))) #TODO remove me after introducing parametrization + insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+one(T),child_mort_id,octree.b)) #TODO remove me after introducing parametrization leave_idx += 1 end end -function coarsen!(octree::OctreeBWG{dim,N,M}, o::OctantBWG{dim,N,M}) where {dim,N,M} +function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T} + _two = T(2) leave_idx = findfirst(x->x==o,octree.leaves) - shift = child_id(o,octree.b) - 1 - if shift != 0 + shift = child_id(o,octree.b) - one(T) + if shift != zero(T) old_morton = morton(o,o.l,octree.b) - o = OctantBWG(dim,Int(o.l),old_morton,Int(octree.b)) #TODO fix after parametrization + o = OctantBWG(dim,o.l,old_morton,octree.b) end window_start = leave_idx - shift - window_length = 2^dim - 1 + window_length = _two^dim - one(T) new_octant = parent(o, octree.b) octree.leaves[leave_idx - shift] = new_octant - deleteat!(octree.leaves,leave_idx-shift+1:leave_idx-shift+window_length) + deleteat!(octree.leaves,leave_idx-shift+one(T):leave_idx-shift+window_length) end -OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6}([zero(OctantBWG{3,8,6})],b,nodes) -OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4}([zero(OctantBWG{2,4,4})],b,nodes) +OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6,Int32}([zero(OctantBWG{3,8,6})],Int32(b),nodes) +OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4,Int32}([zero(OctantBWG{2,4,4})],Int32(b),nodes) OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4,4}(cell.nodes,b) OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) @@ -197,11 +195,11 @@ function child_id(octant::OctantBWG{2},b::Integer=_maxlevel[1]) return i+0x01 end -function parent(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} - if octant.l > 0 - h = _compute_size(b,octant.l) - l = octant.l - 0x01 - return OctantBWG{dim,N,M}(l,octant.xyz .& ~h) +function parent(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T} + if octant.l > zero(T) + h = T(_compute_size(b,octant.l)) + l = octant.l - one(T) + return OctantBWG(l,octant.xyz .& ~h) else error("root has no parent") end @@ -213,77 +211,89 @@ Given an `octant`, computes the two smallest possible octants that fit into the of `octant`, respectively. These computed octants are called first and last descendants of `octant` since they are connected to `octant` by a path down the octree to the maximum level `b` """ -function descendants(octant::OctantBWG{dim,N,M}, b::Integer=_maxlevel[dim-1]) where {dim,N,M} +function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T} l1 = b; l2 = b # not sure - h = _compute_size(b,octant.l) - return OctantBWG{dim,N,M}(l1,octant.xyz), OctantBWG{dim,N,M}(l2,octant.xyz .+ (h-1)) + h = T(_compute_size(b,octant.l)) + return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end -function face_neighbor(octant::OctantBWG{dim,N,M}, f::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M} +function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1]) where {dim,N,M,T} l = octant.l - h = _compute_size(b,octant.l) + h = T(_compute_size(b,octant.l)) x,y,z = octant.xyz - x += ((f == 1) ? -h : ((f == 2) ? h : 0)) - y += ((f == 3) ? -h : ((f == 4) ? h : 0)) - z += ((f == 5) ? -h : ((f == 6) ? h : 0)) - return OctantBWG{dim,N,M}(l,(x,y,z)) + x += ((f == T(1)) ? -h : ((f == T(2)) ? h : zero(T))) + y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) + z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) + dim == 2 ? OctantBWG(l,(x,y)) : OctantBWG(l,(x,y,z)) end +#TODO: not nice don't know how to dispatch this +face_neighbor(o::OctantBWG{dim,N,M,T}, f::Int, b::Int) where {dim,N,M,T} = face_neighbor(o,T(f),T(b)) """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` """ -function edge_neighbor(octant::OctantBWG{3,N,M}, e::Integer, b::Integer=_maxlevel[2]) where {N,M} +function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) where {N,M,T} @assert 1 ≤ e ≤ 12 - e -= 1 + e -= one(T) l = octant.l - h = _compute_size(b,octant.l) + _one = one(T) + _two = T(2) + h = T(_compute_size(b,octant.l)) ox,oy,oz = octant.xyz - case = e ÷ 4 - if case == 0 + case = e ÷ T(4) + if case == zero(T) x = ox - y = oy + (2*(e & 0x01) - 1)*h - z = oz + ((e & 0x02) - 1)*h - return OctantBWG{3,N,M}(l,(x,y,z)) - elseif case == 1 - x = ox + (2*(e & 0x01) - 1)*h + y = oy + (_two*(e & _one) - one(T))*h + z = oz + ((e & _two) - _one)*h + return OctantBWG(l,(x,y,z)) + elseif case == one(T) + x = ox + (_two*(e & _one) - _one)*h y = oy - z = oz + ((e & 0x02) - 1)*h - return OctantBWG{3,N,M}(l,(x,y,z)) - elseif case == 2 - x = ox + (2*(e & 0x01) - 1)*h - y = oy + ((e & 0x02) - 1)*h + z = oz + ((e & _two) - _one)*h + return OctantBWG(l,(x,y,z)) + elseif case == _two + x = ox + (_two*(e & _one) - _one)*h + y = oy + ((e & _two) - _one)*h z = oz - return OctantBWG{3,N,M}(l,(x,y,z)) + return OctantBWG(l,(x,y,z)) else error("edge case not found") end end +#TODO: not nice don't know how to dispatch this +edge_neighbor(o::OctantBWG{3,N,M,T}, e::Int, b::Int) where {N,M,T} = edge_neighbor(o,T(e),T(b)) """ corner_neighbor(octant::OctantBWG, c::Integer, b::Integer) Computes the corner neighbor octant which is only connected by the corner `c` to `octant` """ -function corner_neighbor(octant::OctantBWG{3,N,M}, c::Integer, b::Integer=_maxlevel[2]) where {N,M} - c -= 1 +function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) where {N,M,T} + c -= one(T) l = octant.l - h = _compute_size(b,octant.l) + h = T(_compute_size(b,octant.l)) ox,oy,oz = octant.xyz - x = ox + (2*(c & 1) - 1)*h - y = oy + ((c & 2) - 1)*h - z = oz + ((c & 4)/2 - 1)*h - return OctantBWG{3,N,M}(l,(x,y,z)) + _one = one(T) + _two = T(2) + x = ox + (_two*(c & _one) - _one)*h + y = oy + ((c & _two) - _one)*h + z = oz + ((c & T(4))÷_two - _one)*h + return OctantBWG(l,(x,y,z)) end -function corner_neighbor(octant::OctantBWG{2,N,M}, c::Integer, b::Integer=_maxlevel[1]) where {N,M} - c -= 1 +function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) where {N,M,T} + c -= one(T) l = octant.l h = _compute_size(b,octant.l) ox,oy = octant.xyz - x = ox + (2*(c & 1) - 1)*h - y = oy + ((c & 2) - 1)*h - return OctantBWG{2,N,M}(l,(x,y)) + _one = one(T) + _two = T(2) + x = ox + (_two*(c & _one) - _one)*h + y = oy + ((c & _two) - _one)*h + return OctantBWG(l,(x,y)) end +#TODO: not nice don't know how to dispatch this +corner_neighbor(o::OctantBWG{dim,N,M,T}, c::Int, b::Int) where {dim,N,M,T} = corner_neighbor(o,T(c),T(b)) function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{3,N,M}) where {N,M} x,y,z = o.xyz diff --git a/test/test_octant.jl b/test/test_octant.jl index 92dc48b2a3..9897511962 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -37,48 +37,19 @@ end @testset "OctantBWG Encoding" begin - # Tests from Figure 3a) and 3b) of Burstedde et al - o = Ferrite.OctantBWG{3,8,6}(2,(0,4,2)) - b = 0x03 +# # Tests from Figure 3a) and 3b) of Burstedde et al + o = Ferrite.OctantBWG(3,2,21,3) + b = 3 @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - o = Ferrite.OctantBWG{3,8,6}(2,(2,2,0)) + o = Ferrite.OctantBWG(3,2,4,3) @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) - @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - - # Now I shift the root about (1,1,1) - o = Ferrite.OctantBWG{3,8,6}(2,(0,4,2) .+ 1) - b = 0x03 - @test Ferrite.child_id(o,b) == 5 - @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0) .+ 1) - @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - o = Ferrite.OctantBWG{3,8,6}(2,(2,2,0) .+ 1) - @test Ferrite.child_id(o,b) == 4 - @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG{3,8,6}(0,(0,0,0) .+ 1) + @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) - # coordinate system always on lowest level - # dim 3, level 2, morton id 2, number of levels 3 - @test Ferrite.OctantBWG(3,2,2,3) == Ferrite.OctantBWG{3,8,6}(2,(2,0,0)) - # dim 3, level 1, morton id 2, number of levels 3 - @test Ferrite.OctantBWG(3,1,2,3) == Ferrite.OctantBWG{3,8,6}(1,(4,0,0)) - # dim 3, level 0, morton id 2, number of levels 3 - @test_throws AssertionError Ferrite.OctantBWG(3,0,2,3) - # dim 3, level 2, morton id 4, number of levels 3 - @test Ferrite.OctantBWG(3,2,4,3) == Ferrite.OctantBWG{3,8,6}(2,(2,2,0)) - @test Ferrite.OctantBWG(3,1,4,3) == Ferrite.OctantBWG{3,8,6}(1,(4,4,0)) - @test Ferrite.OctantBWG(3,2,5,3) == Ferrite.OctantBWG{3,8,6}(2,(0,0,2)) - @test Ferrite.OctantBWG(3,1,5,3) == Ferrite.OctantBWG{3,8,6}(1,(0,0,4)) - @test Ferrite.OctantBWG(2,1,1,3) == Ferrite.OctantBWG{2,4,4}(1,(0,0)) - @test Ferrite.OctantBWG(2,1,2,3) == Ferrite.OctantBWG{2,4,4}(1,(4,0)) - @test Ferrite.OctantBWG(2,1,3,3) == Ferrite.OctantBWG{2,4,4}(1,(0,4)) - @test Ferrite.OctantBWG(2,1,4,3) == Ferrite.OctantBWG{2,4,4}(1,(4,4)) @test Ferrite.child_id(Ferrite.OctantBWG(2,1,1,3),3) == 1 @test Ferrite.child_id(Ferrite.OctantBWG(2,1,2,3),3) == 2 @test Ferrite.child_id(Ferrite.OctantBWG(2,1,3,3),3) == 3 @@ -110,47 +81,47 @@ end end @testset "OctantBWG Operations" begin - o = Ferrite.OctantBWG{3,8,6}(1,(2,0,0)) - @test Ferrite.face_neighbor(o,0x01,0x02) == Ferrite.OctantBWG{3,8,6}(1,(0,0,0)) - @test Ferrite.face_neighbor(o,0x02,0x02) == Ferrite.OctantBWG{3,8,6}(1,(4,0,0)) - @test Ferrite.face_neighbor(o,0x03,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,-2,0)) - @test Ferrite.face_neighbor(o,0x04,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,2,0)) - @test Ferrite.face_neighbor(o,0x05,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,-2)) - @test Ferrite.face_neighbor(o,0x06,0x02) == Ferrite.OctantBWG{3,8,6}(1,(2,0,2)) - #@test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(2,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,0,0))) - #@test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(2,0,0)), Ferrite.OctantBWG{3,8,6}(2,(4,2,2))) + o = Ferrite.OctantBWG(1,(2,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(0,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(4,0,0)) + @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(2,-2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(2,2,0)) + @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(2,0,-2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(2,0,2)) + @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(2,0,0)), Ferrite.OctantBWG(2,(3,1,1))) + @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(2,0,0)), Ferrite.OctantBWG(3,(5,3,3))) - o = Ferrite.OctantBWG{3,8,6}(1,(0,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG{3,8,6}(1,(-2,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG{3,8,6}(1,(2,0,0)) - @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG{3,8,6}(1,(0,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG{3,8,6}(1,(0,2,0)) - @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG{3,8,6}(1,(0,0,2)) - o = Ferrite.OctantBWG{3,8,6}(0,(0,0,0)) - #@test Ferrite.descendants(o,2) == (Ferrite.OctantBWG{3,8,6}(1,(0,0,0)), Ferrite.OctantBWG{3,8,6}(1,(2,2,2))) - #@test Ferrite.descendants(o,3) == (Ferrite.OctantBWG{3,8,6}(2,(0,0,0)), Ferrite.OctantBWG{3,8,6}(2,(6,6,6))) + o = Ferrite.OctantBWG(1,(0,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(-2,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(2,0,0)) + @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(0,-2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(0,2,0)) + @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(0,0,-2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(0,0,2)) + o = Ferrite.OctantBWG(0,(0,0,0)) + @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(0,0,0)), Ferrite.OctantBWG(2,(3,3,3))) + @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(0,0,0)), Ferrite.OctantBWG(3,(7,7,7))) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),1,3) == Ferrite.OctantBWG{3,8,6}(2,(2,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),4,3) == Ferrite.OctantBWG{3,8,6}(2,(2,2,2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),6,3) == Ferrite.OctantBWG{3,8,6}(2,(4,0,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),9,3) == Ferrite.OctantBWG{3,8,6}(2,(0,-2,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),12,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),1,3) == Ferrite.OctantBWG(2,(2,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),4,3) == Ferrite.OctantBWG(2,(2,2,2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),6,3) == Ferrite.OctantBWG(2,(4,0,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),9,3) == Ferrite.OctantBWG(2,(0,-2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),12,3) == Ferrite.OctantBWG(2,(4,2,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(3,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(3,(0,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(3,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(3,(2,2,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(2,(0,-4,-4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(2,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(2,(4,4,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(1,(0,0,0)),1,4) == Ferrite.OctantBWG{3,8,6}(1,(0,-8,-8)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG{3,8,6}(1,(0,0,0)),12,4) == Ferrite.OctantBWG{3,8,6}(1,(8,8,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),1,4) == Ferrite.OctantBWG(3,(0,-2,-2)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),12,4) == Ferrite.OctantBWG(3,(2,2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),1,4) == Ferrite.OctantBWG(2,(0,-4,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),12,4) == Ferrite.OctantBWG(2,(4,4,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),1,4) == Ferrite.OctantBWG(1,(0,-8,-8)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),12,4) == Ferrite.OctantBWG(1,(8,8,0)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),1,3) == Ferrite.OctantBWG{3,8,6}(2,(0,-2,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),4,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{3,8,6}(2,(2,0,0)),8,3) == Ferrite.OctantBWG{3,8,6}(2,(4,2,2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),1,3) == Ferrite.OctantBWG(2,(0,-2,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),4,3) == Ferrite.OctantBWG(2,(4,2,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),8,3) == Ferrite.OctantBWG(2,(4,2,2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),1,3) == Ferrite.OctantBWG{2,4,4}(2,(0,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),2,3) == Ferrite.OctantBWG{2,4,4}(2,(4,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG{2,4,4}(2,(2,0)),4,3) == Ferrite.OctantBWG{2,4,4}(2,(4,2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),1,3) == Ferrite.OctantBWG(2,(0,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),2,3) == Ferrite.OctantBWG(2,(4,-2)) + @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),4,3) == Ferrite.OctantBWG(2,(4,2)) end @testset "OctreeBWG Operations" begin @@ -164,7 +135,7 @@ end adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells @test cell isa OctreeBWG - @test cell.leaves[1] == OctantBWG(2,0,1,Int(cell.b)) + @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) end #simple first and second level refinement # first case @@ -188,13 +159,13 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 4 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,1,m,Int(adaptive_grid.cells[1].b)) + @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) # octree holds now 3 first level and 4 second level @test length(adaptive_grid.cells[1].leaves) == 7 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,2,m,Int(adaptive_grid.cells[1].b)) + @test octant == OctantBWG(2,2,m,adaptive_grid.cells[1].b) end # second case # x-----------x-----------x @@ -262,12 +233,12 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 8 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,1,m,Int(adaptive_grid.cells[1].b)) + @test octant == OctantBWG(3,1,m,adaptive_grid.cells[1].b) end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 15 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,2,m,Int(adaptive_grid.cells[1].b)) + @test octant == OctantBWG(3,2,m,adaptive_grid.cells[1].b) end adaptive_grid = ForestBWG(grid,3) Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) From ef8c905b92e723a7694f59790be7bff72c22d4b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Oct 2022 13:36:46 +0200 Subject: [PATCH 035/143] better but not perfect parametrization for *_neighbor dispatches --- src/Adaptivity/AdaptiveCells.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 9f2fcf0833..082f2b5da9 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -61,7 +61,7 @@ end # > for example, in Algorithm 1 yield the correct result even for negative coordinates. # also from BWG 2011 # TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ -function morton(o::OctantBWG{dim,N,M,T},l::Integer,b::Integer) where {dim,N,M,T} +function morton(o::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T} id = zero(widen(eltype(o.xyz))) loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - one(T) for i in zero(T):loop_length @@ -94,7 +94,7 @@ function refine!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {d start_child_id = morton(old_octant,old_octant.l+one(T),octree.b) end_child_id = start_child_id + N-one(T) for child_mort_id in start_child_id:end_child_id - insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+one(T),child_mort_id,octree.b)) #TODO remove me after introducing parametrization + insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+one(T),child_mort_id,octree.b)) leave_idx += 1 end end @@ -226,8 +226,8 @@ function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1] z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) dim == 2 ? OctantBWG(l,(x,y)) : OctantBWG(l,(x,y,z)) end -#TODO: not nice don't know how to dispatch this -face_neighbor(o::OctantBWG{dim,N,M,T}, f::Int, b::Int) where {dim,N,M,T} = face_neighbor(o,T(f),T(b)) +#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow +face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1,T2,T3} = face_neighbor(o,T1(f),T1(b)) """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) @@ -261,8 +261,8 @@ function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) wher error("edge case not found") end end -#TODO: not nice don't know how to dispatch this -edge_neighbor(o::OctantBWG{3,N,M,T}, e::Int, b::Int) where {N,M,T} = edge_neighbor(o,T(e),T(b)) +#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow +edge_neighbor(o::OctantBWG{3,N,M,T1}, e::T2, b::T3) where {N,M,T1,T2,T3} = edge_neighbor(o,T1(e),T1(b)) """ corner_neighbor(octant::OctantBWG, c::Integer, b::Integer) @@ -292,8 +292,8 @@ function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) wh y = oy + ((c & _two) - _one)*h return OctantBWG(l,(x,y)) end -#TODO: not nice don't know how to dispatch this -corner_neighbor(o::OctantBWG{dim,N,M,T}, c::Int, b::Int) where {dim,N,M,T} = corner_neighbor(o,T(c),T(b)) +#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow +corner_neighbor(o::OctantBWG{dim,N,M,T1}, c::T2, b::T3) where {dim,N,M,T1,T2,T3} = corner_neighbor(o,T1(c),T1(b)) function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{3,N,M}) where {N,M} x,y,z = o.xyz From ebe9af80702fc19454a379729cdae5e93baa8ab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 14 Oct 2022 11:00:59 +0200 Subject: [PATCH 036/143] dispatch as I wanted --- src/Adaptivity/AdaptiveCells.jl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 082f2b5da9..ddb87f04bf 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -61,7 +61,7 @@ end # > for example, in Algorithm 1 yield the correct result even for negative coordinates. # also from BWG 2011 # TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ -function morton(o::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T} +function morton(o::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Integer} id = zero(widen(eltype(o.xyz))) loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - one(T) for i in zero(T):loop_length @@ -73,6 +73,7 @@ function morton(o::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T} # discard the bit information about deeper levels return (id >> ((b-l)*dim))+one(T) end +morton(o::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = morton(o,T1(l),T1(b)) Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) @@ -217,7 +218,7 @@ function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end -function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1]) where {dim,N,M,T} +function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y,z = octant.xyz @@ -226,14 +227,13 @@ function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1] z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) dim == 2 ? OctantBWG(l,(x,y)) : OctantBWG(l,(x,y,z)) end -#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow -face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1,T2,T3} = face_neighbor(o,T1(f),T1(b)) +face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` """ -function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) where {N,M,T} +function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} @assert 1 ≤ e ≤ 12 e -= one(T) l = octant.l @@ -261,14 +261,13 @@ function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) wher error("edge case not found") end end -#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow -edge_neighbor(o::OctantBWG{3,N,M,T1}, e::T2, b::T3) where {N,M,T1,T2,T3} = edge_neighbor(o,T1(e),T1(b)) +edge_neighbor(o::OctantBWG{3,N,M,T1}, e::T2, b::T3) where {N,M,T1<:Integer,T2<:Integer,T3<:Integer} = edge_neighbor(o,T1(e),T1(b)) """ corner_neighbor(octant::OctantBWG, c::Integer, b::Integer) Computes the corner neighbor octant which is only connected by the corner `c` to `octant` """ -function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) where {N,M,T} +function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} c -= one(T) l = octant.l h = T(_compute_size(b,octant.l)) @@ -281,7 +280,7 @@ function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) wh return OctantBWG(l,(x,y,z)) end -function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) where {N,M,T} +function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) where {N,M,T<:Integer} c -= one(T) l = octant.l h = _compute_size(b,octant.l) @@ -292,8 +291,7 @@ function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) wh y = oy + ((c & _two) - _one)*h return OctantBWG(l,(x,y)) end -#TODO: cannot specify T1,T2,T3 to be of subtype Integer, stack overflow -corner_neighbor(o::OctantBWG{dim,N,M,T1}, c::T2, b::T3) where {dim,N,M,T1,T2,T3} = corner_neighbor(o,T1(c),T1(b)) +corner_neighbor(o::OctantBWG{dim,N,M,T1}, c::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = corner_neighbor(o,T1(c),T1(b)) function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{3,N,M}) where {N,M} x,y,z = o.xyz From b7102e3bf5125eb1b6f26cd9f143672f6c65eeb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 15 Oct 2022 15:49:16 +0200 Subject: [PATCH 037/143] start interfacing the grid interface; non fully working version of transform_face (I think works in 3D) --- src/Adaptivity/AdaptiveCells.jl | 39 ++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index ddb87f04bf..bb91816a20 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -155,6 +155,8 @@ function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim-1]) where dim return ForestBWG(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) end +getneighborhood(forest::ForestBWG,idx) = getneighborhood(forest.topology,forest,idx) + function getncells(grid::ForestBWG) numcells = 0 for tree in grid.cells @@ -163,6 +165,9 @@ function getncells(grid::ForestBWG) return numcells end +getcelltype(grid::ForestBWG) = eltype(grid.cells) +getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO + function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) println(io, "ForestBWG with ") println(io, " $(getncells(agrid)) cells") @@ -213,7 +218,7 @@ of `octant`, respectively. These computed octants are called first and last desc since they are connected to `octant` by a path down the octree to the maximum level `b` """ function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T} - l1 = b; l2 = b # not sure + l1 = b; l2 = b h = T(_compute_size(b,octant.l)) return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end @@ -229,6 +234,38 @@ function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1] end face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) +#TODO: this is not working in 2d as of now, indices used in the paper confuse me +function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} + _one = one(T2) + _two = T2(2) + #currently rotation not encoded + kprime, fprime = getneighborhood(forest,FaceIndex(k,f))[1] + a₂ = f ÷ 2; b₂ = fprime ÷ 2 + sprime = _one - ((f & _one) ⊻ (fprime & _one)) + s = zeros(T2,2) + b = zeros(T2,3) + r = 0 #no rotation information in face_neighbor currently + if dim == 2 + a₀ = 1 - a₂; b₀ = 1 - b₂; s[1] = r #no rotation as of now + else + a₀ = (f < 3) ? 1 : 0; a₁ = (f < 5) ? 2 : 1 + #u = ℛ[1,f] ⊻ ℛ[1,fprime] ⊻ T2((r == 1) | (r == 3)) + b[1] = (fprime < 3) ? 1 : 0; b[2] = (fprime < 5) ? 2 : 1 + #v = T2(ℛ[f,fprime] == 1) + s[1] = r & 2; s[2] = r & 3 + end + b = forest.cells[1].b + l = o.l; g = 2^b - 2^(b-l) + x = T2((s[1] == 1) ? o.xyz[1] : g - o.xyz[1]) + y = T2((s[2] == 1) ? o.xyz[2] : g - o.xyz[2]) + z = T2((_two*(fprime & 1) - 1)*2^b + sprime*g + (1-2*sprime)*o.xyz[2]) + if dim == 2 + return OctantBWG(l,(x,z)) + else + return OctantBWG(l,(x,y,z)) + end +end + """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` From 5f6ccd794c1fdbadc11999cab24e79b5ccd1b349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 26 Oct 2022 19:06:00 +0200 Subject: [PATCH 038/143] included getcells dispatch without index and with integer index, need to generalize for dim=2 and 3 --- src/Adaptivity/AdaptiveCells.jl | 56 ++++++++++++++++++++++++--------- test/test_octant.jl | 22 +++++++++++++ 2 files changed, 64 insertions(+), 14 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index bb91816a20..3d4cd4e5cb 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -61,19 +61,21 @@ end # > for example, in Algorithm 1 yield the correct result even for negative coordinates. # also from BWG 2011 # TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ -function morton(o::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Integer} - id = zero(widen(eltype(o.xyz))) - loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - one(T) - for i in zero(T):loop_length - for d in zero(T):dim-one(T) +function morton(octant::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Integer} + o = one(T) + z = zero(T) + id = zero(widen(eltype(octant.xyz))) + loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - o + for i in z:loop_length + for d in z:dim-o # first shift extract i-th bit and second shift inserts it at interleaved index - id = id | ((o.xyz[d+one(T)] & (one(T) << i)) << ((dim-one(T))*i+d)) + id = id | ((octant.xyz[d+o] & (o << i)) << ((dim-o)*i+d)) end end # discard the bit information about deeper levels - return (id >> ((b-l)*dim))+one(T) + return (id >> ((b-l)*dim))+o end -morton(o::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = morton(o,T1(l),T1(b)) +morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = morton(octant,T1(l),T1(b)) Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) @@ -87,20 +89,21 @@ struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} nodes::NTuple{N,Int} end -function refine!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} +function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} + o = one(T) # really the only way? - leave_idx = findfirst(x->x==o,octree.leaves) + leave_idx = findfirst(x->x==pivot_octant,octree.leaves) # how to obtain id from morton index ? old_octant = popat!(octree.leaves,leave_idx) - start_child_id = morton(old_octant,old_octant.l+one(T),octree.b) - end_child_id = start_child_id + N-one(T) + start_child_id = morton(old_octant,old_octant.l+o,octree.b) + end_child_id = start_child_id + N-o for child_mort_id in start_child_id:end_child_id - insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+one(T),child_mort_id,octree.b)) + insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+o,child_mort_id,octree.b)) leave_idx += 1 end end -function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T} +function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} _two = T(2) leave_idx = findfirst(x->x==o,octree.leaves) shift = child_id(o,octree.b) - one(T) @@ -165,12 +168,37 @@ function getncells(grid::ForestBWG) return numcells end +function getcells(forest::ForestBWG{dim}) where dim + celltype = dim == 2 ? OctantBWG{2,4,4,Int32} : OctantBWG{3,8,6,Int32} + ncells = getncells(forest) + cellvector = Vector{celltype}(undef,ncells) + o = one(Int32) + cellid = o + for tree in forest.cells + for leaf in tree.leaves + cellvector[cellid] = leaf + cellid += o + end + end + return cellvector +end + +function getcells(forest::ForestBWG{dim}, cellid::Int) where dim + nleaves = length.(forest.cells) + nleaves_cumsum = cumsum(nleaves) + k = findfirst(x->cellid<=x,nleaves_cumsum) + leaveid = cellid % 4 + leaveid = leaveid == 0 ? 4 : leaveid + return forest.cells[k].leaves[leaveid] +end + getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) println(io, "ForestBWG with ") println(io, " $(getncells(agrid)) cells") + println(io, " $(length(agrid.cells)) trees") end """ diff --git a/test/test_octant.jl b/test/test_octant.jl index 9897511962..4c3f5e2117 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -263,3 +263,25 @@ end end end end + +@testset "ForestBWG AbstractGrid Interfacing" begin + function refine_all(grid::ForestBWG,l) + for tree in adaptive_grid.cells + for leaf in tree.leaves + if leaf.l != l-1 #maxlevel + continue + else + Ferrite.refine!(tree,leaf) + end + end + end + end + maxlevel = 3 + grid = generate_grid(Quadrilateral,(2,2)) + adaptive_grid = ForestBWG(grid,maxlevel) + for l in 1:maxlevel + refine_all(adaptive_grid,l) + @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) + end + +end From 312cc480fbe0612d731eed2f1f767629e2cacbb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 26 Oct 2022 20:08:58 +0200 Subject: [PATCH 039/143] generalize getcells for idx::Int dispatch --- src/Adaptivity/AdaptiveCells.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 3d4cd4e5cb..0f403ff6ea 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -184,11 +184,12 @@ function getcells(forest::ForestBWG{dim}) where dim end function getcells(forest::ForestBWG{dim}, cellid::Int) where dim + _mod = dim == 2 ? 4 : 8 nleaves = length.(forest.cells) nleaves_cumsum = cumsum(nleaves) k = findfirst(x->cellid<=x,nleaves_cumsum) - leaveid = cellid % 4 - leaveid = leaveid == 0 ? 4 : leaveid + leaveid = cellid % _mod + leaveid = leaveid == 0 ? _mod : leaveid return forest.cells[k].leaves[leaveid] end From 85bb2d76452d30ff7b58b53ab950b7dd63983f2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 27 Oct 2022 10:01:00 +0200 Subject: [PATCH 040/143] getcells(forest,i) was wrong before, now a version which seems to work fine --- src/Adaptivity/AdaptiveCells.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 0f403ff6ea..84334f5e16 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -184,13 +184,13 @@ function getcells(forest::ForestBWG{dim}) where dim end function getcells(forest::ForestBWG{dim}, cellid::Int) where dim - _mod = dim == 2 ? 4 : 8 + #TODO should nleaves be saved by forest? nleaves = length.(forest.cells) nleaves_cumsum = cumsum(nleaves) k = findfirst(x->cellid<=x,nleaves_cumsum) - leaveid = cellid % _mod - leaveid = leaveid == 0 ? _mod : leaveid - return forest.cells[k].leaves[leaveid] + #TODO is this actually correct? + leafid = k == 1 ? cellid : cellid - (nleaves_cumsum[k] - nleaves[k]) + return forest.cells[k].leaves[leafid] end getcelltype(grid::ForestBWG) = eltype(grid.cells) From 71621916f8517249b8476dcc19cc6138121b1e2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 27 Oct 2022 16:27:49 +0200 Subject: [PATCH 041/143] add md --- p4est-ferrite.md | 182 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 p4est-ferrite.md diff --git a/p4est-ferrite.md b/p4est-ferrite.md new file mode 100644 index 0000000000..9eef46f018 --- /dev/null +++ b/p4est-ferrite.md @@ -0,0 +1,182 @@ +# P4est in Julia with Ferrite + +All of it is based on these papers: +- [Original p4est paper](https://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf) +- [Extension to anisotropic refinement, aka p6est](https://epubs.siam.org/doi/10.1137/140974407) +- [Extension to RefTet elements and in depth explanations](https://bonndoc.ulb.uni-bonn.de/xmlui/handle/20.500.11811/7661); basically monography about t8code + +## Important Concepts + +One of the most important concepts, where everything is based on, are space filling curves (SFC). +In particular, [Z-order (also named Morton order, Morton space-filling curves)](https://en.wikipedia.org/wiki/Z-order_curve) are used in p4est. +The basic idea is that each Octant (in 3D) or quadrant (in 2D) can be encoded by 2 quantities +- the level `l` +- the lower left (front) coordinates `xyz` + +Based on them a unique identifier, the morton index, can be computed. +The good part is, that the mapping from (`l`, `xyz`) -> `mortonidx(l,xyz)` is bijective, meaning we can flip the approach +and can construct each octant/quadrant solely by the `mortonidx`. + +The current implementation of an octant looks currently like this: +```julia +struct OctantBWG{dim, N, M, T} <: AbstractCell{dim,N,M} + #Refinement level + l::T + #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} + xyz::NTuple{dim,T} +end +``` +whenever coordinates are considered we follow the z order logic, meaning x before y before z. + +The octree is implemented as: +```julia +struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} + leaves::Vector{OctantBWG{dim,N,M,T}} + #maximum refinement level + b::T + nodes::NTuple{N,Int} +end +``` +So, only the leaves of the tree are stored and not any intermediate refinement level. +The field `b` is the maximum refinement level and is crucial. This parameter determines the size of the octree coordinate system. +The octree coordinate system is the coordinate system in which the coordinates `xyz` of any `octant::OctantBWG` are described. +This coordinate system goes from [0,2^b]^{dim}. The size of an octant is always 1 at the lowest possible level `b`. + +### Examples + +Let's say the maximum octree level is $b=3$, then the coordinate system is in 2D $[0,2^3]^2 = [0, 8]^2$. +So, our root is on level 0 of size 8 and has the lower left coordinates `(0,0)` + +```julia +# different constructors available, first one OctantBWG(dim,level,mortonid,maximumlevel) +# other possibility by giving directly level and a tuple of coordinates OctantBWG(level,(x,y)) +julia> oct = OctantBWG(2,0,1,3) +OctantBWG{2,4,4} + l = 0 + xy = 0,0 +``` +The size of octants at a specific level can be computed by a simple operation +```julia +#_compute_size(b::Integer, l::Integer) in Ferrite at /home/mkoehler/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:375 +julia> Ferrite._compute_size(3,0) +8 +``` +Now, to fully understand the octree coordinate system we go a level down, i.e. we cut the space in x and y in half. +This means, that the octrees now of size 4. +```julia +julia> Ferrite._compute_size(3,1) +4 +``` +Construct all level 1 octants based on mortonid: +```julia +# note the arguments are dim,level,mortonid,maximumlevel +julia> oct = OctantBWG(2,1,1,3) +OctantBWG{2,4,4} + l = 1 + xy = 0,0 + +julia> o = OctantBWG(2,1,2,3) +OctantBWG{2,4,4} + l = 1 + xy = 4,0 + +julia> o = OctantBWG(2,1,3,3) +OctantBWG{2,4,4} + l = 1 + xy = 0,4 + +julia> o = OctantBWG(2,1,4,3) +OctantBWG{2,4,4} + l = 1 + xy = 4,4 +``` + +So, the morton index is on **one** specific level just a x before y before z "cell" or "element" identifier +``` +x-----------x-----------x +| | | +| | | +| 3 | 4 | +| | | +| | | +x-----------------------x +| | | +| | | +| 1 | 2 | +| | | +| | | +x-----------x-----------x +``` + +The good news: it super cheap to compute octants/quadrants. +An important aspect of the morton index is that it's only consecutive on **one** level. +If you have a tree like this below: + +``` +x-----------x-----------x +| | | +| | | +| 9 | 10 | +| | | +| | | +x-----x--x--x-----------x +| |6 |7 | | +| 3 x--x--x | +| |4 |5 | | +x-----x--x--x 8 | +| | | | +| 1 | 2 | | +x-----x-----x-----------x +``` + +you would maybe think this is the morton index, but strictly speaking it is not. +What we see above is just the `leafindex`, i.e. the index where you find this leaf in the `leaves` array of `OctreeBWG`. +Let's try to construct the lower right based on the morton index on level 1 + +```julia +julia> o = OctantBWG(2,1,8,3) +ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) +Stacktrace: + [1] OctantBWG(dim::Int64, l::Int32, m::Int32, b::Int32) + @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:23 + [2] OctantBWG(dim::Int64, l::Int64, m::Int64, b::Int64) + @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:43 + [3] top-level scope + @ REPL[93]:1 +``` + +The assertion expresses that it is not possible to construct a morton index 8 octant, since the upper bound of the morton index is 4 on level 1. +The morton index of the lower right cell is of course 2 on level 1. + + +## Current state and open questions + +I implemented basic functionality to constructs and operate on octants/octrees. +In particular, I implemented from the p4est paper the Algorithms 1-7,14,15 (and refinement equivalent). + +Currently, I'm at fulfilling the `AbstractGrid` interface which has some tricky parts. +All of the functionality is serial, nothing in terms of distributed is implemented. + +### What we already can do +- refine octants +- coarsen octants +- compute neighbors of octants +- take a `Grid` and make a `ForestBWG` out of it + +### Open questions +- How much do we cache + - We only save very few things and so, sometimes we need to ad hoc compute things. + **Example**: when determining the cell with cellid `i`. We need to go through all trees, determine the length of `leaves` + and take the sum in a cumulative way in order to detect to which tree `i` belongs to. + We could of course cache the number of leaves and get rid of `length(leaves)` calls for each tree +- How to count the nodes? (Actually most important one) In Algorithm 20 of the p4est paper is, an algorithm to count the unique nodes. + However, as far as I understand the algorithm covers only important aspects of hanging nodes and distributed stuff. + So, how to count the unique nodes? We need this for `getcoordinates` and the like. + Maybe by computing face_neighbors and take their coordinate (and of course transform it later) +- What datastructure for the `leaves` ? Currently it's a vector but is there something which exploits that only contiguous pieces are added/removed? + +### Open TODOs +- Coordinate transformation from octree coordinate system to physical coordinate system +- Octant dispatches that are required to fulfill `<:AbstractCell` +- Morton index can be computed much faster by methods I commented above the function +- more efficient `getcells(forest,i)` From a22b5a89c09f7871c12ed67b5073888fc82de7d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 27 Oct 2022 18:15:13 +0200 Subject: [PATCH 042/143] add warning for slow dispatches --- src/Adaptivity/AdaptiveCells.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 84334f5e16..8d920a613e 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -184,8 +184,9 @@ function getcells(forest::ForestBWG{dim}) where dim end function getcells(forest::ForestBWG{dim}, cellid::Int) where dim + @warn "Slow dispatch, consider to call `getcells(forest)` once instead" maxlog=1 #TODO doc page for performance #TODO should nleaves be saved by forest? - nleaves = length.(forest.cells) + nleaves = length.(forest.cells) # cells=trees nleaves_cumsum = cumsum(nleaves) k = findfirst(x->cellid<=x,nleaves_cumsum) #TODO is this actually correct? From ba5cba97ee25ef8440602ea36ad9347747634373 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sun, 30 Oct 2022 16:59:12 +0100 Subject: [PATCH 043/143] make md file nice for quarto compilation --- p4est-ferrite.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/p4est-ferrite.md b/p4est-ferrite.md index 9eef46f018..0af9849b9f 100644 --- a/p4est-ferrite.md +++ b/p4est-ferrite.md @@ -1,6 +1,7 @@ # P4est in Julia with Ferrite All of it is based on these papers: + - [Original p4est paper](https://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf) - [Extension to anisotropic refinement, aka p6est](https://epubs.siam.org/doi/10.1137/140974407) - [Extension to RefTet elements and in depth explanations](https://bonndoc.ulb.uni-bonn.de/xmlui/handle/20.500.11811/7661); basically monography about t8code @@ -10,6 +11,7 @@ All of it is based on these papers: One of the most important concepts, where everything is based on, are space filling curves (SFC). In particular, [Z-order (also named Morton order, Morton space-filling curves)](https://en.wikipedia.org/wiki/Z-order_curve) are used in p4est. The basic idea is that each Octant (in 3D) or quadrant (in 2D) can be encoded by 2 quantities + - the level `l` - the lower left (front) coordinates `xyz` @@ -99,7 +101,7 @@ x-----------x-----------x | 3 | 4 | | | | | | | -x-----------------------x +x-----------x-----------x | | | | | | | 1 | 2 | From d8e33a2ad09bd9aaddff0e13fd76055c839b780c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sun, 30 Oct 2022 18:36:39 +0100 Subject: [PATCH 044/143] start of getnodes --- src/Adaptivity/AdaptiveCells.jl | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 8d920a613e..7b1c5122ec 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -80,7 +80,11 @@ morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2< Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) -# Follow z order, x before y before z for faces, edges and corners +ncorners(::Type{OctantBWG{dim,N}}) where {dim,N} = N +ncorners(o::OctantBWG) = ncorners(typeof(o)) +nchilds(::Type{OctantBWG{dim,N}}) where {dim,N} = N +nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners + # TODO: consider list instead of Vector datastructure struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} @@ -125,6 +129,13 @@ OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) +#maybe not use Base.in, but semantically feels correct +function Base.in(oct::OctantBWG{dim},tree::OctreeBWG{dim}) where dim + maxsize = _maximum_size(tree.b) + outside = any(xyz -> xyz >= maxsize, oct.xyz) || any(xyz -> xyz < 0, oct.xyz) + return !outside +end + """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] @@ -197,6 +208,25 @@ end getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO +function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} + _ncorners = ncorners(C) + nodeidx = 1 + nodes_duplicate = Vector{Node{dim,T}}(undef,getncells(forest)*_ncorners) + nodids = Vector{Int}(undef,getncells(forest)*_ncorners) + for tree in forest.cells + for leaf in tree.leaves + for c in 1:_ncorners + neighbor = corner_neighbor(leaf,c,tree.b) + if neighbor ∈ tree # checks if neighbor is in boundary of tree (w.r.t. octree coordinates) + # check if the leafid is smaller then the neighbor or what? + else + # now how do I recover the interoctree neighborhood type? + end + end + end + end +end + function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) println(io, "ForestBWG with ") println(io, " $(getncells(agrid)) cells") From 1e2bc7f337b351fc7270e02e10c4b76829daf6e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 31 Oct 2022 12:42:29 +0100 Subject: [PATCH 045/143] transform corner and a nonworking version of getnodes --- src/Adaptivity/AdaptiveCells.jl | 140 +++++++++++++++++++++++--------- 1 file changed, 100 insertions(+), 40 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 7b1c5122ec..d4118d10e7 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -21,7 +21,7 @@ Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: Integer @assert l ≤ b #maximum refinement level exceeded @assert m ≤ (one(T)+one(T))^(dim*l) - x,y,z = (zero(T),zero(T),zero(T)) + x,y,z = (zero(T),zero(T),zero(T)) h = Int32(_compute_size(b,l)) _zero = zero(T) _one = one(T) @@ -32,12 +32,12 @@ function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: I z = z | (h*((m-_one) & _two^(dim*i+_two))÷_two^((dim-_one)*i+_two)) end if dim == 2 - OctantBWG{dim,4,4,T}(l,(x,y)) + OctantBWG{dim,4,4,T}(l,(x,y)) elseif dim == 3 - OctantBWG{dim,8,6,T}(l,(x,y,z)) + OctantBWG{dim,8,6,T}(l,(x,y,z)) else error("$dim Dimension not supported") - end + end end OctantBWG(dim::Int,l::Int,m::Int,b::Int=_maxlevel[dim-1]) = OctantBWG(dim,Int32(l),Int32(m),Int32(b)) @@ -80,7 +80,7 @@ morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2< Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) -ncorners(::Type{OctantBWG{dim,N}}) where {dim,N} = N +ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N ncorners(o::OctantBWG) = ncorners(typeof(o)) nchilds(::Type{OctantBWG{dim,N}}) where {dim,N} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners @@ -88,7 +88,7 @@ nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z # TODO: consider list instead of Vector datastructure struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} - #maximum refinement level + #maximum refinement level b::T nodes::NTuple{N,Int} end @@ -129,8 +129,7 @@ OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) -#maybe not use Base.in, but semantically feels correct -function Base.in(oct::OctantBWG{dim},tree::OctreeBWG{dim}) where dim +function inside(tree::OctreeBWG{dim},oct::OctantBWG{dim}) where dim maxsize = _maximum_size(tree.b) outside = any(xyz -> xyz >= maxsize, oct.xyz) || any(xyz -> xyz < 0, oct.xyz) return !outside @@ -146,8 +145,8 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} # Sets cellsets::Dict{String,Set{Int}} nodesets::Dict{String,Set{Int}} - facesets::Dict{String,Set{FaceIndex}} - edgesets::Dict{String,Set{EdgeIndex}} + facesets::Dict{String,Set{FaceIndex}} + edgesets::Dict{String,Set{EdgeIndex}} vertexsets::Dict{String,Set{VertexIndex}} #Topology topology::ExclusiveTopology @@ -174,7 +173,7 @@ getneighborhood(forest::ForestBWG,idx) = getneighborhood(forest.topology,forest, function getncells(grid::ForestBWG) numcells = 0 for tree in grid.cells - numcells += length(tree) + numcells += length(tree) end return numcells end @@ -209,22 +208,44 @@ getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} - _ncorners = ncorners(C) - nodeidx = 1 - nodes_duplicate = Vector{Node{dim,T}}(undef,getncells(forest)*_ncorners) - nodids = Vector{Int}(undef,getncells(forest)*_ncorners) - for tree in forest.cells + nodes = Vector{Node{dim,Int32}}() + sizehint!(nodes,getncells(forest)*2^dim) # TODO worth it? max is everything duplicate + for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves - for c in 1:_ncorners + for c in 1:ncorners(leaf) neighbor = corner_neighbor(leaf,c,tree.b) - if neighbor ∈ tree # checks if neighbor is in boundary of tree (w.r.t. octree coordinates) - # check if the leafid is smaller then the neighbor or what? + if inside(tree,neighbor) # checks if neighbor is in boundary of tree (w.r.t. octree coordinates) + # I think the below is valid + neighbor_morton = morton(neighbor,neighbor.l,tree.b) + leaf_morton = morton(leaf,leaf.l,tree.b) + # If the participating neighbor has higher morton id assign new node + if leaf_morton < neighbor_morton + push!(nodes, Node(transform_corner(forest,k,c,leaf).xyz)) + end else - # now how do I recover the interoctree neighborhood type? + #TODO I don't know how to handle the other case, the below doesn't work + lowest_octree = true + for f in corner_face_participation(dim,c) + k′ = getneighborhood(forest,FaceIndex(k,f)) + if isempty(k′) + continue + else + k′ = k′[1][1] # always half face, ugly TODO + end + if k′ < k + lowest_octree = false + break + end + end + if lowest_octree + #transform needs the neighbor c + push!(nodes,Node(transform_corner(forest,k,c,leaf).xyz)) + end end end end end + return nodes end function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) @@ -237,10 +258,10 @@ end child_id(octant::OctantBWG, b::Integer) Given some OctantBWG `octant` and maximum refinement level `b`, compute the child_id of `octant` note the following quote from Bursedde et al: - children are numbered from 0 for the front lower left child, + children are numbered from 0 for the front lower left child, to 1 for the front lower right child, to 2 for the back lower left, and so on, with 4, . . . , 7 being the four children on top of the children 0, . . . , 3. -shifted by 1 due to julia 1 based indexing +shifted by 1 due to julia 1 based indexing """ function child_id(octant::OctantBWG{3},b::Integer=_maxlevel[2]) i = 0x00 @@ -266,7 +287,7 @@ function parent(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where h = T(_compute_size(b,octant.l)) l = octant.l - one(T) return OctantBWG(l,octant.xyz .& ~h) - else + else error("root has no parent") end end @@ -286,7 +307,7 @@ end function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) - x,y,z = octant.xyz + x,y,z = octant.xyz x += ((f == T(1)) ? -h : ((f == T(2)) ? h : zero(T))) y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) @@ -300,7 +321,7 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2 _two = T2(2) #currently rotation not encoded kprime, fprime = getneighborhood(forest,FaceIndex(k,f))[1] - a₂ = f ÷ 2; b₂ = fprime ÷ 2 + a₂ = f ÷ 2; b₂ = fprime ÷ 2 sprime = _one - ((f & _one) ⊻ (fprime & _one)) s = zeros(T2,2) b = zeros(T2,3) @@ -320,12 +341,31 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2 y = T2((s[2] == 1) ? o.xyz[2] : g - o.xyz[2]) z = T2((_two*(fprime & 1) - 1)*2^b + sprime*g + (1-2*sprime)*o.xyz[2]) if dim == 2 - return OctantBWG(l,(x,z)) + return OctantBWG(l,(x,z)) else return OctantBWG(l,(x,y,z)) end end +""" + transform_corner(forest,k,c',oct) + transform_corner(forest,v::VertexIndex,oct) + +Algorithm 12 in p4est paper to transform corner into different octree coordinate system +Note: in Algorithm 12 is c as a argument, but it's never used, therefore I removed it +""" +function transform_corner(forest::ForestBWG,k::T1,c′::T1,oct::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} + # make a dispatch that returns only the coordinates? + b = forest.cells[k].b + l = oct.l; g = 2^b - 2^(b-l) + _inside = inside(forest.cells[k],oct) + h⁻ = _inside ? 0 : -2^(b-l); h⁺ = _inside ? g : 2^b + xyz = ntuple(i->((c′-1) & 2^(i-1) == 0) ? h⁻ : h⁺,dim) + return OctantBWG(l,xyz) +end + +transform_corner(forest::ForestBWG,v::VertexIndex,oct::OctantBWG) = transform_corner(forest,v[1],v[2],oct) + """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` @@ -340,17 +380,17 @@ function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) wher ox,oy,oz = octant.xyz case = e ÷ T(4) if case == zero(T) - x = ox - y = oy + (_two*(e & _one) - one(T))*h + x = ox + y = oy + (_two*(e & _one) - one(T))*h z = oz + ((e & _two) - _one)*h return OctantBWG(l,(x,y,z)) elseif case == one(T) - x = ox + (_two*(e & _one) - _one)*h - y = oy + x = ox + (_two*(e & _one) - _one)*h + y = oy z = oz + ((e & _two) - _one)*h - return OctantBWG(l,(x,y,z)) + return OctantBWG(l,(x,y,z)) elseif case == _two - x = ox + (_two*(e & _one) - _one)*h + x = ox + (_two*(e & _one) - _one)*h y = oy + ((e & _two) - _one)*h z = oz return OctantBWG(l,(x,y,z)) @@ -371,7 +411,7 @@ function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) wh ox,oy,oz = octant.xyz _one = one(T) _two = T(2) - x = ox + (_two*(c & _one) - _one)*h + x = ox + (_two*(c & _one) - _one)*h y = oy + ((c & _two) - _one)*h z = oz + ((c & T(4))÷_two - _one)*h return OctantBWG(l,(x,y,z)) @@ -384,12 +424,20 @@ function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) wh ox,oy = octant.xyz _one = one(T) _two = T(2) - x = ox + (_two*(c & _one) - _one)*h + x = ox + (_two*(c & _one) - _one)*h y = oy + ((c & _two) - _one)*h return OctantBWG(l,(x,y)) end corner_neighbor(o::OctantBWG{dim,N,M,T1}, c::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = corner_neighbor(o,T1(c),T1(b)) +function corner_face_participation(dim::T,c::T) where T<:Integer + if dim == 2 + return 𝒱₂_perm[findall(x->c ∈ x, eachrow(𝒱₂))] + else + return 𝒱₃_perm[findall(x->c ∈ x, eachrow(𝒱₃))] + end +end + function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{3,N,M}) where {N,M} x,y,z = o.xyz println(io, "OctantBWG{3,$N,$M}") @@ -405,13 +453,13 @@ function Base.show(io::IO, ::MIME"text/plain", o::OctantBWG{2,N,M}) where {N,M} end _compute_size(b::Integer,l::Integer) = 2^(b-l) -_maximum_size(b::Integer) = 2^(b) +_maximum_size(b::Integer) = 2^(b) # return the two adjacent faces $f_i$ adjacent to edge `edge` _face(edge::Int) = 𝒮[edge, :] # return the `i`-th adjacent face fᵢ to edge `edge` _face(edge::Int, i::Int) = 𝒮[edge, i] # return two face corners ξᵢ of the face `face` along edge `edge` -_face_edge_corners(edge::Int, face::Int) = 𝒯[edge,face] +_face_edge_corners(edge::Int, face::Int) = 𝒯[edge,face] # return the two `edge` corners cᵢ _edge_corners(edge::Int) = 𝒰[edge,:] # return the `i`-th edge corner of `edge` @@ -422,7 +470,7 @@ _neighbor_corner(f::Int,f′::Int,r::Int,ξ::Int) = 𝒫[𝒬[ℛ[f,f′],r],ξ] # map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup function _face_corners(dim::Int,face::Int,ξ::Int) if dim == 2 - return 𝒱₂[face,ξ] + return 𝒱₂[face,ξ] elseif dim == 3 return 𝒱₃[face,ξ] else @@ -432,7 +480,7 @@ end function _face_corners(dim::Int,face::Int) if dim == 2 - return 𝒱₂[face,:] + return 𝒱₂[face,:] elseif dim == 3 return 𝒱₃[face,:] else @@ -452,7 +500,7 @@ const 𝒮 = [3 5 1 3 2 3 1 4 - 2 4] + 2 4] # (0,0) non existing connections const 𝒯 = [(0, 0) (0, 0) (1, 2) (0, 0) (1, 2) (0, 0) @@ -484,7 +532,7 @@ const 𝒰 = [1 2 const 𝒱₂ = [1 3 2 4 1 2 - 3 4] + 3 4] const 𝒱₃ = [1 3 5 7 2 4 6 8 @@ -493,6 +541,18 @@ const 𝒱₃ = [1 3 5 7 1 2 3 4 5 6 7 8] +const 𝒱₂_perm = [4 + 2 + 1 + 3] + +const 𝒱₃_perm = [2 + 4 + 3 + 5 + 1 + 6] + const ℛ = [1 2 2 1 1 2 3 1 1 2 2 1 3 1 1 2 2 1 From b4c89e7181ea62d3cfca0c16c22cac325d3c0a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 1 Nov 2022 10:38:29 +0100 Subject: [PATCH 046/143] include algorithm paper in md --- p4est-ferrite.md | 1 + 1 file changed, 1 insertion(+) diff --git a/p4est-ferrite.md b/p4est-ferrite.md index 0af9849b9f..ae62949c91 100644 --- a/p4est-ferrite.md +++ b/p4est-ferrite.md @@ -5,6 +5,7 @@ All of it is based on these papers: - [Original p4est paper](https://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf) - [Extension to anisotropic refinement, aka p6est](https://epubs.siam.org/doi/10.1137/140974407) - [Extension to RefTet elements and in depth explanations](https://bonndoc.ulb.uni-bonn.de/xmlui/handle/20.500.11811/7661); basically monography about t8code +- [Lucas sent me this, lots of algorithms I could need](https://epubs.siam.org/doi/epdf/10.1137/140970963) ## Important Concepts From 6dc687a42f0c31ab01b2df054316124d79c7de5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Nov 2022 07:52:59 +0100 Subject: [PATCH 047/143] dimension agnostic child_id and introduction of ancestor_id --- src/Adaptivity/AdaptiveCells.jl | 36 +++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index d4118d10e7..6a692e7ca1 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -85,7 +85,6 @@ ncorners(o::OctantBWG) = ncorners(typeof(o)) nchilds(::Type{OctantBWG{dim,N}}) where {dim,N} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners -# TODO: consider list instead of Vector datastructure struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level @@ -197,6 +196,7 @@ function getcells(forest::ForestBWG{dim}, cellid::Int) where dim @warn "Slow dispatch, consider to call `getcells(forest)` once instead" maxlog=1 #TODO doc page for performance #TODO should nleaves be saved by forest? nleaves = length.(forest.cells) # cells=trees + #TODO remove that later by for loop or IBWG 2015 iterator approach nleaves_cumsum = cumsum(nleaves) k = findfirst(x->cellid<=x,nleaves_cumsum) #TODO is this actually correct? @@ -207,12 +207,14 @@ end getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO +#TODO: this function should wrap the LNodes Iterator of IBWG2015 function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Vector{Node{dim,Int32}}() - sizehint!(nodes,getncells(forest)*2^dim) # TODO worth it? max is everything duplicate + sizehint!(nodes,getncells(forest)*2^dim) for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves for c in 1:ncorners(leaf) + #below doesn't work since I need to check and loop for supp(c), see IBWG 2015 neighbor = corner_neighbor(leaf,c,tree.b) if inside(tree,neighbor) # checks if neighbor is in boundary of tree (w.r.t. octree coordinates) # I think the below is valid @@ -263,22 +265,30 @@ note the following quote from Bursedde et al: 4, . . . , 7 being the four children on top of the children 0, . . . , 3. shifted by 1 due to julia 1 based indexing """ -function child_id(octant::OctantBWG{3},b::Integer=_maxlevel[2]) +function child_id(octant::OctantBWG{dim,N,M,T},b::Integer=_maxlevel[2]) where {dim,N,M,T<:Integer} i = 0x00 - h = _compute_size(b,octant.l) - x,y,z = octant.xyz - i = i | ((x & h) != 0x00 ? 0x01 : 0x00) - i = i | ((y & h) != 0x00 ? 0x02 : 0x00) - i = i | ((z & h) != 0x00 ? 0x04 : 0x00) + t = T(2) + z = zero(T) + h = T(_compute_size(b,octant.l)) + xyz = octant.xyz + for j in 0:(dim-1) + i = i | ((xyz[j+1] & h) != z ? t^j : z) + end return i+0x01 end -function child_id(octant::OctantBWG{2},b::Integer=_maxlevel[1]) +""" + ancestor_id(octant::OctantBWG, l::Integer, b::Integer) +Algorithm 3.2 of IBWG 2015 that generalizes `child_id` for different queried levels. +""" +function ancestor_id(octant::OctantBWG{dim,N,M,T}, l::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} i = 0x00 - h = _compute_size(b, octant.l) - x,y = octant.xyz - i = i | ((x & h) != 0x00 ? 0x01 : 0x00) - i = i | ((y & h) != 0x00 ? 0x02 : 0x00) + t = T(2) + z = zero(T) + h = T(_compute_size(b,l)) + for j in 0:(dim-1) + i = i | ((octant.xyz[j+1] & h) != z ? t^j : z) + end return i+0x01 end From 3c56a70eec31db1d518bf02d2ea6d0442e307fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Nov 2022 17:59:41 +0100 Subject: [PATCH 048/143] add split_array --- src/Adaptivity/AdaptiveCells.jl | 45 +++++++++++++++++++++++++++ test/test_octant.jl | 54 ++++++++++++++++++--------------- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 6a692e7ca1..7bfb2a555d 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -85,6 +85,22 @@ ncorners(o::OctantBWG) = ncorners(typeof(o)) nchilds(::Type{OctantBWG{dim,N}}) where {dim,N} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners +Base.isequal(o1::OctantBWG, o2::OctantBWG) = (o1.l == o2.l) && (o1.xyz == o2.xyz) +""" + o1::OctantBWG < o2::OctantBWG +Implements Algorithm 2.1 of IBWG 2015. +Checks first if mortonid is smaller and later if level is smaller. +Thus, ancestors precede descendants (preordering). +""" +function Base.isless(o1::OctantBWG, o2::OctantBWG) + if o1.xyz != o2.xyz + #TODO verify b=o1.l/b=o2.l as argument potential bug otherwise + return morton(o1,o1.l,o1.l) < morton(o2,o2.l,o2.l) + else + return o1.l < o2.l + end +end + struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level @@ -134,6 +150,33 @@ function inside(tree::OctreeBWG{dim},oct::OctantBWG{dim}) where dim return !outside end +""" + split_array(octree::OctreeBWG, a::OctantBWG) +Algorithm 3.3 of IBWG2015. So far tested for root and level 1 and level 2 elements. +Need to verify further +TODO update this docs +""" +function split_array(octree::OctreeBWG{dim}, a::OctantBWG{dim,N,M,T}) where {dim,N,M,T} + leaves = octree.leaves + o = one(T) + 𝐤 = T[i==1 ? 1 : length(leaves)+1 for i in 1:2^dim+1] + for i in 2:2^dim + m = 𝐤[i-1] + while m < 𝐤[i] + n = m + (𝐤[i] - m)÷2 + c = ancestor_id(leaves[n], a.l+o, octree.b) + if c < i + m = n+1 + else + for j in i:c + 𝐤[j] = n + end + end + end + end + return [view(leaves,𝐤[i]:𝐤[i+1]-1) for i in 1:2^dim] +end + """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] @@ -280,8 +323,10 @@ end """ ancestor_id(octant::OctantBWG, l::Integer, b::Integer) Algorithm 3.2 of IBWG 2015 that generalizes `child_id` for different queried levels. +Applied to a single octree, i.e. the array of leaves, yields a monotonic sequence """ function ancestor_id(octant::OctantBWG{dim,N,M,T}, l::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} + @assert 0 < l ≤ octant.l i = 0x00 t = T(2) z = zero(T) diff --git a/test/test_octant.jl b/test/test_octant.jl index 4c3f5e2117..cb783165ef 100644 --- a/test/test_octant.jl +++ b/test/test_octant.jl @@ -2,9 +2,9 @@ @test Ferrite._face(1) == [3,5] @test Ferrite._face(5) == [1,5] @test Ferrite._face(12) == [2,4] - @test Ferrite._face(1,1) == 3 && Ferrite._face(1,2) == 5 - @test Ferrite._face(5,1) == 1 && Ferrite._face(5,2) == 5 - @test Ferrite._face(12,1) == 2 && Ferrite._face(12,2) == 4 + @test Ferrite._face(1,1) == 3 && Ferrite._face(1,2) == 5 + @test Ferrite._face(5,1) == 1 && Ferrite._face(5,2) == 5 + @test Ferrite._face(12,1) == 2 && Ferrite._face(12,2) == 4 @test Ferrite._face(3,1) == 3 && Ferrite._face(3,2) == 6 @test Ferrite._face_edge_corners(1,1) == (0,0) @@ -29,11 +29,11 @@ @test Ferrite._edge_corners(4) == [7,8] @test Ferrite._edge_corners(12,2) == 8 - #Test Figure 3a) of Burstedde, Wilcox, Ghattas [2011] + #Test Figure 3a) of Burstedde, Wilcox, Ghattas [2011] test_ξs = (1,2,3,4) @test Ferrite._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs - #Test Figure 3b) - @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) + #Test Figure 3b) + @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) end @testset "OctantBWG Encoding" begin @@ -45,8 +45,8 @@ end @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) o = Ferrite.OctantBWG(3,2,4,3) - @test Ferrite.child_id(o,b) == 4 - @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 + @test Ferrite.child_id(o,b) == 4 + @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) @@ -82,22 +82,22 @@ end @testset "OctantBWG Operations" begin o = Ferrite.OctantBWG(1,(2,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(0,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(4,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(0,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(4,0,0)) @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(2,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(2,2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(2,2,0)) @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(2,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(2,0,2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(2,0,2)) @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(2,0,0)), Ferrite.OctantBWG(2,(3,1,1))) @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(2,0,0)), Ferrite.OctantBWG(3,(5,3,3))) o = Ferrite.OctantBWG(1,(0,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(-2,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(2,0,0)) + @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(-2,0,0)) + @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(2,0,0)) @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(0,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(0,2,0)) + @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(0,2,0)) @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(0,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(0,0,2)) + @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(0,0,2)) o = Ferrite.OctantBWG(0,(0,0,0)) @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(0,0,0)), Ferrite.OctantBWG(2,(3,3,3))) @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(0,0,0)), Ferrite.OctantBWG(3,(7,7,7))) @@ -134,8 +134,8 @@ end grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells - @test cell isa OctreeBWG - @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) + @test cell isa OctreeBWG + @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) end #simple first and second level refinement # first case @@ -148,7 +148,7 @@ end # | | | # | | | # x-----x-----x-----------| - # | | | | + # | | | | # | | | | # | | | | # x--x--x-----x | @@ -217,12 +217,17 @@ end @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end end + #check montonicity of ancestor_id + for tree in adaptive_grid.cells + ids = Ferrite.ancestor_id.(tree.leaves,(1,),(tree.b,)) + @test issorted(ids) + end #now go back from finest to coarsest for l in 7:-1:0 - coarsen_all(adaptive_grid) + coarsen_all(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) - end + end end ######################### # now do the same with 3D @@ -257,14 +262,14 @@ end end #now go back from finest to coarsest for l in 4:-1:0 - coarsen_all(adaptive_grid) + coarsen_all(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) - end + end end end -@testset "ForestBWG AbstractGrid Interfacing" begin +@testset "ForestBWG AbstractGrid Interfacing" begin function refine_all(grid::ForestBWG,l) for tree in adaptive_grid.cells for leaf in tree.leaves @@ -283,5 +288,4 @@ end refine_all(adaptive_grid,l) @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) end - end From 2ca01808628bb2aa8701b2a6172fa6435eda58c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sat, 5 Nov 2022 16:12:17 +0100 Subject: [PATCH 049/143] little bit of utility and started the search implementation, however, dont know how to deal with the callback match --- p4est-ferrite.md | 13 ++---- src/Adaptivity/AdaptiveCells.jl | 75 ++++++++++++++++++++++++++------- 2 files changed, 63 insertions(+), 25 deletions(-) diff --git a/p4est-ferrite.md b/p4est-ferrite.md index ae62949c91..3092d45863 100644 --- a/p4est-ferrite.md +++ b/p4est-ferrite.md @@ -167,18 +167,11 @@ All of the functionality is serial, nothing in terms of distributed is implement - take a `Grid` and make a `ForestBWG` out of it ### Open questions -- How much do we cache - - We only save very few things and so, sometimes we need to ad hoc compute things. - **Example**: when determining the cell with cellid `i`. We need to go through all trees, determine the length of `leaves` - and take the sum in a cumulative way in order to detect to which tree `i` belongs to. - We could of course cache the number of leaves and get rid of `length(leaves)` calls for each tree -- How to count the nodes? (Actually most important one) In Algorithm 20 of the p4est paper is, an algorithm to count the unique nodes. - However, as far as I understand the algorithm covers only important aspects of hanging nodes and distributed stuff. - So, how to count the unique nodes? We need this for `getcoordinates` and the like. - Maybe by computing face_neighbors and take their coordinate (and of course transform it later) -- What datastructure for the `leaves` ? Currently it's a vector but is there something which exploits that only contiguous pieces are added/removed? +- How to count the nodes? 4th paper has an answer to that which is called `LNodes`. +Algorithm 6.2, which relies on Iterate, Algorithm 5.3. This alrogithm in turn depends on Algorithm 5.2. and 5.1 ### Open TODOs +- Implement the Iterator of IBWG 2015 - Coordinate transformation from octree coordinate system to physical coordinate system - Octant dispatches that are required to fulfill `<:AbstractCell` - Morton index can be computed much faster by methods I commented above the function diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 7bfb2a555d..6688645356 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -82,7 +82,7 @@ Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N ncorners(o::OctantBWG) = ncorners(typeof(o)) -nchilds(::Type{OctantBWG{dim,N}}) where {dim,N} = N +nchilds(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners Base.isequal(o1::OctantBWG, o2::OctantBWG) = (o1.l == o2.l) && (o1.xyz == o2.xyz) @@ -101,6 +101,14 @@ function Base.isless(o1::OctantBWG, o2::OctantBWG) end end +function children(octant::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} + o = one(T) + _nchilds = nchilds(octant) + startid = morton(octant,octant.l+o,b) + endid = startid + _nchilds + o + return ntuple(i->OctantBWG(dim,octant.l+o,(startid:endid)[i],b),_nchilds) +end + struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level @@ -110,14 +118,12 @@ end function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} o = one(T) - # really the only way? + # TODO replace this with recursive search function leave_idx = findfirst(x->x==pivot_octant,octree.leaves) - # how to obtain id from morton index ? old_octant = popat!(octree.leaves,leave_idx) - start_child_id = morton(old_octant,old_octant.l+o,octree.b) - end_child_id = start_child_id + N-o - for child_mort_id in start_child_id:end_child_id - insert!(octree.leaves,leave_idx,OctantBWG(dim,old_octant.l+o,child_mort_id,octree.b)) + _children = children(pivot_octant,octree.b) + for child in _children + insert!(octree.leaves,leave_idx,child) leave_idx += 1 end end @@ -152,19 +158,17 @@ end """ split_array(octree::OctreeBWG, a::OctantBWG) -Algorithm 3.3 of IBWG2015. So far tested for root and level 1 and level 2 elements. -Need to verify further -TODO update this docs + split_array(octantarray, a::OctantBWG, b::Integer) +Algorithm 3.3 of IBWG2015. Efficient binary search """ -function split_array(octree::OctreeBWG{dim}, a::OctantBWG{dim,N,M,T}) where {dim,N,M,T} - leaves = octree.leaves +function split_array(octantarray, a::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} o = one(T) - 𝐤 = T[i==1 ? 1 : length(leaves)+1 for i in 1:2^dim+1] + 𝐤 = T[i==1 ? 1 : length(octantarray)+1 for i in 1:2^dim+1] for i in 2:2^dim m = 𝐤[i-1] while m < 𝐤[i] n = m + (𝐤[i] - m)÷2 - c = ancestor_id(leaves[n], a.l+o, octree.b) + c = ancestor_id(octantarray[n], a.l+o, b) if c < i m = n+1 else @@ -174,12 +178,53 @@ function split_array(octree::OctreeBWG{dim}, a::OctantBWG{dim,N,M,T}) where {dim end end end - return [view(leaves,𝐤[i]:𝐤[i+1]-1) for i in 1:2^dim] + #TODO non-allocating way? + return ntuple(i->view(octantarray,𝐤[i]:𝐤[i+1]-1),2^dim) +end + +split_array(tree::OctreeBWG, a::OctantBWG) = split_array(tree.leaves, a, tree.b) + +function search(octantarray, a::OctantBWG{dim,N,M,T1}, idxset::Vector{T2}, b::Integer, Match=match) where {dim,N,M,T1<:Integer,T2} + isempty(octantarray) && return + isleaf = (length(octantarray) == 1 && a ∈ octantarray) ? true : false + idxset_match = eltype(idxset)[] + for q in idxset + if Match(a,isleaf,q,b) + push!(idxset_match,q) + end + end + if isempty(idxset_match) && !isleaf + 𝐇 = split_array(octantarray,a,b) + _children = children(a,b) + for (child,h) in zip(_children,𝐇) + search(h,child,idxset_match,b) + end + end + return idxset_match +end + +search(tree::OctreeBWG, a::OctantBWG, idxset, Match=match) = search(tree.leaves, a, idxset, tree.b, match) + +""" + match(o::OctantBWG, isleaf::Bool, q) +from IBWG2015 +> match returns true if there is a leaf r ∈ 𝒪 that is a descendant of o +> such that match_q(r) = true, and is allowed to return a false positive +> (i.e., true even if match_q(r) = false for all descendants leaves of o) +> if isleaf=true, then the return value of match is irrelevant +I don't understand what of a to check against index q +""" +function match(o::OctantBWG, isleaf::Bool, q, b) + isleaf && (return true) + println(q) + println(o) + return false end """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] +and Isaac, Burstedde, Wilcox, Ghattas [2015] """ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} cells::Vector{C} From 35cc7a1fe1c18976ebf0134a40b4cf9409330207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sun, 6 Nov 2022 22:41:44 +0100 Subject: [PATCH 050/143] add vertex, face and edge computation of octant --- src/Adaptivity/AdaptiveCells.jl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 6688645356..b75491e55c 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -109,6 +109,31 @@ function children(octant::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} return ntuple(i->OctantBWG(dim,octant.l+o,(startid:endid)[i],b),_nchilds) end +function vertex(octant::OctantBWG{dim,N,M,T}, c::Integer, b::Integer) where {dim,N,M,T} + h = T(_compute_size(b,octant.l)) + return ntuple(d->((c-1) & (2^(d-1))) == 0 ? octant.xyz[d] : octant.xyz[d] + h ,dim) +end + +function vertices(octant::OctantBWG{dim},b::Integer) where {dim} + _nvertices = 2^dim + return ntuple(i->vertex(octant,i,b),_nvertices) +end + +function face(octant::OctantBWG{2}, f::Integer, b::Integer) + cornerid = view(𝒱₂,f,:) + return ntuple(i->corner(octant, cornerid[i], b),2) +end + +function face(octant::OctantBWG{3}, f::Integer, b::Integer) + cornerid = view(𝒱₃,f,:) + return ntuple(i->corner(octant, cornerid[i], b),4) +end + +function edge(octant::OctantBWG{3}, e::Integer, b::Integer) + cornerid = view(𝒰,e,:) + return ntuple(i->corner(octant,cornerid[i], b),2) +end + struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level From a62295547c762b02d9c4310d476daef3b382d71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 7 Nov 2022 16:23:12 +0100 Subject: [PATCH 051/143] octant boundary sets --- src/Adaptivity/AdaptiveCells.jl | 54 +++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index b75491e55c..178046900b 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -121,17 +121,65 @@ end function face(octant::OctantBWG{2}, f::Integer, b::Integer) cornerid = view(𝒱₂,f,:) - return ntuple(i->corner(octant, cornerid[i], b),2) + return ntuple(i->vertex(octant, cornerid[i], b),2) end function face(octant::OctantBWG{3}, f::Integer, b::Integer) cornerid = view(𝒱₃,f,:) - return ntuple(i->corner(octant, cornerid[i], b),4) + return ntuple(i->vertex(octant, cornerid[i], b),4) end function edge(octant::OctantBWG{3}, e::Integer, b::Integer) cornerid = view(𝒰,e,:) - return ntuple(i->corner(octant,cornerid[i], b),2) + return ntuple(i->vertex(octant,cornerid[i], b),2) +end + +""" + boundaryset(o::OctantBWG{2}, i::Integer, b::Integer +implements two dimensional boundaryset table from Fig.4.1 IBWG 2015 +TODO: could be done little bit less ugly +""" +function boundaryset(o::OctantBWG{2,N,M,T}, i::Integer, b::Integer) where {N,M,T} + settype = Set{Union{Tuple{T,T},NTuple{2,Tuple{T,T}}}} + if i==1 + return settype((vertex(o,1,b),face(o,1,b),face(o,3,b))) + elseif i==2 + return settype((vertex(o,2,b),face(o,2,b),face(o,3,b))) + elseif i==3 + return settype((vertex(o,3,b),face(o,1,b),face(o,4,b))) + elseif i==4 + return settype((vertex(o,4,b),face(o,2,b),face(o,4,b))) + else + throw("no boundary") + end +end + +""" + boundaryset(o::OctantBWG{3}, i::Integer, b::Integer +implements three dimensional boundaryset table from Fig.4.1 IBWG 2015 +TODO: could be done little bit less ugly +""" +function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T} + settype = Set{Union{Tuple{T,T,T},NTuple{2,Tuple{T,T,T}},NTuple{4,Tuple{T,T,T}}}} + if i==1 + return settype((vertex(o,1,b),edge(o,1,b),edge(o,5,b),edge(o,9,b), face(o,1,b),face(o,3,b),face(o,5,b))) + elseif i==2 + return settype((vertex(o,2,b),edge(o,1,b),edge(o,6,b),edge(o,10,b),face(o,2,b),face(o,3,b),face(o,5,b))) + elseif i==3 + return settype((vertex(o,3,b),edge(o,2,b),edge(o,5,b),edge(o,11,b),face(o,1,b),face(o,4,b),face(o,5,b))) + elseif i==4 + return settype((vertex(o,4,b),edge(o,2,b),edge(o,6,b),edge(o,12,b),face(o,2,b),face(o,4,b),face(o,5,b))) + elseif i==5 + return settype((vertex(o,5,b),edge(o,3,b),edge(o,7,b),edge(o,9,b), face(o,1,b),face(o,3,b),face(o,6,b))) + elseif i==6 + return settype((vertex(o,6,b),edge(o,3,b),edge(o,8,b),edge(o,10,b),face(o,2,b),face(o,3,b),face(o,6,b))) + elseif i==7 + return settype((vertex(o,7,b),edge(o,4,b),edge(o,7,b),edge(o,11,b),face(o,1,b),face(o,4,b),face(o,6,b))) + elseif i==8 + return settype((vertex(o,8,b),edge(o,4,b),edge(o,8,b),edge(o,12,b),face(o,2,b),face(o,4,b),face(o,6,b))) + else + throw("no boundary") + end end struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} From cbeaf8c5e258d4f8992b20a82fa02d50c2d8e7c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 7 Nov 2022 20:32:33 +0100 Subject: [PATCH 052/143] first non-working find_range_boundaries --- src/Adaptivity/AdaptiveCells.jl | 41 +++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 178046900b..6dab65e38c 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -140,7 +140,7 @@ implements two dimensional boundaryset table from Fig.4.1 IBWG 2015 TODO: could be done little bit less ugly """ function boundaryset(o::OctantBWG{2,N,M,T}, i::Integer, b::Integer) where {N,M,T} - settype = Set{Union{Tuple{T,T},NTuple{2,Tuple{T,T}}}} + settype = boundarysettype(o) if i==1 return settype((vertex(o,1,b),face(o,1,b),face(o,3,b))) elseif i==2 @@ -160,7 +160,7 @@ implements three dimensional boundaryset table from Fig.4.1 IBWG 2015 TODO: could be done little bit less ugly """ function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T} - settype = Set{Union{Tuple{T,T,T},NTuple{2,Tuple{T,T,T}},NTuple{4,Tuple{T,T,T}}}} + settype = boundarysettype(o) if i==1 return settype((vertex(o,1,b),edge(o,1,b),edge(o,5,b),edge(o,9,b), face(o,1,b),face(o,3,b),face(o,5,b))) elseif i==2 @@ -182,6 +182,43 @@ function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T end end +""" + find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) +Algorithm 4.2 of IBWG 2015 +TODO against what to test? +""" +function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) where {dim,N,M,T} + o = one(T) + if isempty(idxset) || s.l == b + return idxset + end + j = ancestor_id(f,s.l+o,b); k = ancestor_id(l,s.l+o,b) + boundary_j = boundaryset(s,j,b) + kidz = children(s,b) + if j==k + return find_range_boundaries(f,l,kidz[j],idxset ∩ boundary_j,b) + end + idxset_match = boundarysettype(s)() + for i in j:k + idxset_match = idxset_match ∪ (idxset ∩ boundaryset(s,i,b)) + end + boundary_k = boundaryset(s,k,b) + idxset_match_j = setdiff((idxset ∩ boundary_j),idxset_match) + fj, lj = descendants(kidz[j],b) + if fj != f + idxset_match_j = find_range_boundaries(f,lj,kidz[j],idxset_match_j,b) + end + idxset_match_k = setdiff(setdiff((idxset ∩ boundary_j),idxset_match),idxset_match_j) + fk, lk = descendants(kidz[k]) + if lk != l + idxset_match_k = find_range_boundaries(fk,l,kidz[k],idxset_match_k,b) + end + return idxset_match ∪ idxset_match_j ∪ idxset_match_k +end + +boundarysettype(::OctantBWG{3,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T,T},NTuple{2,Tuple{T,T,T}},NTuple{4,Tuple{T,T,T}}}} +boundarysettype(::OctantBWG{2,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T},NTuple{2,Tuple{T,T}}}} + struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level From dc7866decb0863a471e4dd81e83f973f9b68af65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Nov 2022 16:36:43 +0100 Subject: [PATCH 053/143] convenience find_range_boundaries dispatch --- src/Adaptivity/AdaptiveCells.jl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 6dab65e38c..94b7facd80 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -184,8 +184,10 @@ end """ find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) + find_range_boundaries(s::OctantBWG{dim,N,M,T}, idxset, b) Algorithm 4.2 of IBWG 2015 -TODO against what to test? +TODO against what to test? Seems to work in the sense that if idxset has elements in the set +that are not part of the boundary, they are not returned """ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) where {dim,N,M,T} o = one(T) @@ -216,6 +218,12 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, return idxset_match ∪ idxset_match_j ∪ idxset_match_k end +#for convenience +function find_range_boundaries(s::OctantBWG, idxset, b) + f,l = descendants(s,b) + return find_range_boundaries(f,l,s,idxset,b) +end + boundarysettype(::OctantBWG{3,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T,T},NTuple{2,Tuple{T,T,T}},NTuple{4,Tuple{T,T,T}}}} boundarysettype(::OctantBWG{2,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T},NTuple{2,Tuple{T,T}}}} From fc7435859c3e6e813e6a0c7d9f8c3d2d6e0eb0b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 Feb 2023 15:56:15 +0100 Subject: [PATCH 054/143] small bug --- src/Adaptivity/AdaptiveCells.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 94b7facd80..26557422d0 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -211,7 +211,7 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, idxset_match_j = find_range_boundaries(f,lj,kidz[j],idxset_match_j,b) end idxset_match_k = setdiff(setdiff((idxset ∩ boundary_j),idxset_match),idxset_match_j) - fk, lk = descendants(kidz[k]) + fk, lk = descendants(kidz[k],b) if lk != l idxset_match_k = find_range_boundaries(fk,l,kidz[k],idxset_match_k,b) end From 5ef12182c644f3b170359d7b63e8fcc4bd8188d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 23 Feb 2023 14:59:37 +0100 Subject: [PATCH 055/143] introduce OctantIndices --- src/Adaptivity/AdaptiveCells.jl | 91 ++++++++++++++++----------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 26557422d0..969a8aa415 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -68,7 +68,7 @@ function morton(octant::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Intege loop_length = (sizeof(typeof(id))*T(8)) ÷ dim - o for i in z:loop_length for d in z:dim-o - # first shift extract i-th bit and second shift inserts it at interleaved index + # first shift extract i-th bit and second shift inserts it at interleaved index id = id | ((octant.xyz[d+o] & (o << i)) << ((dim-o)*i+d)) end end @@ -109,6 +109,30 @@ function children(octant::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} return ntuple(i->OctantBWG(dim,octant.l+o,(startid:endid)[i],b),_nchilds) end +abstract type OctantIndex{T<:Integer} end +Base.isequal(i1::T,i2::T) where T<:OctantIndex = i1.idx == i2.idx #same type +Base.isequal(i1::T1,i2::T2) where {T1<:OctantIndex,T2<:OctantIndex} = false #different type +Base.hash(idx::OctantIndex) = Base.hash(idx.idx) + +struct OctantCornerIndex{T} <: OctantIndex{T} + idx::T +end +Base.show(io::IO, ::MIME"text/plain", c::OctantCornerIndex) = print(io, "O-Corner $(c.idx)") +Base.show(io::IO, c::OctantCornerIndex) = print(io, "O-Corner $(c.idx)") + +struct OctantEdgeIndex{T} <: OctantIndex{T} + idx::T +end +Base.show(io::IO, ::MIME"text/plain", e::OctantEdgeIndex) = print(io, "O-Edge $(e.idx)") +Base.show(io::IO, e::OctantEdgeIndex) = print(io, "O-Edge $(e.idx)") + +struct OctantFaceIndex{T} <: OctantIndex{T} + idx::T +end +Base.show(io::IO, ::MIME"text/plain", f::OctantFaceIndex) = print(io, "O-Face $(f.idx)") +Base.show(io::IO, f::OctantFaceIndex) = print(io, "O-Face $(f.idx)") + +vertex(octant::OctantBWG, c::OctantCornerIndex, b::Integer) = vertex(octant,c.idx,b) function vertex(octant::OctantBWG{dim,N,M,T}, c::Integer, b::Integer) where {dim,N,M,T} h = T(_compute_size(b,octant.l)) return ntuple(d->((c-1) & (2^(d-1))) == 0 ? octant.xyz[d] : octant.xyz[d] + h ,dim) @@ -119,6 +143,7 @@ function vertices(octant::OctantBWG{dim},b::Integer) where {dim} return ntuple(i->vertex(octant,i,b),_nvertices) end +vertex(octant::OctantBWG, f::OctantFaceIndex, b::Integer) = vertex(octant,f.idx,b) function face(octant::OctantBWG{2}, f::Integer, b::Integer) cornerid = view(𝒱₂,f,:) return ntuple(i->vertex(octant, cornerid[i], b),2) @@ -129,6 +154,7 @@ function face(octant::OctantBWG{3}, f::Integer, b::Integer) return ntuple(i->vertex(octant, cornerid[i], b),4) end +vertex(octant::OctantBWG, e::OctantEdgeIndex, b::Integer) = vertex(octant,e.idx,b) function edge(octant::OctantBWG{3}, e::Integer, b::Integer) cornerid = view(𝒰,e,:) return ntuple(i->vertex(octant,cornerid[i], b),2) @@ -142,13 +168,13 @@ TODO: could be done little bit less ugly function boundaryset(o::OctantBWG{2,N,M,T}, i::Integer, b::Integer) where {N,M,T} settype = boundarysettype(o) if i==1 - return settype((vertex(o,1,b),face(o,1,b),face(o,3,b))) + return Set((OctantCornerIndex(1),OctantFaceIndex(1),OctantFaceIndex(3))) elseif i==2 - return settype((vertex(o,2,b),face(o,2,b),face(o,3,b))) + return Set((OctantCornerIndex(2),OctantFaceIndex(2),OctantFaceIndex(3))) elseif i==3 - return settype((vertex(o,3,b),face(o,1,b),face(o,4,b))) + return Set((OctantCornerIndex(3),OctantFaceIndex(1),OctantFaceIndex(4))) elseif i==4 - return settype((vertex(o,4,b),face(o,2,b),face(o,4,b))) + return Set((OctantCornerIndex(4),OctantFaceIndex(2),OctantFaceIndex(4))) else throw("no boundary") end @@ -162,21 +188,21 @@ TODO: could be done little bit less ugly function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T} settype = boundarysettype(o) if i==1 - return settype((vertex(o,1,b),edge(o,1,b),edge(o,5,b),edge(o,9,b), face(o,1,b),face(o,3,b),face(o,5,b))) + return Set((OctantCornerIndex(1),OctantEdgeIndex(1),OctantEdgeIndex(5),OctantEdgeIndex(9), OctantFaceIndex(1),OctantFaceIndex(3),OctantFaceIndex(5))) elseif i==2 - return settype((vertex(o,2,b),edge(o,1,b),edge(o,6,b),edge(o,10,b),face(o,2,b),face(o,3,b),face(o,5,b))) + return Set((OctantCornerIndex(2),OctantEdgeIndex(1),OctantEdgeIndex(6),OctantEdgeIndex(10),OctantFaceIndex(2),OctantFaceIndex(3),OctantFaceIndex(5))) elseif i==3 - return settype((vertex(o,3,b),edge(o,2,b),edge(o,5,b),edge(o,11,b),face(o,1,b),face(o,4,b),face(o,5,b))) + return Set((OctantCornerIndex(3),OctantEdgeIndex(2),OctantEdgeIndex(5),OctantEdgeIndex(11),OctantFaceIndex(1),OctantFaceIndex(4),OctantFaceIndex(5))) elseif i==4 - return settype((vertex(o,4,b),edge(o,2,b),edge(o,6,b),edge(o,12,b),face(o,2,b),face(o,4,b),face(o,5,b))) + return Set((OctantCornerIndex(4),OctantEdgeIndex(2),OctantEdgeIndex(6),OctantEdgeIndex(12),OctantFaceIndex(2),OctantFaceIndex(4),OctantFaceIndex(5))) elseif i==5 - return settype((vertex(o,5,b),edge(o,3,b),edge(o,7,b),edge(o,9,b), face(o,1,b),face(o,3,b),face(o,6,b))) + return Set((OctantCornerIndex(5),OctantEdgeIndex(3),OctantEdgeIndex(7),OctantEdgeIndex(9), OctantFaceIndex(1),OctantFaceIndex(3),OctantFaceIndex(6))) elseif i==6 - return settype((vertex(o,6,b),edge(o,3,b),edge(o,8,b),edge(o,10,b),face(o,2,b),face(o,3,b),face(o,6,b))) + return Set((OctantCornerIndex(6),OctantEdgeIndex(3),OctantEdgeIndex(8),OctantEdgeIndex(10),OctantFaceIndex(2),OctantFaceIndex(3),OctantFaceIndex(6))) elseif i==7 - return settype((vertex(o,7,b),edge(o,4,b),edge(o,7,b),edge(o,11,b),face(o,1,b),face(o,4,b),face(o,6,b))) + return Set((OctantCornerIndex(7),OctantEdgeIndex(4),OctantEdgeIndex(7),OctantEdgeIndex(11),OctantFaceIndex(1),OctantFaceIndex(4),OctantFaceIndex(6))) elseif i==8 - return settype((vertex(o,8,b),edge(o,4,b),edge(o,8,b),edge(o,12,b),face(o,2,b),face(o,4,b),face(o,6,b))) + return Set((OctantCornerIndex(8),OctantEdgeIndex(4),OctantEdgeIndex(8),OctantEdgeIndex(12),OctantFaceIndex(2),OctantFaceIndex(4),OctantFaceIndex(6))) else throw("no boundary") end @@ -186,7 +212,7 @@ end find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) find_range_boundaries(s::OctantBWG{dim,N,M,T}, idxset, b) Algorithm 4.2 of IBWG 2015 -TODO against what to test? Seems to work in the sense that if idxset has elements in the set +TODO against what to test? Seems to work in the sense that if idxset has elements in the set that are not part of the boundary, they are not returned """ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) where {dim,N,M,T} @@ -415,40 +441,13 @@ getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same #TODO: this function should wrap the LNodes Iterator of IBWG2015 function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} - nodes = Vector{Node{dim,Int32}}() - sizehint!(nodes,getncells(forest)*2^dim) + nodes = Dict{Tuple{Int,NTuple{dim,Int32}},NTuple{dim,Int32}}() for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves - for c in 1:ncorners(leaf) - #below doesn't work since I need to check and loop for supp(c), see IBWG 2015 - neighbor = corner_neighbor(leaf,c,tree.b) - if inside(tree,neighbor) # checks if neighbor is in boundary of tree (w.r.t. octree coordinates) - # I think the below is valid - neighbor_morton = morton(neighbor,neighbor.l,tree.b) - leaf_morton = morton(leaf,leaf.l,tree.b) - # If the participating neighbor has higher morton id assign new node - if leaf_morton < neighbor_morton - push!(nodes, Node(transform_corner(forest,k,c,leaf).xyz)) - end - else - #TODO I don't know how to handle the other case, the below doesn't work - lowest_octree = true - for f in corner_face_participation(dim,c) - k′ = getneighborhood(forest,FaceIndex(k,f)) - if isempty(k′) - continue - else - k′ = k′[1][1] # always half face, ugly TODO - end - if k′ < k - lowest_octree = false - break - end - end - if lowest_octree - #transform needs the neighbor c - push!(nodes,Node(transform_corner(forest,k,c,leaf).xyz)) - end + _vertices = vertices(leaf,tree.b) + for v in _vertices + if !haskey(nodes,(k,v)) + nodes[(k,v)] = v end end end From 435e77e7f50a419de854860538e5225c652d4a35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 23 Feb 2023 16:40:00 +0100 Subject: [PATCH 056/143] successfully created a over complicated identity mapping --- src/Adaptivity/AdaptiveCells.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 969a8aa415..f308f50557 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -112,23 +112,25 @@ end abstract type OctantIndex{T<:Integer} end Base.isequal(i1::T,i2::T) where T<:OctantIndex = i1.idx == i2.idx #same type Base.isequal(i1::T1,i2::T2) where {T1<:OctantIndex,T2<:OctantIndex} = false #different type -Base.hash(idx::OctantIndex) = Base.hash(idx.idx) struct OctantCornerIndex{T} <: OctantIndex{T} idx::T end +Base.hash(idx::OctantCornerIndex) = Base.hash((0,idx.idx)) Base.show(io::IO, ::MIME"text/plain", c::OctantCornerIndex) = print(io, "O-Corner $(c.idx)") Base.show(io::IO, c::OctantCornerIndex) = print(io, "O-Corner $(c.idx)") struct OctantEdgeIndex{T} <: OctantIndex{T} idx::T end +Base.hash(idx::OctantEdgeIndex) = Base.hash((1,idx.idx)) Base.show(io::IO, ::MIME"text/plain", e::OctantEdgeIndex) = print(io, "O-Edge $(e.idx)") Base.show(io::IO, e::OctantEdgeIndex) = print(io, "O-Edge $(e.idx)") struct OctantFaceIndex{T} <: OctantIndex{T} idx::T end +Base.hash(idx::OctantFaceIndex) = Base.hash((2,idx.idx)) Base.show(io::IO, ::MIME"text/plain", f::OctantFaceIndex) = print(io, "O-Face $(f.idx)") Base.show(io::IO, f::OctantFaceIndex) = print(io, "O-Face $(f.idx)") @@ -226,7 +228,7 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, if j==k return find_range_boundaries(f,l,kidz[j],idxset ∩ boundary_j,b) end - idxset_match = boundarysettype(s)() + idxset_match = Set{OctantIndex{T}}() for i in j:k idxset_match = idxset_match ∪ (idxset ∩ boundaryset(s,i,b)) end From 9c993d41b8b17c63d276273a45d64b0834a3608f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 14 Apr 2023 14:01:49 +0200 Subject: [PATCH 057/143] get rid of boundarysettype and use union --- src/Adaptivity/AdaptiveCells.jl | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index f308f50557..e53a15b398 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -168,7 +168,6 @@ implements two dimensional boundaryset table from Fig.4.1 IBWG 2015 TODO: could be done little bit less ugly """ function boundaryset(o::OctantBWG{2,N,M,T}, i::Integer, b::Integer) where {N,M,T} - settype = boundarysettype(o) if i==1 return Set((OctantCornerIndex(1),OctantFaceIndex(1),OctantFaceIndex(3))) elseif i==2 @@ -188,7 +187,6 @@ implements three dimensional boundaryset table from Fig.4.1 IBWG 2015 TODO: could be done little bit less ugly """ function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T} - settype = boundarysettype(o) if i==1 return Set((OctantCornerIndex(1),OctantEdgeIndex(1),OctantEdgeIndex(5),OctantEdgeIndex(9), OctantFaceIndex(1),OctantFaceIndex(3),OctantFaceIndex(5))) elseif i==2 @@ -217,8 +215,8 @@ Algorithm 4.2 of IBWG 2015 TODO against what to test? Seems to work in the sense that if idxset has elements in the set that are not part of the boundary, they are not returned """ -function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) where {dim,N,M,T} - o = one(T) +function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1}, s::OctantBWG{dim,N,M,T1}, idxset::Set{OctantIndex{T2}}, b) where {dim,N,M,T1,T2} + o = one(T1) if isempty(idxset) || s.l == b return idxset end @@ -228,9 +226,9 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, if j==k return find_range_boundaries(f,l,kidz[j],idxset ∩ boundary_j,b) end - idxset_match = Set{OctantIndex{T}}() + idxset_match = Set{OctantIndex{T2}}() for i in j:k - idxset_match = idxset_match ∪ (idxset ∩ boundaryset(s,i,b)) + union!(idxset_match,idxset ∩ boundaryset(s,i,b)) end boundary_k = boundaryset(s,k,b) idxset_match_j = setdiff((idxset ∩ boundary_j),idxset_match) @@ -246,15 +244,12 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, return idxset_match ∪ idxset_match_j ∪ idxset_match_k end -#for convenience +#for convenience, should probably changed to parent(s) until parent(s)==root and then descendants(root) function find_range_boundaries(s::OctantBWG, idxset, b) f,l = descendants(s,b) return find_range_boundaries(f,l,s,idxset,b) end -boundarysettype(::OctantBWG{3,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T,T},NTuple{2,Tuple{T,T,T}},NTuple{4,Tuple{T,T,T}}}} -boundarysettype(::OctantBWG{2,N,M,T}) where {N,M,T} = Set{Union{Tuple{T,T},NTuple{2,Tuple{T,T}}}} - struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level From 621731ec8111012b5ef19a5a043277ba43a080cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 17 Apr 2023 12:58:18 +0200 Subject: [PATCH 058/143] typo in recursive algo paper in find_range_boundaries algorithm 4.2 line 9 B_cap^j should be B_cap^k, figure 4.2 has the correct version; works now for figure example --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index e53a15b398..b40858735a 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -227,7 +227,7 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1 return find_range_boundaries(f,l,kidz[j],idxset ∩ boundary_j,b) end idxset_match = Set{OctantIndex{T2}}() - for i in j:k + for i in (j+1):(k-1) union!(idxset_match,idxset ∩ boundaryset(s,i,b)) end boundary_k = boundaryset(s,k,b) @@ -236,7 +236,7 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1 if fj != f idxset_match_j = find_range_boundaries(f,lj,kidz[j],idxset_match_j,b) end - idxset_match_k = setdiff(setdiff((idxset ∩ boundary_j),idxset_match),idxset_match_j) + idxset_match_k = setdiff(setdiff((idxset ∩ boundary_k),idxset_match),idxset_match_j) fk, lk = descendants(kidz[k],b) if lk != l idxset_match_k = find_range_boundaries(fk,l,kidz[k],idxset_match_k,b) From 439d910d64c0d63e09c995829f1328acaca724c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 17 Apr 2023 13:13:46 +0200 Subject: [PATCH 059/143] add placeholder for isrelevant algorithm 5.1 --- src/Adaptivity/AdaptiveCells.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index b40858735a..c625632f18 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -250,6 +250,18 @@ function find_range_boundaries(s::OctantBWG, idxset, b) return find_range_boundaries(f,l,s,idxset,b) end +function isrelevant(xyz::NTuple{dim,T},leafsuppₚ::Set{<:OctantBWG}) where {dim,T} + ###### only relevant for distributed + #for all s in leafsuppₚ + # if s in 𝒪ₚ + # return true + # else + # check stuff Algorithm 5.1 line 4-5 + # end + #end + return true +end + struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} leaves::Vector{OctantBWG{dim,N,M,T}} #maximum refinement level From e013d639e2a3530e51c5a8becacfbbfcad83ec8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Sun, 23 Apr 2023 13:10:19 +0200 Subject: [PATCH 060/143] o instead of 1 --- src/Adaptivity/AdaptiveCells.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index c625632f18..0d32b707b2 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -227,7 +227,7 @@ function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1 return find_range_boundaries(f,l,kidz[j],idxset ∩ boundary_j,b) end idxset_match = Set{OctantIndex{T2}}() - for i in (j+1):(k-1) + for i in (j+o):(k-o) union!(idxset_match,idxset ∩ boundaryset(s,i,b)) end boundary_k = boundaryset(s,k,b) From f194cb0e4e2085b5c150408bf8abcd6d2f581619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 3 Jul 2023 14:13:05 +0200 Subject: [PATCH 061/143] visualization script not included in Ferrite; transformation of points from octree coordinate system to physical coordinate system --- src/Adaptivity/AdaptiveCells.jl | 33 +++++++++++++++++++++++++-------- src/Adaptivity/visualization.jl | 27 +++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 8 deletions(-) create mode 100644 src/Adaptivity/visualization.jl diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index ce94540f9f..bf7c02e8f8 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -156,6 +156,11 @@ function face(octant::OctantBWG{3}, f::Integer, b::Integer) return ntuple(i->vertex(octant, cornerid[i], b),4) end +function faces(octant::OctantBWG{dim}, b::Integer) where dim + _nfaces = 2*dim + return ntuple(i->face(octant,i,b),_nfaces) +end + vertex(octant::OctantBWG, e::OctantEdgeIndex, b::Integer) = vertex(octant,e.idx,b) function edge(octant::OctantBWG{3}, e::Integer, b::Integer) cornerid = view(𝒰,e,:) @@ -212,8 +217,7 @@ end find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) find_range_boundaries(s::OctantBWG{dim,N,M,T}, idxset, b) Algorithm 4.2 of IBWG 2015 -TODO against what to test? Seems to work in the sense that if idxset has elements in the set -that are not part of the boundary, they are not returned +TODO: write tests """ function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1}, s::OctantBWG{dim,N,M,T1}, idxset::Set{OctantIndex{T2}}, b) where {dim,N,M,T1,T2} o = one(T1) @@ -270,6 +274,7 @@ struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{RefHypercube{dim}} end function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} + @assert pivot_octant.l + 1 <= octree.b o = one(T) # TODO replace this with recursive search function leave_idx = findfirst(x->x==pivot_octant,octree.leaves) @@ -447,17 +452,21 @@ end getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO +function transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{dim,T}) where {dim,T} + tree = forest.cells[k] + cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate + octant_physical_coordinates = sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(vertex),j),1:length(cellnodes)) + return Vec{dim}(octant_physical_coordinates .* 2/(2^tree.b) .- 1) +end #TODO: this function should wrap the LNodes Iterator of IBWG2015 function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} - nodes = Dict{Tuple{Int,NTuple{dim,Int32}},NTuple{dim,Int32}}() + nodes = Set{Tuple{Int,NTuple{dim,Int32}}}() for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves _vertices = vertices(leaf,tree.b) for v in _vertices - if !haskey(nodes,(k,v)) - nodes[(k,v)] = v - end + push!(nodes,(k,v)) end end end @@ -530,14 +539,22 @@ function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end -function face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} +function face_neighbor(octant::OctantBWG{3,N,M,T}, f::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y,z = octant.xyz x += ((f == T(1)) ? -h : ((f == T(2)) ? h : zero(T))) y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) - dim == 2 ? OctantBWG(l,(x,y)) : OctantBWG(l,(x,y,z)) + return OctantBWG(l,(x,y,z)) +end +function face_neighbor(octant::OctantBWG{2,N,M,T}, f::T, b::T=_maxlevel[1]) where {N,M,T<:Integer} + l = octant.l + h = T(_compute_size(b,octant.l)) + x,y = octant.xyz + x += ((f == T(1)) ? -h : ((f == T(2)) ? h : zero(T))) + y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) + return OctantBWG(l,(x,y)) end face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) diff --git a/src/Adaptivity/visualization.jl b/src/Adaptivity/visualization.jl new file mode 100644 index 0000000000..0bf2420a18 --- /dev/null +++ b/src/Adaptivity/visualization.jl @@ -0,0 +1,27 @@ +function visualize_grid(forest::ForestBWG{dim}) where dim + fig = GLMakie.Figure() + ax = GLMakie.Axis(fig[1,1]) + for tree in forest.cells + for leaf in tree.leaves + cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate |> collect + #request vertices and faces in octree coordinate system + _vertices = Ferrite.vertices(leaf,tree.b) + # transform from octree coordinate system to -1,1 by first shifting to 0,2 and later shift by -1 + _vertices = broadcast.(x->x .* 2/(2^tree.b) .- 1, _vertices) + octant_physical_coordinates = zeros(length(_vertices),dim) + for (i,v) in enumerate(_vertices) + octant_physical_coordinates[i,:] .= sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(v),j),1:length(cellnodes)) + end + GLMakie.scatter!(ax,octant_physical_coordinates,color=:black,markersize=25) + center = sum(octant_physical_coordinates,dims=1) ./ 4 + #GLMakie.scatter!(ax,center,color=:black,markersize=25) + facetable = dim == 2 ? Ferrite.𝒱₂ : Ferrite.𝒱₃ + for faceids in eachrow(facetable) + x = octant_physical_coordinates[faceids,1] + (octant_physical_coordinates[faceids,1] .- center[1])*0.02 + y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 + GLMakie.lines!(ax,x,y,color=:black) + end + end + end + return fig, ax +end From 8932233dc86ed8621acdba29f15ae95461d2cc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 3 Jul 2023 15:52:56 +0200 Subject: [PATCH 062/143] 1 based index fun in transform_face --- src/Adaptivity/AdaptiveCells.jl | 70 +++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index bf7c02e8f8..be8d255000 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -79,6 +79,7 @@ morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2< Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) +root(dim::T) where T<:Integer = zero(OctantBWG{dim,dim^2,2*dim}) ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N ncorners(o::OctantBWG) = ncorners(typeof(o)) @@ -470,6 +471,32 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end end end + for (k,tree) in enumerate(forest.cells) + _vertices = vertices(root(dim),tree.b) + for (vi,v) in enumerate(_vertices) + vertex_neighbor = forest.topology.vertex_neighbor[k,vi] + if k > vertex_neighbor[1] + delete!(nodes,(k,v)) + end + end + _faces = faces(root(dim),tree.b) + for (fi,f) in enumerate(_faces) + face_neighbor = forest.topology.face_neighbor[k,fi] + if k > face_neighbor[1] + for leaf in tree.leaves + for v in vertices(leaf,tree.b) + if fi < 3 + if v[1] == f[1][1] == f[2][1] + + delete!(nodes,(k,v)) + end + end + delete!(nodes,(k,v)) + end + end + end + end + end return nodes end @@ -558,35 +585,39 @@ function face_neighbor(octant::OctantBWG{2,N,M,T}, f::T, b::T=_maxlevel[1]) wher end face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) -#TODO: this is not working in 2d as of now, indices used in the paper confuse me +#TODO: this is working for 2D except rotation, 3D I don't know function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) #currently rotation not encoded - kprime, fprime = getneighborhood(forest,FaceIndex(k,f))[1] - a₂ = f ÷ 2; b₂ = fprime ÷ 2 - sprime = _one - ((f & _one) ⊻ (fprime & _one)) - s = zeros(T2,2) + perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) + kprime, fprime = getneighborhood(forest,FaceIndex(k,perm[f]))[1] + fprime = 𝒱₂_perm_inv[fprime] + sprime = _one - (((f - _one) & _one) ⊻ ((fprime - _one) & _one)) + s = zeros(T2,3) b = zeros(T2,3) - r = 0 #no rotation information in face_neighbor currently + a = zeros(T2,3) + r = zero(T2) #no rotation information in face_neighbor currently + a[3] = (f-_one) ÷ 2; b[3] = (fprime-_one) ÷ 2 if dim == 2 - a₀ = 1 - a₂; b₀ = 1 - b₂; s[1] = r #no rotation as of now + a[1] = 1 - a[3]; b[1] = 1 - b[3]; s[1] = r #no rotation as of now else - a₀ = (f < 3) ? 1 : 0; a₁ = (f < 5) ? 2 : 1 + a[1] = (f < 3) ? 1 : 0; a[2] = (f < 5) ? 2 : 1 #u = ℛ[1,f] ⊻ ℛ[1,fprime] ⊻ T2((r == 1) | (r == 3)) - b[1] = (fprime < 3) ? 1 : 0; b[2] = (fprime < 5) ? 2 : 1 + b[1] = (fprime < 3) ? 1 : 0; b[2] = (fprime < 5) ? 2 : 1 # r = 0 -> index 1 #v = T2(ℛ[f,fprime] == 1) - s[1] = r & 2; s[2] = r & 3 + s[1] = r & 1; s[2] = r & 2 end - b = forest.cells[1].b - l = o.l; g = 2^b - 2^(b-l) - x = T2((s[1] == 1) ? o.xyz[1] : g - o.xyz[1]) - y = T2((s[2] == 1) ? o.xyz[2] : g - o.xyz[2]) - z = T2((_two*(fprime & 1) - 1)*2^b + sprime*g + (1-2*sprime)*o.xyz[2]) + maxlevel = forest.cells[1].b + l = o.l; g = 2^maxlevel - 2^(maxlevel-l) + xyz = zeros(T2,dim) + xyz[b[1] + 1] = T2((s[1] == 0) ? o.xyz[a[1] + 1] : g - o.xyz[a[1] + 1]) + xyz[b[3] + 1] = T2((_two*(fprime & 1) - 1)*2^maxlevel + sprime*g + (1-2*sprime)*o.xyz[a[3] + 1]) if dim == 2 - return OctantBWG(l,(x,z)) + return OctantBWG(l,(xyz[1],xyz[2])) else - return OctantBWG(l,(x,y,z)) + xyz[b[2] + 1] = T2((s[2] == 0) ? o.xyz[a[1] + 1] : g - o.xyz[a[2] + 1]) + return OctantBWG(l,(xyz[1],xyz[2],xyz[3])) end end @@ -789,6 +820,11 @@ const 𝒱₂_perm = [4 1 3] +const 𝒱₂_perm_inv = [3 + 2 + 4 + 1] + const 𝒱₃_perm = [2 4 3 From c1b112287f9f4a7248d0db8235156427f571f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 3 Jul 2023 17:26:43 +0200 Subject: [PATCH 063/143] maybe a 2D working version of getnodes --- src/Adaptivity/AdaptiveCells.jl | 46 +++++++++++++++++++++++++++------ 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index be8d255000..ee601bdc7a 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -456,11 +456,13 @@ getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same function transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{dim,T}) where {dim,T} tree = forest.cells[k] cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate + vertex = vertex .* (2/(2^tree.b)) .- 1 octant_physical_coordinates = sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(vertex),j),1:length(cellnodes)) - return Vec{dim}(octant_physical_coordinates .* 2/(2^tree.b) .- 1) + return Vec{dim}(octant_physical_coordinates) end #TODO: this function should wrap the LNodes Iterator of IBWG2015 +#TODO: need 𝒱₃ perm tables function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Set{Tuple{Int,NTuple{dim,Int32}}}() for (k,tree) in enumerate(forest.cells) @@ -475,23 +477,49 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} _vertices = vertices(root(dim),tree.b) for (vi,v) in enumerate(_vertices) vertex_neighbor = forest.topology.vertex_neighbor[k,vi] - if k > vertex_neighbor[1] + if length(vertex_neighbor) == 0 + continue + end + if k > vertex_neighbor[1][1] delete!(nodes,(k,v)) end end _faces = faces(root(dim),tree.b) - for (fi,f) in enumerate(_faces) - face_neighbor = forest.topology.face_neighbor[k,fi] - if k > face_neighbor[1] + for (fi,f) in enumerate(_faces) # fi in p4est notation + face_neighbor = forest.topology.face_neighbor[k,𝒱₂_perm[fi]] + if length(face_neighbor) == 0 + continue + end + k′ = face_neighbor[1][1] + if k > k′ for leaf in tree.leaves for v in vertices(leaf,tree.b) if fi < 3 if v[1] == f[1][1] == f[2][1] - - delete!(nodes,(k,v)) + cache_octant = OctantBWG(leaf.l,v) + cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + if (k′,cache_octant.xyz) ∈ nodes + delete!(nodes,(k,v)) + end + end + elseif fi < 5 + if v[2] == f[1][2] == f[2][2] + cache_octant = OctantBWG(leaf.l,v) + cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + if (k′,cache_octant.xyz) ∈ nodes + delete!(nodes,(k,v)) + end + end + else + @error "help" + if v[3] == f[1][3] == f[2][3] + cache_octant = OctantBWG(leaf.l,v) + cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + if (k′,cache_octant.xyz) ∈ nodes + delete!(nodes,(k,v)) + end end end - delete!(nodes,(k,v)) end end end @@ -815,11 +843,13 @@ const 𝒱₃ = [1 3 5 7 1 2 3 4 5 6 7 8] +# Face indices permutation from p4est idx to Ferrite idx const 𝒱₂_perm = [4 2 1 3] +# Face indices permutation from Ferrite idx to p4est idx const 𝒱₂_perm_inv = [3 2 4 From 0bfd8a4877e124f6141356b04240c7ff89766f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 4 Jul 2023 10:08:43 +0200 Subject: [PATCH 064/143] add refine_all and convenience transform_pointBWG --- src/Adaptivity/AdaptiveCells.jl | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index ee601bdc7a..8ae2af7999 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -287,6 +287,18 @@ function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T end end +function refine_all(forest::ForestBWG,l) + for tree in forest.cells + for leaf in tree.leaves + if leaf.l != l-1 #maxlevel + continue + else + refine!(tree,leaf) + end + end + end +end + function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} _two = T(2) leave_idx = findfirst(x->x==o,octree.leaves) @@ -455,12 +467,14 @@ getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO function transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{dim,T}) where {dim,T} tree = forest.cells[k] - cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate + cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate vertex = vertex .* (2/(2^tree.b)) .- 1 octant_physical_coordinates = sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(vertex),j),1:length(cellnodes)) return Vec{dim}(octant_physical_coordinates) end +transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) + #TODO: this function should wrap the LNodes Iterator of IBWG2015 #TODO: need 𝒱₃ perm tables function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} @@ -473,7 +487,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end end end - for (k,tree) in enumerate(forest.cells) + for (k,tree) in enumerate(forest.cells) _vertices = vertices(root(dim),tree.b) for (vi,v) in enumerate(_vertices) vertex_neighbor = forest.topology.vertex_neighbor[k,vi] @@ -482,7 +496,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end if k > vertex_neighbor[1][1] delete!(nodes,(k,v)) - end + end end _faces = faces(root(dim),tree.b) for (fi,f) in enumerate(_faces) # fi in p4est notation @@ -496,7 +510,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} for v in vertices(leaf,tree.b) if fi < 3 if v[1] == f[1][1] == f[2][1] - cache_octant = OctantBWG(leaf.l,v) + cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) @@ -504,7 +518,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end elseif fi < 5 if v[2] == f[1][2] == f[2][2] - cache_octant = OctantBWG(leaf.l,v) + cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) @@ -513,7 +527,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} else @error "help" if v[3] == f[1][3] == f[2][3] - cache_octant = OctantBWG(leaf.l,v) + cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) @@ -522,7 +536,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end end end - end + end end end return nodes From 6e96c8a68b33e2510d7983477bd7c365d731f04e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 5 Jul 2023 10:29:07 +0200 Subject: [PATCH 065/143] refine_all and coarsen_all at proper place; included some docs --- src/Adaptivity/AdaptiveCells.jl | 86 ++++++++++++++++++++++++++++----- test/test_p4est.jl | 41 ++-------------- 2 files changed, 78 insertions(+), 49 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 8ae2af7999..e975524778 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -287,18 +287,6 @@ function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T end end -function refine_all(forest::ForestBWG,l) - for tree in forest.cells - for leaf in tree.leaves - if leaf.l != l-1 #maxlevel - continue - else - refine!(tree,leaf) - end - end - end -end - function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} _two = T(2) leave_idx = findfirst(x->x==o,octree.leaves) @@ -396,6 +384,10 @@ end ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} `p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] and Isaac, Burstedde, Wilcox, Ghattas [2015] + +## Constructor + ForestBWG(grid::AbstractGrid{dim}, b=_maxlevel[dim-1]) where dim +Builds an adaptive grid based on a non-adaptive one `grid` and a given max refinement level `b`. """ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} cells::Vector{C} @@ -426,6 +418,28 @@ function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim-1]) where dim return ForestBWG(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) end +function refine_all(forest::ForestBWG,l) + for tree in forest.cells + for leaf in tree.leaves + if leaf.l != l-1 #maxlevel + continue + else + refine!(tree,leaf) + end + end + end +end + +function coarsen_all(forest::ForestBWG) + for tree in forest.cells + for leaf in tree.leaves + if child_id(leaf,tree.b) == 1 + coarsen!(tree,leaf) + end + end + end +end + getneighborhood(forest::ForestBWG,idx) = getneighborhood(forest.topology,forest,idx) function getncells(grid::ForestBWG) @@ -465,6 +479,13 @@ end getcelltype(grid::ForestBWG) = eltype(grid.cells) getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO + +""" + transform_pointBWG(forest, vertices) -> Vector{Vec{dim}} + transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{dim,T}) where {dim,T} -> Vec{dim} + +Transformation of a octree coordinate system point `vertex` (or a collection `vertices`) to the corresponding physical coordinate system. +""" function transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{dim,T}) where {dim,T} tree = forest.cells[k] cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate @@ -608,6 +629,26 @@ function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end +""" + face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[2]) -> OctantBWG{3,N,M,T} +Intraoctree face neighbor for a given faceindex `f` (in p4est, i.e. z order convention) and specified maximum refinement level `b`. +Implements Algorithm 5 of BWG p4est paper. + + x-------x-------x + | | | + | 3 | 4 | + | | | + x-------x-------x + | | | + o 1 * 2 | + | | | + x-------x-------x + +Consider octant 1 at `xyz=(0,0)`, a maximum refinement level of 1 and faceindex 2 (marked as `*`). +Then, the computed face neighbor will be octant 2 with `xyz=(1,0)`. +Note that the function is not sensitive in terms of leaving the octree boundaries. +For the above example, a query for face index 1 (marked as `o`) will return an octant outside of the octree with `xyz=(-1,0)`. +""" function face_neighbor(octant::OctantBWG{3,N,M,T}, f::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) @@ -627,7 +668,26 @@ function face_neighbor(octant::OctantBWG{2,N,M,T}, f::T, b::T=_maxlevel[1]) wher end face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) -#TODO: this is working for 2D except rotation, 3D I don't know +""" + transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T2} +Interoctree coordinate transformation of an given octant `o` to the octree `k` coordinate system by virtually pushing `o`s coordinate system through `k`'s face `f`. +Implements Algorithm 8 of BWG p4est paper. + + x-------x-------x + | | | + | 3 | 4 | + | | | + x-------x-------x + | | | + | 1 * 2 | + | | | + x-------x-------x + +Consider 4 octrees with a single leaf each and a maximum refinement level of 1 +This function transforms octant 1 into the coordinate system of octant 2 by specifying `k=2` and `f=1`. +While in the own octree coordinate system octant 1 is at `xyz=(0,0)`, the returned and transformed octant is located at `xyz=(-2,0)` +TODO: this is working for 2D except rotation, 3D I don't know +""" function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index cb783165ef..7f6959975c 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -191,28 +191,8 @@ end ####uniform refinement and coarsening for all cells and levels#### ################################################################## adaptive_grid = ForestBWG(grid,8) - function refine_all(grid::ForestBWG,l) - for tree in adaptive_grid.cells - for leaf in tree.leaves - if leaf.l != l-1 #maxlevel - continue - else - Ferrite.refine!(tree,leaf) - end - end - end - end - function coarsen_all(adaptive_grid) - for tree in adaptive_grid.cells - for leaf in tree.leaves - if Ferrite.child_id(leaf,tree.b) == 1 - Ferrite.coarsen!(tree,leaf) - end - end - end - end for l in 1:8 - refine_all(adaptive_grid,l) + Ferrite.refine_all(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end @@ -224,7 +204,7 @@ end end #now go back from finest to coarsest for l in 7:-1:0 - coarsen_all(adaptive_grid) + Ferrite.coarsen_all(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end @@ -255,14 +235,14 @@ end adaptive_grid = ForestBWG(grid,5) #go from coarsest to finest uniformly for l in 1:5 - refine_all(adaptive_grid,l) + Ferrite.refine_all(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end end #now go back from finest to coarsest for l in 4:-1:0 - coarsen_all(adaptive_grid) + Ferrite.coarsen_all(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end @@ -270,22 +250,11 @@ end end @testset "ForestBWG AbstractGrid Interfacing" begin - function refine_all(grid::ForestBWG,l) - for tree in adaptive_grid.cells - for leaf in tree.leaves - if leaf.l != l-1 #maxlevel - continue - else - Ferrite.refine!(tree,leaf) - end - end - end - end maxlevel = 3 grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,maxlevel) for l in 1:maxlevel - refine_all(adaptive_grid,l) + Ferrite.refine_all(adaptive_grid,l) @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) end end From 06e8d63f434d21e7e0ddcff6f973fc1b25233572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 5 Jul 2023 18:41:54 +0200 Subject: [PATCH 066/143] help --- src/Adaptivity/AdaptiveCells.jl | 208 ++++++++++++++++++++++++++++---- src/Adaptivity/visualization.jl | 18 ++- 2 files changed, 198 insertions(+), 28 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index e975524778..05fdb45deb 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -31,12 +31,10 @@ function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: I y = y | (h*((m-_one) & _two^(dim*i+_one))÷_two^((dim-_one)*i+_one)) z = z | (h*((m-_one) & _two^(dim*i+_two))÷_two^((dim-_one)*i+_two)) end - if dim == 2 - OctantBWG{dim,4,4,T}(l,(x,y)) - elseif dim == 3 - OctantBWG{dim,8,6,T}(l,(x,y,z)) + if dim < 3 + OctantBWG{2,4,4,T}(l,(x,y)) else - error("$dim Dimension not supported") + OctantBWG{3,8,6,T}(l,(x,y,z)) end end @@ -45,9 +43,7 @@ OctantBWG(dim::Int,l::Int,m::Int,b::Int32) = OctantBWG(dim,Int32(l),Int32(m),b) OctantBWG(dim::Int,l::Int32,m::Int,b::Int32) = OctantBWG(dim,l,Int32(m),b) OctantBWG(level::Int,coords::NTuple) = OctantBWG(Int32(level),Int32.(coords)) OctantBWG(level::Int32,coords::NTuple) = OctantBWG(level,Int32.(coords)) -function OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim - dim == 2 ? OctantBWG{2,4,4,Int32}(level,coords) : OctantBWG{3,8,6,Int32}(level,coords) -end +OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim = OctantBWG{dim,2^dim,2*dim,Int32}(level,coords) # From BWG 2011 # > The octant coordinates are stored as integers of a fixed number b of bits, @@ -79,10 +75,12 @@ morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2< Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) -root(dim::T) where T<:Integer = zero(OctantBWG{dim,dim^2,2*dim}) +root(dim::T) where T<:Integer = zero(OctantBWG{dim,2^dim,2*dim}) -ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N +ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N # TODO change to how many corners ncorners(o::OctantBWG) = ncorners(typeof(o)) +nnodes(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N +nnodes(o::OctantBWG) = ncorners(typeof(o)) nchilds(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners @@ -146,7 +144,7 @@ function vertices(octant::OctantBWG{dim},b::Integer) where {dim} return ntuple(i->vertex(octant,i,b),_nvertices) end -vertex(octant::OctantBWG, f::OctantFaceIndex, b::Integer) = vertex(octant,f.idx,b) +face(octant::OctantBWG, f::OctantFaceIndex, b::Integer) = face(octant,f.idx,b) function face(octant::OctantBWG{2}, f::Integer, b::Integer) cornerid = view(𝒱₂,f,:) return ntuple(i->vertex(octant, cornerid[i], b),2) @@ -162,7 +160,7 @@ function faces(octant::OctantBWG{dim}, b::Integer) where dim return ntuple(i->face(octant,i,b),_nfaces) end -vertex(octant::OctantBWG, e::OctantEdgeIndex, b::Integer) = vertex(octant,e.idx,b) +edge(octant::OctantBWG, e::OctantEdgeIndex, b::Integer) = edge(octant,e.idx,b) function edge(octant::OctantBWG{3}, e::Integer, b::Integer) cornerid = view(𝒰,e,:) return ntuple(i->vertex(octant,cornerid[i], b),2) @@ -418,7 +416,7 @@ function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim-1]) where dim return ForestBWG(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) end -function refine_all(forest::ForestBWG,l) +function refine_all!(forest::ForestBWG,l) for tree in forest.cells for leaf in tree.leaves if leaf.l != l-1 #maxlevel @@ -430,7 +428,7 @@ function refine_all(forest::ForestBWG,l) end end -function coarsen_all(forest::ForestBWG) +function coarsen_all!(forest::ForestBWG) for tree in forest.cells for leaf in tree.leaves if child_id(leaf,tree.b) == 1 @@ -497,9 +495,10 @@ end transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) #TODO: this function should wrap the LNodes Iterator of IBWG2015 -#TODO: need 𝒱₃ perm tables -function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} +function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Set{Tuple{Int,NTuple{dim,Int32}}}() + _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm + _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves _vertices = vertices(leaf,tree.b) @@ -521,7 +520,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end _faces = faces(root(dim),tree.b) for (fi,f) in enumerate(_faces) # fi in p4est notation - face_neighbor = forest.topology.face_neighbor[k,𝒱₂_perm[fi]] + face_neighbor = forest.topology.face_neighbor[k,_perm[fi]] if length(face_neighbor) == 0 continue end @@ -532,7 +531,7 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} if fi < 3 if v[1] == f[1][1] == f[2][1] cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) end @@ -540,16 +539,15 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} elseif fi < 5 if v[2] == f[1][2] == f[2][2] cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) end end else - @error "help" if v[3] == f[1][3] == f[2][3] cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,𝒱₂_perm_inv[face_neighbor[1][2]],cache_octant) # after transform + cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) end @@ -559,8 +557,138 @@ function getnodes(forest::ForestBWG{dim,C,T}) where {dim,C,T} end end end + if dim > 2 + #TODO add egede dupplication check + end end - return nodes + return collect(nodes) +end + +#TODO unfinished, isreplaced logic fails +function creategridFB23(forest::ForestBWG{dim}) where dim + celltype = dim < 3 ? Quadrilateral : Hexahedron + opposite_corner = dim < 3 ? opposite_corner_2 : opposite_corner_3 + opposite_face = dim < 3 ? opposite_face_2 : opposite_face_3 + leaves = [Dict{Tuple{Int,Int},celltype}() for i in 1:length(forest.cells)] + isreplaced = zeros(Bool,getncells(forest)*nnodes(forest.cells[1])) + pivot_nodeid = 1 + for (k,tree) in enumerate(forest.cells) + for leaf in tree.leaves + mortonid = morton(leaf,tree.b,tree.b) + _nnodes = nnodes(leaf) + leaves[k][(leaf.l,mortonid)] = celltype(ntuple(i->pivot_nodeid+i-1,_nnodes)) + pivot_nodeid += _nnodes + end + end + for (k,tree) in enumerate(forest.cells) + for leaf in tree.leaves + leaf_mortonid = morton(leaf,tree.b,tree.b) + leaf_vertices = vertices(leaf,tree.b) + leaf_faces = faces(leaf,tree.b) + leaf_nodes = leaves[k][(leaf.l,leaf_mortonid)].nodes + for local_nodeid in 1:nnodes(leaf) + node_neighbor = corner_neighbor(leaf, local_nodeid, tree.b) + if !inside(tree,node_neighbor) + #TODO interoctree :) + continue + end + if node_neighbor.l == tree.b + candidates = (parent(node_neighbor,tree.b), node_neighbor) + elseif node_neighbor.l == 0 + continue + else + candidates = (parent(node_neighbor,tree.b), node_neighbor, children(node_neighbor,tree.b)[opposite_corner[local_nodeid]]) + end + for candidate in candidates + candidate_mortonid = morton(candidate,tree.b,tree.b) + owner = leaf_mortonid < candidate_mortonid + if !owner + continue + end + if haskey(leaves[k],(candidate.l,candidate_mortonid)) + v = vertex(candidate, opposite_corner[local_nodeid], tree.b) + if v == leaf_vertices[local_nodeid] + candidate_nodes = leaves[k][(candidate.l,candidate_mortonid)].nodes + isreplaced[candidate_nodes[opposite_corner[local_nodeid]]] = true + altered_nodetuple = replace(candidate_nodes,candidate_nodes[opposite_corner[local_nodeid]] => leaf_nodes[local_nodeid]) + leaves[k][(candidate.l,candidate_mortonid)] = celltype(altered_nodetuple) + end + end + end + end + for local_faceid in 1:2*dim #true for all hypercubes + _face_neighbor = face_neighbor(leaf, local_faceid, tree.b) + if !inside(tree, _face_neighbor) + #TODO interoctree :) + continue + end + if _face_neighbor.l == tree.b + candidates = (parent(_face_neighbor,tree.b), _face_neighbor) + elseif _face_neighbor.l == 0 + continue + else + kidz = children(_face_neighbor,tree.b) + if local_faceid < 3 + small_c1 = kidz[opposite_face[local_faceid]] + small_c2 = OctantBWG(dim,small_c1.l,morton(small_c1,small_c1.l,tree.b)+2,tree.b) + else #TODO add 3D case + small_c1 = kidz[opposite_face[local_faceid]] + small_c2 = OctantBWG(dim,small_c1.l,morton(small_c1,small_c1.l,tree.b)+1,tree.b) + end + if _face_neighbor.l - 1 != 0 + candidates = (parent(_face_neighbor,tree.b), _face_neighbor, small_c1, small_c2) + else + candidates = (_face_neighbor, small_c1, small_c2) + end + end + for candidate in candidates + candidate_mortonid = morton(candidate,tree.b,tree.b) + owner = leaf_mortonid < candidate_mortonid + if !owner + continue + end + if haskey(leaves[k],(candidate.l,candidate_mortonid)) + neighbor_face = face(candidate, opposite_face[local_faceid], tree.b) + pivot_face = leaf_faces[local_faceid] + contributing_nodes = @view 𝒱₂[local_faceid,:] + contributing_nodes_opposite = @view 𝒱₂[opposite_face[local_faceid],:] + if neighbor_face[1] == pivot_face[1] && neighbor_face[2] == pivot_face[2] + candidate_nodes = leaves[k][(candidate.l,candidate_mortonid)].nodes + altered_nodetuple = candidate_nodes + if candidate_nodes[contributing_nodes_opposite[1]] != leaf_nodes[contributing_nodes[1]] + #isreplaced[candidate_nodes[contributing_nodes_opposite[1]]] = true + altered_nodetuple = replace(altered_nodetuple,candidate_nodes[contributing_nodes_opposite[1]] => leaf_nodes[contributing_nodes[1]]) + end + if candidate_nodes[contributing_nodes_opposite[2]] != leaf_nodes[contributing_nodes[2]] + #isreplaced[candidate_nodes[contributing_nodes_opposite[2]]] = true + altered_nodetuple = replace(altered_nodetuple,candidate_nodes[contributing_nodes_opposite[2]] => leaf_nodes[contributing_nodes[2]]) + end + leaves[k][(candidate.l,candidate_mortonid)] = celltype(altered_nodetuple) + end + end + end + end + end + end + shift = zeros(Int,length(isreplaced)) + for (id,r) in enumerate(isreplaced) + if id == 1 + continue + end + if r + shift[id] = shift[id-1] + 1 + else + shift[id] = shift[id-1] + end + end + for k in 1:length(leaves) + for ((l,m),cell) in leaves[k] + old_nodes = cell.nodes + new_nodes = ntuple(n->old_nodes[n]-shift[old_nodes[n]],length(old_nodes)) + leaves[k][(l,m)] = celltype(new_nodes) + end + end + return leaves end function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) @@ -693,8 +821,9 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2 _two = T2(2) #currently rotation not encoded perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) + _perminv = (dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv) kprime, fprime = getneighborhood(forest,FaceIndex(k,perm[f]))[1] - fprime = 𝒱₂_perm_inv[fprime] + fprime = _perminv[fprime] sprime = _one - (((f - _one) & _one) ⊻ ((fprime - _one) & _one)) s = zeros(T2,3) b = zeros(T2,3) @@ -936,6 +1065,13 @@ const 𝒱₃_perm = [2 1 6] +const 𝒱₃_perm_inv = [5 + 1 + 3 + 2 + 4 + 6] + const ℛ = [1 2 2 1 1 2 3 1 1 2 2 1 3 1 1 2 2 1 @@ -955,3 +1091,29 @@ const 𝒫 = [1 2 3 4 3 4 1 2 4 2 3 1 4 3 2 1] + +const opposite_corner_2 = [4, + 3, + 2, + 1] + +const opposite_corner_3 = [8, + 7, + 6, + 5, + 4, + 3, + 2, + 1] + +const opposite_face_2 = [2, + 1, + 4, + 3] + +const opposite_face_3 = [2, + 1, + 4, + 3, + 6, + 5,] diff --git a/src/Adaptivity/visualization.jl b/src/Adaptivity/visualization.jl index 0bf2420a18..4f07c7b02a 100644 --- a/src/Adaptivity/visualization.jl +++ b/src/Adaptivity/visualization.jl @@ -1,6 +1,6 @@ function visualize_grid(forest::ForestBWG{dim}) where dim fig = GLMakie.Figure() - ax = GLMakie.Axis(fig[1,1]) + ax = dim < 3 ? GLMakie.Axis(fig[1,1]) : GLMakie.LScene(fig[1,1]) for tree in forest.cells for leaf in tree.leaves cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate |> collect @@ -16,10 +16,18 @@ function visualize_grid(forest::ForestBWG{dim}) where dim center = sum(octant_physical_coordinates,dims=1) ./ 4 #GLMakie.scatter!(ax,center,color=:black,markersize=25) facetable = dim == 2 ? Ferrite.𝒱₂ : Ferrite.𝒱₃ - for faceids in eachrow(facetable) - x = octant_physical_coordinates[faceids,1] + (octant_physical_coordinates[faceids,1] .- center[1])*0.02 - y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 - GLMakie.lines!(ax,x,y,color=:black) + for faceids in eachrow(facetable) + if dim < 3 + x = octant_physical_coordinates[faceids,1] + (octant_physical_coordinates[faceids,1] .- center[1])*0.02 + y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 + GLMakie.lines!(ax,x,y,color=:black) + else + faceids = [faceids[1], faceids[2], faceids[4], faceids[3]] + x = octant_physical_coordinates[faceids,1] + (octant_physical_coordinates[faceids,1] .- center[1])*0.02 + y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 + z = octant_physical_coordinates[faceids,3] + (octant_physical_coordinates[faceids,3] .- center[3])*0.02 + GLMakie.lines!(ax,x,y,z,color=:black) + end end end end From 62195243a521ee6c24e2c8a8f21200f8cecb74dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 6 Jul 2023 13:02:42 +0200 Subject: [PATCH 067/143] enhance old algorithm by nodeid and nodeowners; need a isreplaced node logic for consecutive numbering --- src/Adaptivity/AdaptiveCells.jl | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 05fdb45deb..55c48cbf28 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -495,15 +495,21 @@ end transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) #TODO: this function should wrap the LNodes Iterator of IBWG2015 -function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} +function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Set{Tuple{Int,NTuple{dim,Int32}}}() _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() + nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() + pivot_nodeid = 1 for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves _vertices = vertices(leaf,tree.b) for v in _vertices push!(nodes,(k,v)) + nodeids[(k,v)] = pivot_nodeid + pivot_nodeid += 1 + nodeowners[(k,v)] = (k,v) end end end @@ -516,6 +522,10 @@ function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} end if k > vertex_neighbor[1][1] delete!(nodes,(k,v)) + new_v = vertex(root(dim),vertex_neighbor[1][2],tree.b) + new_k = vertex_neighbor[1][1] + nodeids[(k,v)] = nodeids[(new_k,new_v)] + nodeowners[(k,v)] = (new_k,new_v) end end _faces = faces(root(dim),tree.b) @@ -534,6 +544,8 @@ function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) + nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] + nodeowners[(k,v)] = (k′,cache_octant.xyz) end end elseif fi < 5 @@ -542,6 +554,8 @@ function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) + nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] + nodeowners[(k,v)] = (k′,cache_octant.xyz) end end else @@ -550,6 +564,8 @@ function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes delete!(nodes,(k,v)) + nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] + nodeowners[(k,v)] = (k′,cache_octant.xyz) end end end @@ -561,7 +577,7 @@ function createsnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} #TODO add egede dupplication check end end - return collect(nodes) + return collect(nodes), nodeids, nodeowners end #TODO unfinished, isreplaced logic fails From 1834b3c8c66b2e95e950f894e724d5abbd1642a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 6 Jul 2023 14:38:22 +0200 Subject: [PATCH 068/143] create grid works in 2D I guess --- src/Adaptivity/AdaptiveCells.jl | 46 ++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 55c48cbf28..59ce3d2562 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -495,8 +495,9 @@ end transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) #TODO: this function should wrap the LNodes Iterator of IBWG2015 -function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} - nodes = Set{Tuple{Int,NTuple{dim,Int32}}}() +function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} + nodes = Vector{Tuple{Int,NTuple{dim,Int32}}}() + sizehint!(nodes,getncells(forest)*2^dim) _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() @@ -516,12 +517,12 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} for (k,tree) in enumerate(forest.cells) _vertices = vertices(root(dim),tree.b) for (vi,v) in enumerate(_vertices) - vertex_neighbor = forest.topology.vertex_neighbor[k,vi] + vertex_neighbor = forest.topology.vertex_vertex_neighbor[k,vi] if length(vertex_neighbor) == 0 continue end if k > vertex_neighbor[1][1] - delete!(nodes,(k,v)) + #delete!(nodes,(k,v)) new_v = vertex(root(dim),vertex_neighbor[1][2],tree.b) new_k = vertex_neighbor[1][1] nodeids[(k,v)] = nodeids[(new_k,new_v)] @@ -530,7 +531,7 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} end _faces = faces(root(dim),tree.b) for (fi,f) in enumerate(_faces) # fi in p4est notation - face_neighbor = forest.topology.face_neighbor[k,_perm[fi]] + face_neighbor = forest.topology.face_face_neighbor[k,_perm[fi]] if length(face_neighbor) == 0 continue end @@ -543,7 +544,7 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes - delete!(nodes,(k,v)) + #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) end @@ -553,7 +554,7 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes - delete!(nodes,(k,v)) + #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) end @@ -563,7 +564,7 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform if (k′,cache_octant.xyz) ∈ nodes - delete!(nodes,(k,v)) + #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) end @@ -577,7 +578,18 @@ function createnodes1(forest::ForestBWG{dim,C,T}) where {dim,C,T} #TODO add egede dupplication check end end - return collect(nodes), nodeids, nodeowners + celltype = dim < 3 ? Quadrilateral : Hexahedron + cells = celltype[] + cellnodes = zeros(Int,2^dim) + node_map = dim < 3 ? node_map₂ : node_map₃ + for (k,tree) in enumerate(forest.cells) + for leaf in tree.leaves + _vertices = vertices(leaf,tree.b) + cellnodes = ntuple(i-> nodeids[nodeowners[(k,_vertices[i])]],length(_vertices)) + push!(cells,celltype(ntuple(i->cellnodes[node_map[i]],length(cellnodes)))) + end + end + return Grid(cells,transform_pointBWG(forest,nodes) .|> Node) end #TODO unfinished, isreplaced logic fails @@ -1132,4 +1144,18 @@ const opposite_face_3 = [2, 4, 3, 6, - 5,] + 5] + +const node_map₂ = [1, + 2, + 4, + 3] + +const node_map₃ = [1, + 2, + 4, + 3, + 5, + 6, + 8, + 7] From e31d62b6cebeb7e653e5017080ce76b37f14fa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 6 Jul 2023 15:51:12 +0200 Subject: [PATCH 069/143] be in inner peace by distributing with reversed policy --- src/Adaptivity/AdaptiveCells.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 59ce3d2562..846a334a63 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -502,14 +502,14 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() - pivot_nodeid = 1 - for (k,tree) in enumerate(forest.cells) - for leaf in tree.leaves + pivot_nodeid = getncells(forest)*2^dim + for (k,tree) in enumerate(Iterators.reverse(forest.cells))# reverse for smallest k ownership policy in nodeids + for leaf in Iterators.reverse(tree.leaves)# reverse for smallest k ownership policy in nodeids _vertices = vertices(leaf,tree.b) - for v in _vertices + for v in Iterators.reverse(_vertices)# reverse for smallest k ownership policy in nodeids push!(nodes,(k,v)) nodeids[(k,v)] = pivot_nodeid - pivot_nodeid += 1 + pivot_nodeid -= 1 nodeowners[(k,v)] = (k,v) end end From f42f0a17d3012a8dd8c617402fb0092f337efef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 6 Jul 2023 15:57:30 +0200 Subject: [PATCH 070/143] revert policy again, I'm not smart enough --- src/Adaptivity/AdaptiveCells.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 846a334a63..1b733265d3 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -502,14 +502,14 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() - pivot_nodeid = getncells(forest)*2^dim - for (k,tree) in enumerate(Iterators.reverse(forest.cells))# reverse for smallest k ownership policy in nodeids - for leaf in Iterators.reverse(tree.leaves)# reverse for smallest k ownership policy in nodeids + pivot_nodeid = 1 + for (k,tree) in enumerate(forest.cells) + for leaf in tree.leaves _vertices = vertices(leaf,tree.b) - for v in Iterators.reverse(_vertices)# reverse for smallest k ownership policy in nodeids + for v in _vertices push!(nodes,(k,v)) nodeids[(k,v)] = pivot_nodeid - pivot_nodeid -= 1 + pivot_nodeid += 1 nodeowners[(k,v)] = (k,v) end end @@ -521,7 +521,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} if length(vertex_neighbor) == 0 continue end - if k > vertex_neighbor[1][1] + if k < vertex_neighbor[1][1] #delete!(nodes,(k,v)) new_v = vertex(root(dim),vertex_neighbor[1][2],tree.b) new_k = vertex_neighbor[1][1] @@ -536,7 +536,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} continue end k′ = face_neighbor[1][1] - if k > k′ + if k < k′ for leaf in tree.leaves for v in vertices(leaf,tree.b) if fi < 3 From a33cb4a7f606f16108583ea4f2f4a6b853d8c17e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 7 Jul 2023 09:53:03 +0200 Subject: [PATCH 071/143] add script to solve on p4est grid; works for unrefined grid already; refined one needs handling of boundarysets --- src/Adaptivity/AdaptiveCells.jl | 2 +- test/runtests.jl | 1 + test/test_p4est.jl | 10 +-- test/test_p4est_example.jl | 150 ++++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 6 deletions(-) create mode 100644 test/test_p4est_example.jl diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 1b733265d3..9b0f0fce31 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -589,7 +589,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} push!(cells,celltype(ntuple(i->cellnodes[node_map[i]],length(cellnodes)))) end end - return Grid(cells,transform_pointBWG(forest,nodes) .|> Node) + return Grid(cells,transform_pointBWG(forest,nodes) .|> Node, cellsets=forest.cellsets, nodesets=forest.nodesets, facesets=forest.facesets, edgesets=forest.edgesets, vertexsets=forest.vertexsets) end #TODO unfinished, isreplaced logic fails diff --git a/test/runtests.jl b/test/runtests.jl index ac0a0b7d42..e20ee2e4d7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -38,4 +38,5 @@ end #HAS_EXTENSIONS && include("blockarrays.jl") #include("test_examples.jl") include("test_p4est.jl") +include("test_p4est_example.jl") @test all(x -> isdefined(Ferrite, x), names(Ferrite)) # Test that all exported symbols are defined diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 7f6959975c..1a8f26d41b 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -192,7 +192,7 @@ end ################################################################## adaptive_grid = ForestBWG(grid,8) for l in 1:8 - Ferrite.refine_all(adaptive_grid,l) + Ferrite.refine_all!(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end @@ -204,7 +204,7 @@ end end #now go back from finest to coarsest for l in 7:-1:0 - Ferrite.coarsen_all(adaptive_grid) + Ferrite.coarsen_all!(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end @@ -235,14 +235,14 @@ end adaptive_grid = ForestBWG(grid,5) #go from coarsest to finest uniformly for l in 1:5 - Ferrite.refine_all(adaptive_grid,l) + Ferrite.refine_all!(adaptive_grid,l) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end end #now go back from finest to coarsest for l in 4:-1:0 - Ferrite.coarsen_all(adaptive_grid) + Ferrite.coarsen_all!(adaptive_grid) for tree in adaptive_grid.cells @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end @@ -254,7 +254,7 @@ end grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,maxlevel) for l in 1:maxlevel - Ferrite.refine_all(adaptive_grid,l) + Ferrite.refine_all!(adaptive_grid,l) @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) end end diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl new file mode 100644 index 0000000000..290d006bd6 --- /dev/null +++ b/test/test_p4est_example.jl @@ -0,0 +1,150 @@ +using Ferrite, Test + +module ConvergenceTestHelper + +using Ferrite, SparseArrays, ForwardDiff, Test +import LinearAlgebra: diag + +get_geometry(::Ferrite.Interpolation{RefLine}) = Line +get_geometry(::Ferrite.Interpolation{RefQuadrilateral}) = Quadrilateral +get_geometry(::Ferrite.Interpolation{RefTriangle}) = Triangle +get_geometry(::Ferrite.Interpolation{RefPrism}) = Wedge +get_geometry(::Ferrite.Interpolation{RefHexahedron}) = Hexahedron +get_geometry(::Ferrite.Interpolation{RefTetrahedron}) = Tetrahedron + +get_quadrature_order(::Lagrange{shape, order}) where {shape, order} = 2*order +get_quadrature_order(::Serendipity{shape, order}) where {shape, order} = 2*order +get_quadrature_order(::CrouzeixRaviart{shape, order}) where {shape, order} = 2*order+1 + +get_N(::Ferrite.Interpolation{shape, 1}) where {shape} = 19 +get_N(::Ferrite.Interpolation{shape, 2}) where {shape} = 12 +get_N(::Ferrite.Interpolation{shape, 3}) where {shape} = 8 +get_N(::Ferrite.Interpolation{shape, 4}) where {shape} = 5 +get_N(::Ferrite.Interpolation{shape, 5}) where {shape} = 3 + +analytical_solution(x) = prod(cos, x*π/2) +analytical_rhs(x) = -sum(diag(ForwardDiff.hessian(analytical_solution,x))) + +# Standard assembly copy pasta for Poisson problem +function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues, coords) + n_basefuncs = getnbasefunctions(cellvalues) + ## Reset to 0 + fill!(Ke, 0) + fill!(fe, 0) + ## Loop over quadrature points + for q_point in 1:getnquadpoints(cellvalues) + ## Get the quadrature weight + dΩ = getdetJdV(cellvalues, q_point) + x = spatial_coordinate(cellvalues, q_point, coords) + ## Loop over test shape functions + for i in 1:n_basefuncs + δu = shape_value(cellvalues, q_point, i) + ∇δu = shape_gradient(cellvalues, q_point, i) + ## Add contribution to fe + fe[i] += analytical_rhs(x) * δu * dΩ + ## Loop over trial shape functions + for j in 1:n_basefuncs + ∇u = shape_gradient(cellvalues, q_point, j) + ## Add contribution to Ke + Ke[i, j] += (∇δu ⋅ ∇u) * dΩ + end + end + end + @show norm(Ke), norm(fe) + return Ke, fe +end + +# Standard assembly copy pasta for Poisson problem +function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHandler) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + Ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Allocate global force vector f + f = zeros(ndofs(dh)) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cels + for cell in CellIterator(dh) + ## Reinitialize cellvalues for this cell + reinit!(cellvalues, cell) + coords = getcoordinates(cell) + ## Compute element contribution + assemble_element!(Ke, fe, cellvalues, coords) + ## Assemble Ke and fe into K and f + assemble!(assembler, celldofs(cell), Ke, fe) + end + return K, f +end + +# Check L2 convergence +function check_and_compute_convergence(dh, u, cellvalues, testatol) + L2norm = 0.0 + L∞norm = 0.0 + for cell in CellIterator(dh) + reinit!(cellvalues, cell) + n_basefuncs = getnbasefunctions(cellvalues) + coords = getcoordinates(cell) + uₑ = u[celldofs(cell)] + for q_point in 1:getnquadpoints(cellvalues) + dΩ = getdetJdV(cellvalues, q_point) + x = spatial_coordinate(cellvalues, q_point, coords) + uₐₙₐ = prod(cos, x*π/2) + uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ) + L2norm += norm(uₐₙₐ-uₐₚₚᵣₒₓ)*dΩ + L∞norm = max(L∞norm, norm(uₐₙₐ-uₐₚₚᵣₒₓ)) + @test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=testatol) + end + end + L2norm, L∞norm +end + +# Assemble and solve +function solve(dh, ch, cellvalues) + K, f = assemble_global(cellvalues, create_sparsity_pattern(dh), dh); + apply!(K, f, ch) + u = K \ f; +end + +function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N) + # Construct Ferrite stuff + dh = DofHandler(grid) + add!(dh, :u, interpolation) + close!(dh); + + ch = ConstraintHandler(dh); + ∂Ω = union( + values(getfacesets(grid))... + ); + dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) + add!(ch, dbc); + close!(ch); + + cellvalues = CellValues(qr, interpolation, interpolation_geo); + + return dh, ch, cellvalues +end + +end # module ConvergenceTestHelper + +@testset "convergence analysis" begin + @testset "$interpolation" for interpolation in ( + Lagrange{RefQuadrilateral, 1}(), + #Lagrange{RefHexahedron, 1}(), + ) + # Generate a grid ... + geometry = ConvergenceTestHelper.get_geometry(interpolation) + interpolation_geo = interpolation + N = ConvergenceTestHelper.get_N(interpolation) + grid = generate_grid(geometry, ntuple(x->19, Ferrite.getdim(geometry))); + adaptive_grid = ForestBWG(grid,1) + grid_transfered = Ferrite.creategrid(adaptive_grid) + # ... a suitable quadrature rule ... + qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) + qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) + # ... and then pray to the gods of convergence. + dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N) + u = ConvergenceTestHelper.solve(dh, ch, cellvalues) + ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2) + end +end From 5a75a40653dc404e956e3e30b24bc5e5086097f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 7 Jul 2023 10:32:37 +0200 Subject: [PATCH 072/143] verify that it works for refinement even though boundarysets have been explicitely added --- src/Adaptivity/AdaptiveCells.jl | 8 +++++++- test/test_p4est_example.jl | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 9b0f0fce31..a70480fa7e 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -589,7 +589,13 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} push!(cells,celltype(ntuple(i->cellnodes[node_map[i]],length(cellnodes)))) end end - return Grid(cells,transform_pointBWG(forest,nodes) .|> Node, cellsets=forest.cellsets, nodesets=forest.nodesets, facesets=forest.facesets, edgesets=forest.edgesets, vertexsets=forest.vertexsets) + #Grid(cells,transform_pointBWG(forest,nodes) .|> Node, cellsets=forest.cellsets, nodesets=forest.nodesets, facesets=forest.facesets, edgesets=forest.edgesets, vertexsets=forest.vertexsets) + grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node) + addfaceset!(grid,"top", x->x[2] == 1) + addfaceset!(grid,"bottom", x->x[2] == -1) + addfaceset!(grid,"left", x->x[1] == -1) + addfaceset!(grid,"right", x->x[1] == 1) + return grid end #TODO unfinished, isreplaced logic fails diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 290d006bd6..2eb56a2aef 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -136,8 +136,14 @@ end # module ConvergenceTestHelper geometry = ConvergenceTestHelper.get_geometry(interpolation) interpolation_geo = interpolation N = ConvergenceTestHelper.get_N(interpolation) - grid = generate_grid(geometry, ntuple(x->19, Ferrite.getdim(geometry))); - adaptive_grid = ForestBWG(grid,1) + grid = generate_grid(geometry, ntuple(x->3, Ferrite.getdim(geometry))); + adaptive_grid = ForestBWG(grid,4) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine_all!(adaptive_grid,2) + Ferrite.refine_all!(adaptive_grid,3) + Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[8]) + Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[5]) grid_transfered = Ferrite.creategrid(adaptive_grid) # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) @@ -145,6 +151,9 @@ end # module ConvergenceTestHelper # ... and then pray to the gods of convergence. dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2) + #ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2) + vtk_grid("p4est_test.vtu",dh) do vtk + vtk_point_data(vtk,dh,u) + end end end From afb54fab860241e1c1ffe1f7c1b6234f71f354be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 31 Jul 2023 17:04:36 +0200 Subject: [PATCH 073/143] reconstruct facesets in 2D for refined grid --- src/Adaptivity/AdaptiveCells.jl | 42 ++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index a70480fa7e..2bc55f2a29 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -589,15 +589,45 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} push!(cells,celltype(ntuple(i->cellnodes[node_map[i]],length(cellnodes)))) end end - #Grid(cells,transform_pointBWG(forest,nodes) .|> Node, cellsets=forest.cellsets, nodesets=forest.nodesets, facesets=forest.facesets, edgesets=forest.edgesets, vertexsets=forest.vertexsets) - grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node) - addfaceset!(grid,"top", x->x[2] == 1) - addfaceset!(grid,"bottom", x->x[2] == -1) - addfaceset!(grid,"left", x->x[1] == -1) - addfaceset!(grid,"right", x->x[1] == 1) + facesets = reconstruct_facesets(forest) #TODO edge, node and cellsets + grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node, facesets=facesets) return grid end +function reconstruct_facesets(forest::ForestBWG{dim}) where dim + new_facesets = typeof(forest.facesets)() + for (facesetname, faceset) in forest.facesets + new_faceset = typeof(faceset)() + for faceidx in faceset + pivot_tree = forest.cells[faceidx[1]] + last_cellid = faceidx[1] != 1 ? sum(length,@view(forest.cells[1:(faceidx[1]-1)])) : 0 + pivot_faceid = faceidx[2] + pivot_face = faces(root(dim),pivot_tree.b)[𝒱₂_perm_inv[pivot_faceid]] + for (leaf_idx,leaf) in enumerate(pivot_tree.leaves) + for (leaf_face_idx,leaf_face) in enumerate(faces(leaf,pivot_tree.b)) + if contains_face(pivot_face,leaf_face) + ferrite_leaf_face_idx = 𝒱₂_perm[leaf_face_idx] + push!(new_faceset,FaceIndex(last_cellid+leaf_idx,ferrite_leaf_face_idx)) + end + end + end + end + new_facesets[facesetname] = new_faceset + end + return new_facesets +end + +# TODO verify and generalize +function contains_face(mface::Tuple{Tuple{T,T},Tuple{T,T}},sface::Tuple{Tuple{T,T},Tuple{T,T}}) where T + if mface[1][1] == sface[1][1] && mface[2][1] == sface[2][1] # vertical + return mface[1][2] ≤ sface[1][2] ≤ sface[2][2] ≤ mface[2][2] + elseif mface[1][2] == sface[1][2] && mface[2][2] == sface[2][2] # horizontal + return mface[1][1] ≤ sface[1][1] ≤ sface[2][1] ≤ mface[2][1] + else + return false + end +end + #TODO unfinished, isreplaced logic fails function creategridFB23(forest::ForestBWG{dim}) where dim celltype = dim < 3 ? Quadrilateral : Hexahedron From d7b38582bb04cb6e5817b9f7ea264c8a08639475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 31 Jul 2023 17:21:05 +0200 Subject: [PATCH 074/143] fix test --- test/test_p4est_example.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 2eb56a2aef..62475aa356 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -114,7 +114,7 @@ function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N) ch = ConstraintHandler(dh); ∂Ω = union( - values(getfacesets(grid))... + values(Ferrite.getfacesets(grid))... ); dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) add!(ch, dbc); From 6be5454e17ac62703bd70bde8036d284693f9571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 2 Aug 2023 13:53:24 +0200 Subject: [PATCH 075/143] 'localize' comparison of neighboring octree nodes --- src/Adaptivity/AdaptiveCells.jl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2bc55f2a29..a30a87f9b3 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -543,7 +543,9 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} if v[1] == f[1][1] == f[2][1] cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - if (k′,cache_octant.xyz) ∈ nodes + #TODO check if its worth to change this comparison from ∈ nodes to ∈ all nodes of k' + #if (k′,cache_octant.xyz) ∈ nodes + if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) @@ -553,7 +555,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} if v[2] == f[1][2] == f[2][2] cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - if (k′,cache_octant.xyz) ∈ nodes + #if (k′,cache_octant.xyz) ∈ nodes + if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) @@ -563,7 +566,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} if v[3] == f[1][3] == f[2][3] cache_octant = OctantBWG(leaf.l,v) cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - if (k′,cache_octant.xyz) ∈ nodes + #if (k′,cache_octant.xyz) ∈ nodes + if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) From 53ce18a1014bee60ff8b100b5069b5d024f4539a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 2 Aug 2023 14:23:23 +0200 Subject: [PATCH 076/143] simplify vertex similar to face check --- src/Adaptivity/AdaptiveCells.jl | 49 +++++++++++---------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index a30a87f9b3..322ea8c07f 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -537,41 +537,24 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end k′ = face_neighbor[1][1] if k < k′ + if fi < 3 + parallel_axis = 1 + elseif 3 ≤ fi < 5 + parallel_axis = 2 + elseif 5 ≤ fi < 7 + parallel_axis = 3 + end for leaf in tree.leaves for v in vertices(leaf,tree.b) - if fi < 3 - if v[1] == f[1][1] == f[2][1] - cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - #TODO check if its worth to change this comparison from ∈ nodes to ∈ all nodes of k' - #if (k′,cache_octant.xyz) ∈ nodes - if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) - #delete!(nodes,(k,v)) - nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] - nodeowners[(k,v)] = (k′,cache_octant.xyz) - end - end - elseif fi < 5 - if v[2] == f[1][2] == f[2][2] - cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - #if (k′,cache_octant.xyz) ∈ nodes - if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) - #delete!(nodes,(k,v)) - nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] - nodeowners[(k,v)] = (k′,cache_octant.xyz) - end - end - else - if v[3] == f[1][3] == f[2][3] - cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - #if (k′,cache_octant.xyz) ∈ nodes - if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) - #delete!(nodes,(k,v)) - nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] - nodeowners[(k,v)] = (k′,cache_octant.xyz) - end + if v[parallel_axis] == f[1][parallel_axis] == f[2][parallel_axis] + cache_octant = OctantBWG(leaf.l,v) + cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform + #TODO check if its worth to change this comparison from ∈ nodes to ∈ all nodes of k' + #if (k′,cache_octant.xyz) ∈ nodes + if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) + #delete!(nodes,(k,v)) + nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] + nodeowners[(k,v)] = (k′,cache_octant.xyz) end end end From a5286dbc121e3b35540c966b87ff1107f3eb9f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Aug 2023 09:30:46 +0200 Subject: [PATCH 077/143] start hanging nodes --- src/Adaptivity/AdaptiveCells.jl | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 322ea8c07f..8efd83c674 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -536,6 +536,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} continue end k′ = face_neighbor[1][1] + neighbor_vertices = vertices.(forest.cells[k′].leaves,forest.cells[k′].b) if k < k′ if fi < 3 parallel_axis = 1 @@ -551,7 +552,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform #TODO check if its worth to change this comparison from ∈ nodes to ∈ all nodes of k' #if (k′,cache_octant.xyz) ∈ nodes - if any((cache_octant.xyz,) .∈ vertices.(forest.cells[k′].leaves,forest.cells[k′].b)) + if any((cache_octant.xyz,) .∈ neighbor_vertices) #delete!(nodes,(k,v)) nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] nodeowners[(k,v)] = (k′,cache_octant.xyz) @@ -578,7 +579,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end facesets = reconstruct_facesets(forest) #TODO edge, node and cellsets grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node, facesets=facesets) - return grid + #hnodes = hangingnodes(forest) + return grid#, hnodes end function reconstruct_facesets(forest::ForestBWG{dim}) where dim @@ -604,6 +606,28 @@ function reconstruct_facesets(forest::ForestBWG{dim}) where dim return new_facesets end +function hangingnodes(forest::ForestBWG{dim}) where dim + _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm + _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + for (k,tree) in forest.cells + rootfaces = faces(root(dim),tree.b) + for (l,leaf) in enumerate(tree.leaves) + for (root_fi,root_f) in enumerate(rootfaces) # fi in p4est notation + face_neighbor = forest.topology.face_face_neighbor[k,_perm[root_fi]] + if length(face_neighbor) == 0 + continue + end + for (leaf_face_idx,leaf_face) in enumerate(faces(leaf,tree.b)) + if contains_face(root_f,leaf_face) + end + end + root_k′ = face_neighbor[1][1] + root_fi′ = face_neighbor[1][2] + end + end + end +end + # TODO verify and generalize function contains_face(mface::Tuple{Tuple{T,T},Tuple{T,T}},sface::Tuple{Tuple{T,T},Tuple{T,T}}) where T if mface[1][1] == sface[1][1] && mface[2][1] == sface[2][1] # vertical From bc580957054ba8e29e25e1a5d931b370b2f64e77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Aug 2023 12:43:08 +0200 Subject: [PATCH 078/143] interoctree hnodes detection --- src/Adaptivity/AdaptiveCells.jl | 50 ++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 8efd83c674..9147c3b7da 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -579,8 +579,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end facesets = reconstruct_facesets(forest) #TODO edge, node and cellsets grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node, facesets=facesets) - #hnodes = hangingnodes(forest) - return grid#, hnodes + hnodes = hangingnodes(forest, nodeids, nodeowners) + return grid, hnodes end function reconstruct_facesets(forest::ForestBWG{dim}) where dim @@ -606,26 +606,38 @@ function reconstruct_facesets(forest::ForestBWG{dim}) where dim return new_facesets end -function hangingnodes(forest::ForestBWG{dim}) where dim +function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv - for (k,tree) in forest.cells + candidate_octants = eltype(forest.cells)[] + neighbor_octants = eltype(forest.cells)[] + hnodes = Dict{Tuple{Int,NTuple{dim,Int32}},Vector{Tuple{Int,NTuple{dim,Int32}}}}() + for (k,tree) in enumerate(forest.cells) rootfaces = faces(root(dim),tree.b) - for (l,leaf) in enumerate(tree.leaves) - for (root_fi,root_f) in enumerate(rootfaces) # fi in p4est notation - face_neighbor = forest.topology.face_face_neighbor[k,_perm[root_fi]] - if length(face_neighbor) == 0 - continue - end - for (leaf_face_idx,leaf_face) in enumerate(faces(leaf,tree.b)) - if contains_face(root_f,leaf_face) + for (l,leaf) in enumerate(tree.leaves) + if leaf == root(dim) + continue + end + for (ci,c) in enumerate(vertices(leaf,tree.b)) + parent_ = parent(leaf,tree.b) + parentfaces = faces(parent_,tree.b) + for (pface_i, pface) in enumerate(parentfaces) + if iscenter(c,pface) #hanging node candidate + #interoctree branch + inter_neighbor = face_neighbor(parent_, pface_i, tree.b) + inter_neighbor_idx = findfirst(x->x==inter_neighbor,tree.leaves) + if inter_neighbor_idx !== nothing + inter_neighbor_faces = faces(inter_neighbor,tree.b) + nf = findfirst(x->x==pface,inter_neighbor_faces) + hnodes[(k,c)] = [(k,nc) for nc in inter_neighbor_faces[nf]] + end + #intraoctree branch end end - root_k′ = face_neighbor[1][1] - root_fi′ = face_neighbor[1][2] end end end + return hnodes end # TODO verify and generalize @@ -639,6 +651,16 @@ function contains_face(mface::Tuple{Tuple{T,T},Tuple{T,T}},sface::Tuple{Tuple{T, end end +function center(pivot_face) + centerpoint = ntuple(i->0,length(pivot_face[1])) + for c in pivot_face + centerpoint = c .+ centerpoint + end + return centerpoint .÷ length(pivot_face) +end + +iscenter(c,f) = c == center(f) + #TODO unfinished, isreplaced logic fails function creategridFB23(forest::ForestBWG{dim}) where dim celltype = dim < 3 ? Quadrilateral : Hexahedron From 9ef7cf670cd87c26d234580b06f574705ce7bd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Aug 2023 12:47:05 +0200 Subject: [PATCH 079/143] I meant intraoctree.. --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 9147c3b7da..4ae2c86186 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -623,7 +623,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim parentfaces = faces(parent_,tree.b) for (pface_i, pface) in enumerate(parentfaces) if iscenter(c,pface) #hanging node candidate - #interoctree branch + #intraoctree branch inter_neighbor = face_neighbor(parent_, pface_i, tree.b) inter_neighbor_idx = findfirst(x->x==inter_neighbor,tree.leaves) if inter_neighbor_idx !== nothing @@ -631,7 +631,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim nf = findfirst(x->x==pface,inter_neighbor_faces) hnodes[(k,c)] = [(k,nc) for nc in inter_neighbor_faces[nf]] end - #intraoctree branch + #interoctree branch end end end From 820432ea03d071254906a00b3469a893eedfc049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Aug 2023 13:57:01 +0200 Subject: [PATCH 080/143] interoctree works for hanging nodes probably too, need more testing --- src/Adaptivity/AdaptiveCells.jl | 38 ++++++++++++++++++++++++++------- test/test_p4est_example.jl | 3 ++- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 4ae2c86186..7f7b157926 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -609,6 +609,7 @@ end function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + opposite_face = dim == 2 ? opposite_face_2 : opposite_face_3 candidate_octants = eltype(forest.cells)[] neighbor_octants = eltype(forest.cells)[] hnodes = Dict{Tuple{Int,NTuple{dim,Int32}},Vector{Tuple{Int,NTuple{dim,Int32}}}}() @@ -623,15 +624,36 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim parentfaces = faces(parent_,tree.b) for (pface_i, pface) in enumerate(parentfaces) if iscenter(c,pface) #hanging node candidate - #intraoctree branch - inter_neighbor = face_neighbor(parent_, pface_i, tree.b) - inter_neighbor_idx = findfirst(x->x==inter_neighbor,tree.leaves) - if inter_neighbor_idx !== nothing - inter_neighbor_faces = faces(inter_neighbor,tree.b) - nf = findfirst(x->x==pface,inter_neighbor_faces) - hnodes[(k,c)] = [(k,nc) for nc in inter_neighbor_faces[nf]] + neighbor_candidate = face_neighbor(parent_, pface_i, tree.b) + if inside(tree,neighbor_candidate) #intraoctree branch + neighbor_candidate_idx = findfirst(x->x==neighbor_candidate,tree.leaves) + if neighbor_candidate_idx !== nothing + neighbor_candidate_faces = faces(neighbor_candidate,tree.b) + nf = findfirst(x->x==pface,neighbor_candidate_faces) + hnodes[(k,c)] = [(k,nc) for nc in neighbor_candidate_faces[nf]] + break + end + else #interoctree branch + for (ri,rf) in enumerate(rootfaces) + face_neighbor = forest.topology.face_face_neighbor[k,_perm[ri]] + if length(face_neighbor) == 0 + continue + end + if contains_face(rf, pface) + k′ = face_neighbor[1][1] + ri′ = _perminv[face_neighbor[1][2]] + interoctree_neighbor = transform_face(forest, k′, ri′, neighbor_candidate) + interoctree_neighbor_candidate_idx = findfirst(x->x==interoctree_neighbor,forest.cells[k′].leaves) + if interoctree_neighbor_candidate_idx !== nothing + neighbor_candidate_faces = faces(neighbor_candidate,forest.cells[k′].b) + transformed_neighbor_faces = faces(interoctree_neighbor,forest.cells[k′].b) + nf = findfirst(x->x==pface,neighbor_candidate_faces) + hnodes[(k,c)] = [(k′,nc) for nc in transformed_neighbor_faces[nf]] + break + end + end + end end - #interoctree branch end end end diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 62475aa356..7560b6a680 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -144,7 +144,8 @@ end # module ConvergenceTestHelper Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[8]) Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[5]) - grid_transfered = Ferrite.creategrid(adaptive_grid) + grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) + @show hnodes # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) From be6581e446ab654bdb697bade53272cc7a67cfd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 4 Aug 2023 16:06:18 +0200 Subject: [PATCH 081/143] solve with constraint --- src/Adaptivity/AdaptiveCells.jl | 11 ++++++----- test/test_p4est_example.jl | 18 +++++++++++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 7f7b157926..587d658e19 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -610,9 +610,8 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv opposite_face = dim == 2 ? opposite_face_2 : opposite_face_3 - candidate_octants = eltype(forest.cells)[] - neighbor_octants = eltype(forest.cells)[] - hnodes = Dict{Tuple{Int,NTuple{dim,Int32}},Vector{Tuple{Int,NTuple{dim,Int32}}}}() + #hnodes = Dict{Tuple{Int,NTuple{dim,Int32}},Vector{Tuple{Int,NTuple{dim,Int32}}}}() + hnodes = Dict{Int,Vector{Int}}() for (k,tree) in enumerate(forest.cells) rootfaces = faces(root(dim),tree.b) for (l,leaf) in enumerate(tree.leaves) @@ -630,7 +629,8 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim if neighbor_candidate_idx !== nothing neighbor_candidate_faces = faces(neighbor_candidate,tree.b) nf = findfirst(x->x==pface,neighbor_candidate_faces) - hnodes[(k,c)] = [(k,nc) for nc in neighbor_candidate_faces[nf]] + #hnodes[(k,c)] = [(k,nc) for nc in neighbor_candidate_faces[nf]] + hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k,nc)]] for nc in neighbor_candidate_faces[nf]] break end else #interoctree branch @@ -648,7 +648,8 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim neighbor_candidate_faces = faces(neighbor_candidate,forest.cells[k′].b) transformed_neighbor_faces = faces(interoctree_neighbor,forest.cells[k′].b) nf = findfirst(x->x==pface,neighbor_candidate_faces) - hnodes[(k,c)] = [(k′,nc) for nc in transformed_neighbor_faces[nf]] + #hnodes[(k,c)] = [(k′,nc) for nc in transformed_neighbor_faces[nf]] + hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k′,nc)]] for nc in transformed_neighbor_faces[nf]] break end end diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 7560b6a680..dbc9a92f00 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -101,23 +101,28 @@ end # Assemble and solve function solve(dh, ch, cellvalues) - K, f = assemble_global(cellvalues, create_sparsity_pattern(dh), dh); + K, f = assemble_global(cellvalues, create_sparsity_pattern(dh,ch), dh); apply!(K, f, ch) u = K \ f; + apply!(u,ch) end -function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N) +function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N, hnodes) # Construct Ferrite stuff dh = DofHandler(grid) add!(dh, :u, interpolation) - close!(dh); - + dh, vdict, edict, fdict = Ferrite.__close!(dh); + ch = ConstraintHandler(dh); ∂Ω = union( values(Ferrite.getfacesets(grid))... ); dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) add!(ch, dbc); + for (hdof,mdof) in hnodes + lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) + add!(ch,lc) + end close!(ch); cellvalues = CellValues(qr, interpolation, interpolation_geo); @@ -141,16 +146,15 @@ end # module ConvergenceTestHelper Ferrite.refine_all!(adaptive_grid,1) Ferrite.refine_all!(adaptive_grid,2) Ferrite.refine_all!(adaptive_grid,3) - Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[1].leaves[1]) Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[8]) Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[5]) grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) - @show hnodes # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) # ... and then pray to the gods of convergence. - dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N) + dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) #ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2) vtk_grid("p4est_test.vtu",dh) do vtk From 39c4c0280318c7db0e1f297a345c1b384c31cb63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 7 Aug 2023 16:36:10 +0200 Subject: [PATCH 082/143] initial nonworking version of balancing intraoctree --- src/Adaptivity/AdaptiveCells.jl | 89 ++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 587d658e19..6f3a40b3fe 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -307,12 +307,14 @@ OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) -function inside(tree::OctreeBWG{dim},oct::OctantBWG{dim}) where dim - maxsize = _maximum_size(tree.b) +function inside(oct::OctantBWG{dim},b) where dim + maxsize = _maximum_size(b) outside = any(xyz -> xyz >= maxsize, oct.xyz) || any(xyz -> xyz < 0, oct.xyz) return !outside end +inside(tree::OctreeBWG{dim},oct::OctantBWG{dim}) where dim = inside(oct,tree.b) + """ split_array(octree::OctreeBWG, a::OctantBWG) split_array(octantarray, a::OctantBWG, b::Integer) @@ -663,6 +665,89 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim return hnodes end +# Algorithm 7 of preprint Sundar, Sampath, Biros +# https://padas.oden.utexas.edu/static/papers/OctreeBalance21.pdf +function balancetree(tree::OctreeBWG) + W = copy(tree.leaves); P = eltype(tree.leaves)[]; R = eltype(tree.leaves)[] + for l in tree.b:-1:1 #TODO verify to do this until level 1 + Q = [o for o in tree.leaves if o.l == l] + sort!(Q,by=x->morton(x,tree.b,tree.b)) + #construct T + T = eltype(Q)[] + for x in Q + if isempty(T) + push!(T,x) + continue + end + p = parent(x,tree.b) + if p ∉ parent.(T,(tree.b,)) + push!(T,x) + end + end + for t in T + push!(R,t,siblings(t,tree.b)...) + push!(P,possibleneighbors(parent(t,tree.b),l-1,tree.b)...) + end + append!(P,x for x in W if x.l == l-1) + filter!(x->x.l !== l-1, W) + unique!(P) + append!(W,P) + empty!(P) + end + sort!(R,by=x->morton(x,tree.b,tree.b)) + linearise!(R,tree.b) + return OctreeBWG(R,tree.b,tree.nodes) +end + +# Algorithm 8 of preprint Sundar, Sampath, Biros +# inverted the algorithm to delete! instead of add incrementally to a new array +function linearise!(leaves::Vector{T},b) where T<:OctantBWG + i = 1 + while i <= length(leaves)-1 + if isancestor(leaves[i],leaves[i+1],b) # if i isancestor of i+1 + deleteat!(leaves,i) + end + i += 1 + end +end + +function siblings(o::OctantBWG,b;include_self=false) + siblings = children(parent(o,b),b) + if !include_self + siblings = filter(x-> x !== o, siblings) + end + return siblings +end + +# TODO make dimension agnostic +function possibleneighbors(o::OctantBWG{2},l,b) + neighbors = ntuple(8) do i + if i > 4 + j = i - 4 + face_neighbor(o,j,b) + else + corner_neighbor(o,i,b) + end + end + neighbors = filter(x->inside(x,b),neighbors) + return neighbors +end + +function isancestor(o1,o2,b) + ancestor = false + l = o2.l - 1 + p = parent(o2,b) + while l > 0 + if p == o1 + ancestor = true + break + end + l -= 1 + p = parent(p,b) + end + return ancestor +end + # TODO verify and generalize function contains_face(mface::Tuple{Tuple{T,T},Tuple{T,T}},sface::Tuple{Tuple{T,T},Tuple{T,T}}) where T if mface[1][1] == sface[1][1] && mface[2][1] == sface[2][1] # vertical From 2c88a952636e28c7c9a0bc43c81d536f8c011dff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 10:19:15 +0200 Subject: [PATCH 083/143] Seems like working version of balancing intraoctree, need more tests --- src/Adaptivity/AdaptiveCells.jl | 28 ++++++++++++++-------------- test/test_p4est.jl | 11 +++++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 6f3a40b3fe..0de7bdf19e 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -667,10 +667,11 @@ end # Algorithm 7 of preprint Sundar, Sampath, Biros # https://padas.oden.utexas.edu/static/papers/OctreeBalance21.pdf +# TODO optimise the unnecessary allocations function balancetree(tree::OctreeBWG) W = copy(tree.leaves); P = eltype(tree.leaves)[]; R = eltype(tree.leaves)[] for l in tree.b:-1:1 #TODO verify to do this until level 1 - Q = [o for o in tree.leaves if o.l == l] + Q = [o for o in W if o.l == l] sort!(Q,by=x->morton(x,tree.b,tree.b)) #construct T T = eltype(Q)[] @@ -689,7 +690,7 @@ function balancetree(tree::OctreeBWG) push!(P,possibleneighbors(parent(t,tree.b),l-1,tree.b)...) end append!(P,x for x in W if x.l == l-1) - filter!(x->x.l !== l-1, W) + filter!(x->!(x.l == l-1), W) #don't know why I have to negotiate like this, otherwise behaves weird unique!(P) append!(W,P) empty!(P) @@ -702,13 +703,8 @@ end # Algorithm 8 of preprint Sundar, Sampath, Biros # inverted the algorithm to delete! instead of add incrementally to a new array function linearise!(leaves::Vector{T},b) where T<:OctantBWG - i = 1 - while i <= length(leaves)-1 - if isancestor(leaves[i],leaves[i+1],b) # if i isancestor of i+1 - deleteat!(leaves,i) - end - i += 1 - end + inds = [i for i in 1:length(leaves)-1 if isancestor(leaves[i],leaves[i+1],b)] + deleteat!(leaves,inds) end function siblings(o::OctantBWG,b;include_self=false) @@ -726,22 +722,26 @@ function possibleneighbors(o::OctantBWG{2},l,b) j = i - 4 face_neighbor(o,j,b) else - corner_neighbor(o,i,b) + corner_neighbor(o,i,b) end end neighbors = filter(x->inside(x,b),neighbors) return neighbors end +""" + isancestor(o1,o2,b) -> Bool +Is o2 an ancestor of o1 +""" function isancestor(o1,o2,b) ancestor = false - l = o2.l - 1 - p = parent(o2,b) + l = o1.l - 1 + p = parent(o1,b) while l > 0 - if p == o1 + if p == o2 ancestor = true break - end + end l -= 1 p = parent(p,b) end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 1a8f26d41b..250483922a 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -258,3 +258,14 @@ end @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) end end + +@testset "Balancing" begin + grid = generate_grid(Quadrilateral,(1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + @test length(balanced.leaves) == 16 +end From 72acdeb0bd614c99cfd1ea8cdefbf6a3547257de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 11:13:20 +0200 Subject: [PATCH 084/143] remove bugs and add one more test for balancing --- src/Adaptivity/AdaptiveCells.jl | 10 +++++----- test/test_p4est.jl | 12 ++++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 0de7bdf19e..d36a49b301 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -672,7 +672,7 @@ function balancetree(tree::OctreeBWG) W = copy(tree.leaves); P = eltype(tree.leaves)[]; R = eltype(tree.leaves)[] for l in tree.b:-1:1 #TODO verify to do this until level 1 Q = [o for o in W if o.l == l] - sort!(Q,by=x->morton(x,tree.b,tree.b)) + sort!(Q) #construct T T = eltype(Q)[] for x in Q @@ -695,7 +695,7 @@ function balancetree(tree::OctreeBWG) append!(W,P) empty!(P) end - sort!(R,by=x->morton(x,tree.b,tree.b)) + sort!(R) # be careful with sort, by=morton doesn't work due to ambuigity at max depth level linearise!(R,tree.b) return OctreeBWG(R,tree.b,tree.nodes) end @@ -735,10 +735,10 @@ Is o2 an ancestor of o1 """ function isancestor(o1,o2,b) ancestor = false - l = o1.l - 1 - p = parent(o1,b) + l = o2.l - 1 + p = parent(o2,b) while l > 0 - if p == o2 + if p == o1 ancestor = true break end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 250483922a..f1b302b115 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -268,4 +268,16 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) balanced = Ferrite.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 16 + + adaptive_grid = ForestBWG(grid,5) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[15]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[16]) + balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + @test length(balanced.leaves) == 64 end From 05986ba0a9f80bed1a6aa3cefa7f9187f100b610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 16:16:27 +0200 Subject: [PATCH 085/143] first interoctree balancing sketch, need further tests, first simple test works --- src/Adaptivity/AdaptiveCells.jl | 78 ++++++++++++++++++++++++++++++--- test/test_p4est.jl | 12 ++++- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index d36a49b301..6f7baf40c3 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -273,7 +273,9 @@ struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{RefHypercube{dim}} end function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} - @assert pivot_octant.l + 1 <= octree.b + if !(pivot_octant.l + 1 <= octree.b) + return + end o = one(T) # TODO replace this with recursive search function leave_idx = findfirst(x->x==pivot_octant,octree.leaves) @@ -665,10 +667,71 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim return hnodes end +# Algorithm 17 of BWG Paper +function balanceforest!(forest::ForestBWG{dim}) where dim + _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm + _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + for k in 1:length(forest.cells) + tree = forest.cells[k] + balanced = balancetree(tree) + forest.cells[k] = balanced + root_ = root(dim) + for (o_i, o) in enumerate(forest.cells[k].leaves) + ss = possibleneighbors(o,o.l,tree.b,;insidetree=false) + isinside = inside.(ss,(tree.b,)) + notinsideidx = findall(.! isinside) + if !isempty(notinsideidx) + for s_i in notinsideidx + s = ss[s_i] + if s_i <= 4 #corner neighbor, only true for 2D see possibleneighbors + cc = forest.topology.vertex_vertex_neighbor[k,s_i] + isempty(cc) && continue + cc = cc[1] + o′ = transform_corner(forest,cc,o) + s′ = transform_corner(forest,cc,s) + neighbor_tree = forest.cells[cc[1]] + if s′ ∉ neighbor_tree.leaves && parent(s', neighbor_tree.b) ∉ neighbor_tree.leaves + if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves + refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) + else + refine!(tree,s) + end + end + else # face neighbor, only true for 2D + s_i -= 4 + fc = forest.topology.face_face_neighbor[k,_perm[s_i]] + isempty(fc) && continue + fc = fc[1] + k′, f′ = fc[1], _perminv[fc[2]] + o′ = transform_corner(forest,k′,f′,o) + s′ = transform_corner(forest,k′,f′,s) + neighbor_tree = forest.cells[fc[1]] + if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves + if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves + refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) + else + refine!(tree,o) + end + end + end + end + end + end + end + #for k in 1:length(forest.cells) + # tree = forest.cells[k] + # balanced = balancetree(tree) + # forest.cells[k] = balanced + #end +end + # Algorithm 7 of preprint Sundar, Sampath, Biros # https://padas.oden.utexas.edu/static/papers/OctreeBalance21.pdf # TODO optimise the unnecessary allocations function balancetree(tree::OctreeBWG) + if length(tree.leaves) == 1 + return tree + end W = copy(tree.leaves); P = eltype(tree.leaves)[]; R = eltype(tree.leaves)[] for l in tree.b:-1:1 #TODO verify to do this until level 1 Q = [o for o in W if o.l == l] @@ -716,7 +779,7 @@ function siblings(o::OctantBWG,b;include_self=false) end # TODO make dimension agnostic -function possibleneighbors(o::OctantBWG{2},l,b) +function possibleneighbors(o::OctantBWG{2},l,b;insidetree=true) neighbors = ntuple(8) do i if i > 4 j = i - 4 @@ -725,7 +788,9 @@ function possibleneighbors(o::OctantBWG{2},l,b) corner_neighbor(o,i,b) end end - neighbors = filter(x->inside(x,b),neighbors) + if insidetree + neighbors = filter(x->inside(x,b),neighbors) + end return neighbors end @@ -946,7 +1011,7 @@ function parent(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where l = octant.l - one(T) return OctantBWG(l,octant.xyz .& ~h) else - error("root has no parent") + root(dim) end end @@ -1002,7 +1067,8 @@ end face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) """ - transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T2} + transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T1,T2} + transform_face(forest::ForestBWG, f::FaceIndex, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T2} Interoctree coordinate transformation of an given octant `o` to the octree `k` coordinate system by virtually pushing `o`s coordinate system through `k`'s face `f`. Implements Algorithm 8 of BWG p4est paper. @@ -1057,6 +1123,8 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2 end end +transform_face(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face(forest,f[1],f[2],oct) + """ transform_corner(forest,k,c',oct) transform_corner(forest,v::VertexIndex,oct) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index f1b302b115..addc415cb3 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -43,12 +43,12 @@ end @test Ferrite.child_id(o,b) == 5 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) - @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + @test Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) == Ferrite.root(3) o = Ferrite.OctantBWG(3,2,4,3) @test Ferrite.child_id(o,b) == 4 @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) - @test_throws ErrorException Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) + @test Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) == Ferrite.root(3) @test Ferrite.child_id(Ferrite.OctantBWG(2,1,1,3),3) == 1 @test Ferrite.child_id(Ferrite.OctantBWG(2,1,2,3),3) == 2 @@ -280,4 +280,12 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[16]) balanced = Ferrite.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 64 + + + grid = generate_grid(Quadrilateral,(2,1)) + adaptive_grid = ForestBWG(grid,2) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 11 end From f94d40fad9ec744bdf1294ac79d7de7e603b0bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 16:18:15 +0200 Subject: [PATCH 086/143] first interoctree balancing sketch, need further tests, first simple test works --- src/Adaptivity/AdaptiveCells.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 6f7baf40c3..3718711d30 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -668,6 +668,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim end # Algorithm 17 of BWG Paper +# TODO need further work for dimension agnostic case function balanceforest!(forest::ForestBWG{dim}) where dim _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv From 37e288d9853dbf19f8bca2d6cacc8671f611f550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 18:42:28 +0200 Subject: [PATCH 087/143] more testing; not sure if nc level > 2 needs to be covered interoctree wise; if so current algorithm can't --- src/Adaptivity/AdaptiveCells.jl | 41 +++++++++++++++++++++++---------- test/test_p4est.jl | 7 ++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 3718711d30..d3e14a2aeb 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -670,8 +670,10 @@ end # Algorithm 17 of BWG Paper # TODO need further work for dimension agnostic case function balanceforest!(forest::ForestBWG{dim}) where dim - _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm - _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + perm_face = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm + perm_face_inv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + perm_corner = dim == 2 ? node_map₂ : node_map₃ + perm_corner_inv = dim == 2 ? node_map₂_inv : node_map₃_inv for k in 1:length(forest.cells) tree = forest.cells[k] balanced = balancetree(tree) @@ -685,33 +687,34 @@ function balanceforest!(forest::ForestBWG{dim}) where dim for s_i in notinsideidx s = ss[s_i] if s_i <= 4 #corner neighbor, only true for 2D see possibleneighbors - cc = forest.topology.vertex_vertex_neighbor[k,s_i] + cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] isempty(cc) && continue cc = cc[1] - o′ = transform_corner(forest,cc,o) - s′ = transform_corner(forest,cc,s) + k′, c′ = cc[1], perm_corner_inv[cc[2]] + o′ = transform_corner(forest,k′,c′,o) + s′ = transform_corner(forest,k′,c′,s) neighbor_tree = forest.cells[cc[1]] - if s′ ∉ neighbor_tree.leaves && parent(s', neighbor_tree.b) ∉ neighbor_tree.leaves + if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) - else - refine!(tree,s) + #else + # refine!(tree,o) end end else # face neighbor, only true for 2D s_i -= 4 - fc = forest.topology.face_face_neighbor[k,_perm[s_i]] + fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] isempty(fc) && continue fc = fc[1] - k′, f′ = fc[1], _perminv[fc[2]] + k′, f′ = fc[1], perm_face_inv[fc[2]] o′ = transform_corner(forest,k′,f′,o) s′ = transform_corner(forest,k′,f′,s) neighbor_tree = forest.cells[fc[1]] if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) - else - refine!(tree,o) + #else + # refine!(tree,o) end end end @@ -1397,6 +1400,11 @@ const node_map₂ = [1, 4, 3] +const node_map₂_inv = [1, + 2, + 4, + 3] + const node_map₃ = [1, 2, 4, @@ -1405,3 +1413,12 @@ const node_map₃ = [1, 6, 8, 7] + +const node_map₃_inv = [1, + 2, + 4, + 3, + 5, + 6, + 8, + 7] diff --git a/test/test_p4est.jl b/test/test_p4est.jl index addc415cb3..d5a2731347 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -288,4 +288,11 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) Ferrite.balanceforest!(adaptive_grid) @test Ferrite.getncells(adaptive_grid) == 11 + + grid = generate_grid(Quadrilateral,(2,2)) + adaptive_grid = ForestBWG(grid,2) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 19 end From 9e53661dcef9ea8c0dc547165cb599d0d82add0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 22:29:30 +0200 Subject: [PATCH 088/143] first sketch of adaptive manufactured solution; need to bookkeep a shift for the cellids after previous cellid refined --- src/Adaptivity/AdaptiveCells.jl | 12 ++++++ test/test_p4est_example.jl | 75 ++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index d3e14a2aeb..116a7a0465 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -432,6 +432,18 @@ function refine_all!(forest::ForestBWG,l) end end +function refine!(forest::ForestBWG, cellid::Integer) + nleaves_k = length(forest.cells[1].leaves) + prev_nleaves_k = 0 + k = 1 + while nleaves_k < cellid + k += 1 + prev_nleaves_k = nleaves_k + nleaves_k += length(forest.cells[k].leaves) + end + refine!(forest.cells[k],forest.cells[k].leaves[cellid-prev_nleaves_k]) +end + function coarsen_all!(forest::ForestBWG) for tree in forest.cells for leaf in tree.leaves diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index dbc9a92f00..675b147579 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -50,7 +50,7 @@ function assemble_element!(Ke::Matrix, fe::Vector, cellvalues::CellValues, coord end end end - @show norm(Ke), norm(fe) + #@show norm(Ke), norm(fe) return Ke, fe end @@ -78,10 +78,12 @@ function assemble_global(cellvalues::CellValues, K::SparseMatrixCSC, dh::DofHand end # Check L2 convergence -function check_and_compute_convergence(dh, u, cellvalues, testatol) +function check_and_compute_convergence(dh, u, cellvalues, testatol, forest) L2norm = 0.0 L∞norm = 0.0 - for cell in CellIterator(dh) + for (cellid,cell) in enumerate(CellIterator(dh)) + L2loc = 0.0 + L∞loc = 0.0 reinit!(cellvalues, cell) n_basefuncs = getnbasefunctions(cellvalues) coords = getcoordinates(cell) @@ -93,9 +95,15 @@ function check_and_compute_convergence(dh, u, cellvalues, testatol) uₐₚₚᵣₒₓ = function_value(cellvalues, q_point, uₑ) L2norm += norm(uₐₙₐ-uₐₚₚᵣₒₓ)*dΩ L∞norm = max(L∞norm, norm(uₐₙₐ-uₐₚₚᵣₒₓ)) - @test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=testatol) + L2loc += norm(uₐₙₐ-uₐₚₚᵣₒₓ)*dΩ + L∞loc = max(L∞norm, norm(uₐₙₐ-uₐₚₚᵣₒₓ)) + #@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=testatol) + end + if L2loc > 1e-3 + Ferrite.refine!(forest,cellid) end end + Ferrite.balanceforest!(forest) L2norm, L∞norm end @@ -132,33 +140,32 @@ end end # module ConvergenceTestHelper -@testset "convergence analysis" begin - @testset "$interpolation" for interpolation in ( - Lagrange{RefQuadrilateral, 1}(), - #Lagrange{RefHexahedron, 1}(), - ) - # Generate a grid ... - geometry = ConvergenceTestHelper.get_geometry(interpolation) - interpolation_geo = interpolation - N = ConvergenceTestHelper.get_N(interpolation) - grid = generate_grid(geometry, ntuple(x->3, Ferrite.getdim(geometry))); - adaptive_grid = ForestBWG(grid,4) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine_all!(adaptive_grid,2) - Ferrite.refine_all!(adaptive_grid,3) - Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[8]) - Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[5]) - grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) - # ... a suitable quadrature rule ... - qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) - qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) - # ... and then pray to the gods of convergence. - dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) - u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - #ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2) - vtk_grid("p4est_test.vtu",dh) do vtk - vtk_point_data(vtk,dh,u) - end - end -end +#@testset "convergence analysis" begin +# @testset "$interpolation" for interpolation in ( +# Lagrange{RefQuadrilateral, 1}(), +# #Lagrange{RefHexahedron, 1}(), +# ) +# L2norm = Inf +# # Generate a grid ... +# geometry = ConvergenceTestHelper.get_geometry(interpolation) +# interpolation_geo = interpolation +# N = ConvergenceTestHelper.get_N(interpolation) +# grid = generate_grid(geometry, ntuple(x->3, Ferrite.getdim(geometry))); +# adaptive_grid = ForestBWG(grid,7) +# # ... a suitable quadrature rule ... +# qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) +# qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) +# # ... and then pray to the gods of convergence. +# i = 0 +# while L2norm > 1e-2 +# grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) +# dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) +# u = ConvergenceTestHelper.solve(dh, ch, cellvalues) +# L2norm, _ = ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2, adaptive_grid) +# vtk_grid("p4est_test$(i).vtu",dh) do vtk +# vtk_point_data(vtk,dh,u) +# end +# i += 1 +# end +# end +#end From 21e75569773b5bcaa9b7eeaad189067203b5e79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 8 Aug 2023 22:54:16 +0200 Subject: [PATCH 089/143] manufactured solution 2d adaptivity done --- src/Adaptivity/AdaptiveCells.jl | 10 +++++ test/test_p4est_example.jl | 68 +++++++++++++++++---------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 116a7a0465..a8adacbd86 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -444,6 +444,16 @@ function refine!(forest::ForestBWG, cellid::Integer) refine!(forest.cells[k],forest.cells[k].leaves[cellid-prev_nleaves_k]) end +function refine!(forest::ForestBWG, cellids::Vector{<:Integer}) + ncells = getncells(forest) + shift = 0 + for cellid in cellids + refine!(forest,cellid+shift) + shift += getncells(forest) - ncells + ncells = getncells(forest) + end +end + function coarsen_all!(forest::ForestBWG) for tree in forest.cells for leaf in tree.leaves diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 675b147579..4fda1efa12 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -22,7 +22,7 @@ get_N(::Ferrite.Interpolation{shape, 3}) where {shape} = 8 get_N(::Ferrite.Interpolation{shape, 4}) where {shape} = 5 get_N(::Ferrite.Interpolation{shape, 5}) where {shape} = 3 -analytical_solution(x) = prod(cos, x*π/2) +analytical_solution(x) = cot(norm(x)) analytical_rhs(x) = -sum(diag(ForwardDiff.hessian(analytical_solution,x))) # Standard assembly copy pasta for Poisson problem @@ -81,6 +81,7 @@ end function check_and_compute_convergence(dh, u, cellvalues, testatol, forest) L2norm = 0.0 L∞norm = 0.0 + marked_cells = Int[] for (cellid,cell) in enumerate(CellIterator(dh)) L2loc = 0.0 L∞loc = 0.0 @@ -99,11 +100,12 @@ function check_and_compute_convergence(dh, u, cellvalues, testatol, forest) L∞loc = max(L∞norm, norm(uₐₙₐ-uₐₚₚᵣₒₓ)) #@test isapprox(uₐₙₐ, uₐₚₚᵣₒₓ; atol=testatol) end - if L2loc > 1e-3 - Ferrite.refine!(forest,cellid) + if L2loc > 2e-1 + push!(marked_cells,cellid) end end - Ferrite.balanceforest!(forest) + Ferrite.refine!(forest,marked_cells) + #Ferrite.balanceforest!(forest) L2norm, L∞norm end @@ -140,32 +142,32 @@ end end # module ConvergenceTestHelper -#@testset "convergence analysis" begin -# @testset "$interpolation" for interpolation in ( -# Lagrange{RefQuadrilateral, 1}(), -# #Lagrange{RefHexahedron, 1}(), -# ) -# L2norm = Inf -# # Generate a grid ... -# geometry = ConvergenceTestHelper.get_geometry(interpolation) -# interpolation_geo = interpolation -# N = ConvergenceTestHelper.get_N(interpolation) -# grid = generate_grid(geometry, ntuple(x->3, Ferrite.getdim(geometry))); -# adaptive_grid = ForestBWG(grid,7) -# # ... a suitable quadrature rule ... -# qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) -# qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) -# # ... and then pray to the gods of convergence. -# i = 0 -# while L2norm > 1e-2 -# grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) -# dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) -# u = ConvergenceTestHelper.solve(dh, ch, cellvalues) -# L2norm, _ = ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2, adaptive_grid) -# vtk_grid("p4est_test$(i).vtu",dh) do vtk -# vtk_point_data(vtk,dh,u) -# end -# i += 1 -# end -# end -#end +@testset "convergence analysis" begin + @testset "$interpolation" for interpolation in ( + Lagrange{RefQuadrilateral, 1}(), + #Lagrange{RefHexahedron, 1}(), + ) + L2norm = Inf + # Generate a grid ... + geometry = ConvergenceTestHelper.get_geometry(interpolation) + interpolation_geo = interpolation + N = ConvergenceTestHelper.get_N(interpolation) + grid = generate_grid(geometry, ntuple(x->10, Ferrite.getdim(geometry))); + adaptive_grid = ForestBWG(grid,7) + # ... a suitable quadrature rule ... + qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) + qr = QuadratureRule{Ferrite.getrefshape(interpolation)}(qr_order) + # ... and then pray to the gods of convergence. + i = 0 + while L2norm > 1e-3 && i < 8 + grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) + dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) + u = ConvergenceTestHelper.solve(dh, ch, cellvalues) + L2norm, _ = ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2, adaptive_grid) + vtk_grid("p4est_test$(i).vtu",dh) do vtk + vtk_point_data(vtk,dh,u) + end + i += 1 + end + end +end From fe5a6ad2bd78722f46579d08a54bde1680b69a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Oct 2023 15:34:01 +0200 Subject: [PATCH 090/143] started elasticity adaptivity example --- docs/src/literate-tutorials/adaptivity.jl | 162 ++++++++++++++++++ docs/src/literate-tutorials/l.geo | 50 ++++++ docs/src/literate-tutorials/l.msh | 71 ++++++++ .../literate-tutorials/linear_elasticity.jl | 3 - 4 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 docs/src/literate-tutorials/adaptivity.jl create mode 100644 docs/src/literate-tutorials/l.geo create mode 100644 docs/src/literate-tutorials/l.msh delete mode 100644 docs/src/literate-tutorials/linear_elasticity.jl diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl new file mode 100644 index 0000000000..2c57378973 --- /dev/null +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -0,0 +1,162 @@ +using Ferrite, FerriteGmsh, SparseArrays +grid = togrid("l.msh"); +grid = ForestBWG(grid,7) + +struct Elasticity + G::Float64 + K::Float64 +end + +function material_routine(material::Elasticity, ε::SymmetricTensor{2}) + (; G, K) = material + stress(ε) = 2G * dev(ε) + K * tr(ε) * one(ε) + ∂σ∂ε, σ = gradient(stress, ε, :all) + return σ, ∂σ∂ε +end + +E = 200e3 # Young's modulus [MPa] +ν = 0.3 # Poisson's ratio [-] +material = Elasticity(E/2(1+ν), E/3(1-2ν)); + +function assemble_cell!(ke, fe, cellvalues, material, ue) + fill!(ke, 0.0) + fill!(fe, 0.0) + + n_basefuncs = getnbasefunctions(cellvalues) + for q_point in 1:getnquadpoints(cellvalues) + ## For each integration point, compute strain, stress and material stiffness + ε = function_symmetric_gradient(cellvalues, q_point, ue) + σ, ∂σ∂ε = material_routine(material, ε) + + dΩ = getdetJdV(cellvalues, q_point) + for i in 1:n_basefuncs + ∇Nᵢ = shape_gradient(cellvalues, q_point, i)# shape_symmetric_gradient(cellvalues, q_point, i) + fe[i] += σ ⊡ ∇Nᵢ * dΩ # add internal force to residual + for j in 1:n_basefuncs + ∇ˢʸᵐNⱼ = shape_symmetric_gradient(cellvalues, q_point, j) + ke[i, j] += (∂σ∂ε ⊡ ∇ˢʸᵐNⱼ) ⊡ ∇Nᵢ * dΩ + end + end + end +end + +function assemble_global!(K, f, a, dh, cellvalues, material) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cells + for cell in CellIterator(dh) + reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + @views ue = a[celldofs(cell)] + ## Compute element contribution + assemble_cell!(ke, fe, cellvalues, material, ue) + ## Assemble ke and fe into K and f + assemble!(assembler, celldofs(cell), ke, fe) + end + return K, f +end + +function solve(grid,hnodes) + dim = 2 + order = 1 # linear interpolation + ip = Lagrange{RefQuadrilateral, order}()^dim # vector valued interpolation + qr = QuadratureRule{RefQuadrilateral}(2) # 1 quadrature point + cellvalues = CellValues(qr, ip); + + dh = DofHandler(grid) + add!(dh, :u, ip) + dh, vdict, edict, fdict = Ferrite.__close!(dh); + + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) + add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.05, 2)) + for (hdof,mdof) in hnodes + @show vdict[1][hdof], vdict[1][mdof[1]], vdict[1][mdof[2]] + lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) + add!(ch,lc) + end + close!(ch); + + K = create_sparsity_pattern(dh,ch) + f = zeros(ndofs(dh)) + a = zeros(ndofs(dh)) + assemble_global!(K, f, a, dh, cellvalues, material); + apply!(K, f, ch) + u = K \ f; + return u,dh,ch,cellvalues +end + +function compute_fluxes(u,dh) + ip = Lagrange{RefQuadrilateral, 1}()^2 + qr = QuadratureRule{RefQuadrilateral}(1) + cellvalues_sc = CellValues(qr, ip); + qr = QuadratureRule{RefQuadrilateral}(2) + cellvalues = CellValues(qr, ip); + σ_gp_sc = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() + σ_gp_sc_loc = Vector{SymmetricTensor{2,2,Float64,3}}() + σ_gp = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() + σ_gp_loc = Vector{SymmetricTensor{2,2,Float64,3}}() + for (cellid,cell) in enumerate(CellIterator(dh)) + reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + @views ue = u[celldofs(cell)] + for q_point in 1:getnquadpoints(cellvalues) + ε = function_symmetric_gradient(cellvalues, q_point, ue) + σ, ∂σ∂ε = material_routine(material, ε) + push!(σ_gp_loc, σ == NaN ? 1e6 : σ) + end + for q_point in 1:getnquadpoints(cellvalues_sc) + ε = function_symmetric_gradient(cellvalues, q_point, ue) + σ, ∂σ∂ε = material_routine(material, ε) + push!(σ_gp_sc_loc, σ == NaN ? 1e6 : σ) + end + push!(σ_gp,copy(σ_gp_loc)) + push!(σ_gp_sc,copy(σ_gp_sc_loc)) + empty!(σ_gp_loc) + empty!(σ_gp_sc_loc) + end + return σ_gp, σ_gp_sc +end + +function solve_adaptive(initial_grid) + ip = Lagrange{RefQuadrilateral, 1}() + qr = QuadratureRule{RefQuadrilateral}(1) + cellvalues_tensorial = CellValues(qr, ip); + finished = false + i = 1 + grid = initial_grid + while !finished && i<=3 + transfered_grid, hnodes = Ferrite.creategrid(grid) + u,dh,ch,cv = solve(transfered_grid,hnodes) + σ_gp, σ_gp_sc = compute_fluxes(u,dh) + projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid) + σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) + vtk_grid("linear_elasticity-$i", dh) do vtk + vtk_point_data(vtk, dh, u) + vtk_point_data(vtk, projector, σ_dof, "stress") + end + cells_to_refine = Int[] + for (cellid,cell) in enumerate(CellIterator(projector.dh)) + reinit!(cellvalues, cell) + @views σe = σ_dof[celldofs(cell)] + error = 0.0 + for q_point in 1:getnquadpoints(cellvalues_tensorial) + σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) + error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc)/σ_dof_at_sc) + end + if error > 1.0 + push!(cells_to_refine,cellid) + end + end + Ferrite.refine!(grid,cells_to_refine) + Ferrite.balanceforest!(grid) + i += 1 + if isempty(cells_to_refine) + finished = true + end + end +end + +solve_adaptive(grid) diff --git a/docs/src/literate-tutorials/l.geo b/docs/src/literate-tutorials/l.geo new file mode 100644 index 0000000000..b0d2bbbca1 --- /dev/null +++ b/docs/src/literate-tutorials/l.geo @@ -0,0 +1,50 @@ +//+ +Point(1) = {0, 0, 0, 1.0}; +//+ +Point(2) = {1, 0, 0, 1.0}; +//+ +Point(3) = {1, 0.5, 0, 1.0}; +//+ +Point(4) = {0.5, 0.5, 0, 1.0}; +//+ +Point(5) = {0.5, 1, 0, 1.0}; +//+ +Point(6) = {0, 1, 0, 1.0}; +//+ +Line(1) = {1, 2}; +//+ +Line(2) = {2, 3}; +//+ +Line(3) = {3, 4}; +//+ +Line(4) = {4, 5}; +//+ +Line(5) = {5, 6}; +//+ +Line(6) = {6, 1}; +//+ +Line(7) = {1, 4}; +//+ +Curve Loop(1) = {1, 2, 3, 4, 5, 6}; +//+ +Curve Loop(2) = {3, -7, 1, 2}; +//+ +Plane Surface(1) = {2}; +//+ +Curve Loop(3) = {4, 5, 6, 7}; +//+ +Plane Surface(2) = {3}; +//+ +Transfinite Surface {1}; +//+ +Transfinite Surface {2}; +//+ +Physical Curve("top", 8) = {5}; +//+ +Physical Curve("left", 9) = {6}; +//+ +Physical Curve("bottom", 10) = {1}; +//+ +Physical Curve("right", 11) = {2}; +//+ +Physical Surface("domain", 12) = {2, 1}; diff --git a/docs/src/literate-tutorials/l.msh b/docs/src/literate-tutorials/l.msh new file mode 100644 index 0000000000..a6f57b3494 --- /dev/null +++ b/docs/src/literate-tutorials/l.msh @@ -0,0 +1,71 @@ +$MeshFormat +4.1 0 8 +$EndMeshFormat +$PhysicalNames +5 +1 8 "top" +1 9 "left" +1 10 "bottom" +1 11 "right" +2 12 "domain" +$EndPhysicalNames +$Entities +6 7 2 0 +1 0 0 0 0 +2 1 0 0 0 +3 1 0.5 0 0 +4 0.5 0.5 0 0 +5 0.5 1 0 0 +6 0 1 0 0 +1 0 0 0 1 0 0 1 10 2 1 -2 +2 1 0 0 1 0.5 0 1 11 2 2 -3 +3 0.5 0.5 0 1 0.5 0 0 2 3 -4 +4 0.5 0.5 0 0.5 1 0 0 2 4 -5 +5 0 1 0 0.5 1 0 1 8 2 5 -6 +6 0 0 0 0 1 0 1 9 2 6 -1 +7 0 0 0 0.5 0.5 0 0 2 1 -4 +1 0 0 0 1 0.5 0 1 12 4 3 -7 1 2 +2 0 0 0 0.5 1 0 1 12 4 4 5 6 7 +$EndEntities +$Nodes +12 6 1 6 +0 1 0 1 +1 +0 0 0 +0 2 0 1 +2 +1 0 0 +0 3 0 1 +3 +1 0.5 0 +0 4 0 1 +4 +0.5 0.5 0 +0 5 0 1 +5 +0.5 1 0 +0 6 0 1 +6 +0 1 0 +1 1 0 0 +1 2 0 0 +1 5 0 0 +1 6 0 0 +2 1 0 0 +2 2 0 0 +$EndNodes +$Elements +6 6 1 6 +1 1 1 1 +1 1 2 +1 2 1 1 +2 2 3 +1 5 1 1 +3 5 6 +1 6 1 1 +4 6 1 +2 1 3 1 +5 3 4 1 2 +2 2 3 1 +6 4 5 6 1 +$EndElements diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl deleted file mode 100644 index 3381be54a6..0000000000 --- a/docs/src/literate-tutorials/linear_elasticity.jl +++ /dev/null @@ -1,3 +0,0 @@ -# # Linear elasticity -# -# TBW From 864677e2a0b56ecaf85e0c488a6317b2b09f4011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 4 Oct 2023 19:06:19 +0200 Subject: [PATCH 091/143] add application of affine constraints --- docs/src/literate-tutorials/adaptivity.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 2c57378973..f9283f6f29 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,6 +1,6 @@ using Ferrite, FerriteGmsh, SparseArrays grid = togrid("l.msh"); -grid = ForestBWG(grid,7) +grid = ForestBWG(grid,5) struct Elasticity G::Float64 @@ -74,7 +74,6 @@ function solve(grid,hnodes) add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.05, 2)) for (hdof,mdof) in hnodes - @show vdict[1][hdof], vdict[1][mdof[1]], vdict[1][mdof[2]] lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) add!(ch,lc) end @@ -86,11 +85,12 @@ function solve(grid,hnodes) assemble_global!(K, f, a, dh, cellvalues, material); apply!(K, f, ch) u = K \ f; + apply!(u,ch) return u,dh,ch,cellvalues end function compute_fluxes(u,dh) - ip = Lagrange{RefQuadrilateral, 1}()^2 + ip = Lagrange{RefQuadrilateral, 1}()^2 qr = QuadratureRule{RefQuadrilateral}(1) cellvalues_sc = CellValues(qr, ip); qr = QuadratureRule{RefQuadrilateral}(2) @@ -105,12 +105,12 @@ function compute_fluxes(u,dh) for q_point in 1:getnquadpoints(cellvalues) ε = function_symmetric_gradient(cellvalues, q_point, ue) σ, ∂σ∂ε = material_routine(material, ε) - push!(σ_gp_loc, σ == NaN ? 1e6 : σ) + push!(σ_gp_loc, σ) end for q_point in 1:getnquadpoints(cellvalues_sc) ε = function_symmetric_gradient(cellvalues, q_point, ue) σ, ∂σ∂ε = material_routine(material, ε) - push!(σ_gp_sc_loc, σ == NaN ? 1e6 : σ) + push!(σ_gp_sc_loc, σ) end push!(σ_gp,copy(σ_gp_loc)) push!(σ_gp_sc,copy(σ_gp_sc_loc)) @@ -127,7 +127,7 @@ function solve_adaptive(initial_grid) finished = false i = 1 grid = initial_grid - while !finished && i<=3 + while !finished && i<=8 transfered_grid, hnodes = Ferrite.creategrid(grid) u,dh,ch,cv = solve(transfered_grid,hnodes) σ_gp, σ_gp_sc = compute_fluxes(u,dh) @@ -139,13 +139,13 @@ function solve_adaptive(initial_grid) end cells_to_refine = Int[] for (cellid,cell) in enumerate(CellIterator(projector.dh)) - reinit!(cellvalues, cell) + reinit!(cellvalues_tensorial, cell) @views σe = σ_dof[celldofs(cell)] error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc)/σ_dof_at_sc) - end + end if error > 1.0 push!(cells_to_refine,cellid) end From cd24959c6500b74fc5d39608fa7c2b973f85fb73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 9 Oct 2023 17:56:02 +0200 Subject: [PATCH 092/143] the master of copy-pasta striked again; fixed balancing bug --- src/Adaptivity/AdaptiveCells.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index a8adacbd86..f182bae78b 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -729,8 +729,8 @@ function balanceforest!(forest::ForestBWG{dim}) where dim isempty(fc) && continue fc = fc[1] k′, f′ = fc[1], perm_face_inv[fc[2]] - o′ = transform_corner(forest,k′,f′,o) - s′ = transform_corner(forest,k′,f′,s) + o′ = transform_face(forest,k′,f′,o) + s′ = transform_face(forest,k′,f′,s) neighbor_tree = forest.cells[fc[1]] if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves From 5dcbcaed3b8ddd43beaceb4c83c421477ac1f2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 9 Oct 2023 17:56:52 +0200 Subject: [PATCH 093/143] first somewhat okayish looking elasticity --- docs/src/literate-tutorials/adaptivity.jl | 32 ++++++++++++++--------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index f9283f6f29..736bc7f6c8 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,6 +1,6 @@ using Ferrite, FerriteGmsh, SparseArrays grid = togrid("l.msh"); -grid = ForestBWG(grid,5) +grid = ForestBWG(grid,10) struct Elasticity G::Float64 @@ -72,7 +72,7 @@ function solve(grid,hnodes) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) - add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.05, 2)) + add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.01, 2)) for (hdof,mdof) in hnodes lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) add!(ch,lc) @@ -86,7 +86,7 @@ function solve(grid,hnodes) apply!(K, f, ch) u = K \ f; apply!(u,ch) - return u,dh,ch,cellvalues + return u,dh,ch,cellvalues,vdict end function compute_fluxes(u,dh) @@ -127,31 +127,39 @@ function solve_adaptive(initial_grid) finished = false i = 1 grid = initial_grid - while !finished && i<=8 + while !finished && i<=20 transfered_grid, hnodes = Ferrite.creategrid(grid) - u,dh,ch,cv = solve(transfered_grid,hnodes) + u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) σ_gp, σ_gp_sc = compute_fluxes(u,dh) - projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid) + projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid; hnodes=hnodes) σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) - vtk_grid("linear_elasticity-$i", dh) do vtk - vtk_point_data(vtk, dh, u) - vtk_point_data(vtk, projector, σ_dof, "stress") - end cells_to_refine = Int[] + error_arr = Float64[] for (cellid,cell) in enumerate(CellIterator(projector.dh)) reinit!(cellvalues_tensorial, cell) @views σe = σ_dof[celldofs(cell)] error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) - error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc)/σ_dof_at_sc) + #error += norm((σ_dof_at_sc - σ_gp_sc[cellid][1])/σ_gp_sc[cellid][1]) + error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) + error *= getdetJdV(cellvalues_tensorial,q_point) end - if error > 1.0 + if error > 0.01 push!(cells_to_refine,cellid) end + push!(error_arr,error) + end + vtk_grid("linear_elasticity-$i", dh) do vtk + vtk_point_data(vtk, dh, u) + vtk_point_data(vtk, projector, σ_dof, "stress") + vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") + vtk_cell_data(vtk, error_arr, "error") end Ferrite.refine!(grid,cells_to_refine) + transfered_grid, hnodes = Ferrite.creategrid(grid) Ferrite.balanceforest!(grid) + transfered_grid, hnodes = Ferrite.creategrid(grid) i += 1 if isempty(cells_to_refine) finished = true From f802c696ee3b1103cddfd5c2820e4b828ec75af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 10 Oct 2023 14:21:32 +0200 Subject: [PATCH 094/143] non-working L2 Projector --- src/L2_projection.jl | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/src/L2_projection.jl b/src/L2_projection.jl index d9bc37c939..45518e73c7 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -6,6 +6,7 @@ struct L2Projector <: AbstractProjector geom_ip::Interpolation M_cholesky #::SuiteSparse.CHOLMOD.Factor{Float64} dh::DofHandler + ch::ConstraintHandler set::Vector{Int} end @@ -38,6 +39,7 @@ function L2Projector( qr_lhs::QuadratureRule = _mass_qr(func_ip), set = 1:getncells(grid), geom_ip::Interpolation = default_interpolation(getcelltype(grid, first(set))), + hnodes=nothing, ) # TODO: Maybe this should not be allowed? We always assume to project scalar entries. @@ -53,12 +55,20 @@ function L2Projector( dh = DofHandler(grid) sdh = SubDofHandler(dh, Set(set)) add!(sdh, :_, func_ip) # we need to create the field, but the interpolation is not used here - close!(dh) + dh, vdict, _ = __close!(dh) - M = _assemble_L2_matrix(fe_values_mass, set, dh) # the "mass" matrix - M_cholesky = cholesky(M) + ch_hanging = ConstraintHandler(dh) + if hnodes !== nothing + for (hdof,mdof) in hnodes + lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) + add!(ch_hanging,lc) + end + end + close!(ch_hanging); + M = _assemble_L2_matrix(fe_values_mass, set, dh, ch_hanging) # the "mass" matrix + #apply!(M,ch_hanging) - return L2Projector(func_ip, geom_ip, M_cholesky, dh, collect(set)) + return L2Projector(func_ip, geom_ip, M, dh, ch_hanging, collect(set)) end # Quadrature sufficient for integrating a mass matrix @@ -77,10 +87,10 @@ function Base.show(io::IO, ::MIME"text/plain", proj::L2Projector) println(io, " geometric interpolation: ", proj.geom_ip) end -function _assemble_L2_matrix(fe_values, set, dh) +function _assemble_L2_matrix(fe_values, set, dh, ch) n = Ferrite.getnbasefunctions(fe_values) - M = create_symmetric_sparsity_pattern(dh) + M = create_sparsity_pattern(dh, ch) assembler = start_assemble(M) Me = zeros(n, n) @@ -189,6 +199,7 @@ function _project(vars, proj::L2Projector, fe_values::AbstractValues, M::Integer get_data(x::AbstractTensor, i) = x.data[i] get_data(x::Number, i) = x + ch = proj.ch ## Assemble contributions from each cell for (ic,cellnum) in enumerate(proj.set) @@ -215,9 +226,20 @@ function _project(vars, proj::L2Projector, fe_values::AbstractValues, M::Integer end end + #_copyM = copy(proj.M_cholesky) + #apply!(proj.M_cholesky,f[:,1],ch) + #apply!(_copyM,f[:,2],ch) + #apply!(_copyM,f[:,3],ch) + apply!(proj.M_cholesky,ch) + for col in eachcol(f) + apply_zero!(col,ch) + end # solve for the projected nodal values projected_vals = proj.M_cholesky \ f + for col in eachcol(projected_vals) + apply!(col,ch) + end # Recast to original input type make_T(vals) = T <: AbstractTensor ? T(Tuple(vals)) : vals[1] return T[make_T(x) for x in eachrow(projected_vals)] From df1f6ea21112ea674f7f02440b6df57f043dd203 Mon Sep 17 00:00:00 2001 From: termi-official Date: Tue, 10 Oct 2023 16:41:05 +0200 Subject: [PATCH 095/143] Add heat example and first hotfix for elasticity example. --- docs/src/literate-tutorials/adaptivity.jl | 20 ++- .../src/literate-tutorials/heat_adaptivity.jl | 167 ++++++++++++++++++ 2 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 docs/src/literate-tutorials/heat_adaptivity.jl diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 736bc7f6c8..1b1bb6043b 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -91,29 +91,34 @@ end function compute_fluxes(u,dh) ip = Lagrange{RefQuadrilateral, 1}()^2 + # Superconvergent points qr = QuadratureRule{RefQuadrilateral}(1) cellvalues_sc = CellValues(qr, ip); + # "Normal" quadrature points for the fluxes qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); + # Buffers σ_gp_sc = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() σ_gp_sc_loc = Vector{SymmetricTensor{2,2,Float64,3}}() σ_gp = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() σ_gp_loc = Vector{SymmetricTensor{2,2,Float64,3}}() for (cellid,cell) in enumerate(CellIterator(dh)) reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + reinit!(cellvalues_sc, cell) @views ue = u[celldofs(cell)] for q_point in 1:getnquadpoints(cellvalues) ε = function_symmetric_gradient(cellvalues, q_point, ue) - σ, ∂σ∂ε = material_routine(material, ε) + σ, _ = material_routine(material, ε) push!(σ_gp_loc, σ) end for q_point in 1:getnquadpoints(cellvalues_sc) - ε = function_symmetric_gradient(cellvalues, q_point, ue) - σ, ∂σ∂ε = material_routine(material, ε) + ε = function_symmetric_gradient(cellvalues_sc, q_point, ue) + σ, _ = material_routine(material, ε) push!(σ_gp_sc_loc, σ) end push!(σ_gp,copy(σ_gp_loc)) push!(σ_gp_sc,copy(σ_gp_sc_loc)) + # Reset buffer for local points empty!(σ_gp_loc) empty!(σ_gp_sc_loc) end @@ -126,8 +131,10 @@ function solve_adaptive(initial_grid) cellvalues_tensorial = CellValues(qr, ip); finished = false i = 1 - grid = initial_grid + grid = deepcopy(initial_grid) + pvd = paraview_collection("elasticity_amr.pvd"); while !finished && i<=20 + @show i transfered_grid, hnodes = Ferrite.creategrid(grid) u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) σ_gp, σ_gp_sc = compute_fluxes(u,dh) @@ -141,8 +148,7 @@ function solve_adaptive(initial_grid) error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) - #error += norm((σ_dof_at_sc - σ_gp_sc[cellid][1])/σ_gp_sc[cellid][1]) - error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) + error += norm((σ_gp_sc[cellid][q_point] - σ_dof_at_sc )) error *= getdetJdV(cellvalues_tensorial,q_point) end if error > 0.01 @@ -155,6 +161,7 @@ function solve_adaptive(initial_grid) vtk_point_data(vtk, projector, σ_dof, "stress") vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") vtk_cell_data(vtk, error_arr, "error") + pvd[i] = vtk end Ferrite.refine!(grid,cells_to_refine) transfered_grid, hnodes = Ferrite.creategrid(grid) @@ -165,6 +172,7 @@ function solve_adaptive(initial_grid) finished = true end end + vtk_save(pvd); end solve_adaptive(grid) diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl new file mode 100644 index 0000000000..ac01934c74 --- /dev/null +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -0,0 +1,167 @@ +using Ferrite, FerriteGmsh, SparseArrays +grid = generate_grid(Quadrilateral, (4,4)); +grid = ForestBWG(grid,10) + +analytical_solution(x) = prod(cos, x*π/2*10) +analytical_rhs(x) = -laplace(analytical_solution,x) + +function assemble_cell!(ke, fe, cellvalues, ue, coords) + fill!(ke, 0.0) + fill!(fe, 0.0) + + n_basefuncs = getnbasefunctions(cellvalues) + for q_point in 1:getnquadpoints(cellvalues) + x = spatial_coordinate(cellvalues, q_point, coords) + dΩ = getdetJdV(cellvalues, q_point) + for i in 1:n_basefuncs + Nᵢ = shape_value(cellvalues, q_point, i) + ∇Nᵢ = shape_gradient(cellvalues, q_point, i)# shape_symmetric_gradient(cellvalues, q_point, i) + fe[i] += analytical_rhs(x) * Nᵢ * dΩ # add internal force to residual + for j in 1:n_basefuncs + ∇Nⱼ = shape_gradient(cellvalues, q_point, j) + ke[i, j] += ∇Nⱼ ⋅ ∇Nᵢ * dΩ + end + end + end +end + +function assemble_global!(K, f, a, dh, cellvalues) + ## Allocate the element stiffness matrix and element force vector + n_basefuncs = getnbasefunctions(cellvalues) + ke = zeros(n_basefuncs, n_basefuncs) + fe = zeros(n_basefuncs) + ## Create an assembler + assembler = start_assemble(K, f) + ## Loop over all cells + for cell in CellIterator(dh) + reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + @views ue = a[celldofs(cell)] + ## Compute element contribution + coords = getcoordinates(cell) + assemble_cell!(ke, fe, cellvalues, ue, coords) + ## Assemble ke and fe into K and f + assemble!(assembler, celldofs(cell), ke, fe) + end + return K, f +end + +function solve(grid, hnodes) + dim = 2 + order = 1 # linear interpolation + ip = Lagrange{RefQuadrilateral, order}() # vector valued interpolation + qr = QuadratureRule{RefQuadrilateral}(2) # 1 quadrature point + cellvalues = CellValues(qr, ip); + + dh = DofHandler(grid) + add!(dh, :u, ip) + dh, vdict, edict, fdict = Ferrite.__close!(dh); + + ch = ConstraintHandler(dh) + add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfaceset(grid, "left"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfaceset(grid, "bottom"), (x, t) -> 0.0)) + for (hdof,mdof) in hnodes + lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) + add!(ch,lc) + end + close!(ch); + + K = create_sparsity_pattern(dh,ch) + f = zeros(ndofs(dh)) + a = zeros(ndofs(dh)) + assemble_global!(K, f, a, dh, cellvalues); + apply!(K, f, ch) + u = K \ f; + apply!(u,ch) + return u,dh,ch,cellvalues,vdict +end + +function compute_fluxes(u,dh) + ip = Lagrange{RefQuadrilateral, 1}() + # Normal quadrature points + qr = QuadratureRule{RefQuadrilateral}(2) + cellvalues = CellValues(qr, ip); + # Superconvergent point + qr_sc = QuadratureRule{RefQuadrilateral}(1) + cellvalues_sc = CellValues(qr_sc, ip); + #Buffers + σ_gp = Vector{Vector{Vec{2,Float64}}}() + σ_gp_loc = Vector{Vec{2,Float64}}() + σ_gp_sc = Vector{Vector{Vec{2,Float64}}}() + σ_gp_sc_loc = Vector{Vec{2,Float64}}() + for (cellid,cell) in enumerate(CellIterator(dh)) + @views ue = u[celldofs(cell)] + + reinit!(cellvalues, cell) + for q_point in 1:getnquadpoints(cellvalues) + gradu = function_gradient(cellvalues, q_point, ue) + push!(σ_gp_loc, gradu) + end + push!(σ_gp,copy(σ_gp_loc)) + empty!(σ_gp_loc) + + reinit!(cellvalues_sc, cell) + for q_point in 1:getnquadpoints(cellvalues_sc) + gradu = function_gradient(cellvalues_sc, q_point, ue) + push!(σ_gp_sc_loc, gradu) + end + push!(σ_gp_sc,copy(σ_gp_sc_loc)) + empty!(σ_gp_sc_loc) + end + return σ_gp, σ_gp_sc +end + +function solve_adaptive(initial_grid) + ip = Lagrange{RefQuadrilateral, 1}()^2 + qr_sc = QuadratureRule{RefQuadrilateral}(1) + cellvalues_flux = CellValues(qr_sc, ip); + finished = false + i = 1 + grid = deepcopy(initial_grid) + pvd = paraview_collection("heat_amr.pvd"); + while !finished && i<=10 + @show i + transfered_grid, hnodes = Ferrite.creategrid(grid) + u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) + σ_gp, σ_gp_sc = compute_fluxes(u,dh) + projector = L2Projector(Lagrange{RefQuadrilateral, 1}(), transfered_grid; hnodes=hnodes) + σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) + cells_to_refine = Int[] + error_arr = Float64[] + for (cellid,cell) in enumerate(CellIterator(projector.dh)) + reinit!(cellvalues_flux, cell) + @views σe = σ_dof[celldofs(cell)] + error = 0.0 + for q_point in 1:getnquadpoints(cellvalues_flux) + σ_dof_at_sc = function_value(cellvalues_flux, q_point, σe) + error += norm((σ_gp_sc[cellid][q_point] - σ_dof_at_sc )) + error *= getdetJdV(cellvalues_flux,q_point) + end + if error > 0.001 + push!(cells_to_refine,cellid) + end + push!(error_arr,error) + end + + vtk_grid("heat_amr-iteration_$i", dh) do vtk + vtk_point_data(vtk, dh, u) + vtk_point_data(vtk, projector, σ_dof, "flux") + vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "flux sc x") + vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),2), "flux sc y") + vtk_cell_data(vtk, error_arr, "error") + pvd[i] = vtk + end + Ferrite.refine!(grid,cells_to_refine) + transfered_grid, hnodes = Ferrite.creategrid(grid) + Ferrite.balanceforest!(grid) + transfered_grid, hnodes = Ferrite.creategrid(grid) + i += 1 + if isempty(cells_to_refine) + finished = true + end + end + vtk_save(pvd); +end + +solve_adaptive(grid) From 2bf3a4e990ac7ac591f5e0a76ab9598f50d207ed Mon Sep 17 00:00:00 2001 From: termi-official Date: Tue, 10 Oct 2023 16:41:26 +0200 Subject: [PATCH 096/143] Hotfix L2 projection with affine constraints. --- src/L2_projection.jl | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/L2_projection.jl b/src/L2_projection.jl index 45518e73c7..c7427bf7ca 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -225,21 +225,17 @@ function _project(vars, proj::L2Projector, fe_values::AbstractValues, M::Integer f[dof, :] += fe[num, :] end end - - #_copyM = copy(proj.M_cholesky) - #apply!(proj.M_cholesky,f[:,1],ch) - #apply!(_copyM,f[:,2],ch) - #apply!(_copyM,f[:,3],ch) - apply!(proj.M_cholesky,ch) - for col in eachcol(f) - apply_zero!(col,ch) + + # Correctly apply affine constraints + projected_vals = similar(f) + for (i,col) in enumerate(eachcol(f)) + _M = deepcopy(proj.M_cholesky) + apply!(_M, col, ch) + u = _M \ col + apply!(u, ch) + projected_vals[:,i] = u end - # solve for the projected nodal values - projected_vals = proj.M_cholesky \ f - for col in eachcol(projected_vals) - apply!(col,ch) - end # Recast to original input type make_T(vals) = T <: AbstractTensor ? T(Tuple(vals)) : vals[1] return T[make_T(x) for x in eachrow(projected_vals)] From 70fffdfd1ad97d3142dc9ed8f9310174fdaaba76 Mon Sep 17 00:00:00 2001 From: termi-official Date: Tue, 10 Oct 2023 16:49:46 +0200 Subject: [PATCH 097/143] Change solution for heat to something more local. AMR follows the solution. --- docs/src/literate-tutorials/heat_adaptivity.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index ac01934c74..254dd9519f 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -2,7 +2,7 @@ using Ferrite, FerriteGmsh, SparseArrays grid = generate_grid(Quadrilateral, (4,4)); grid = ForestBWG(grid,10) -analytical_solution(x) = prod(cos, x*π/2*10) +analytical_solution(x) = atan(2*(norm(x)-0.5)/0.02) analytical_rhs(x) = -laplace(analytical_solution,x) function assemble_cell!(ke, fe, cellvalues, ue, coords) From bdcbb7b3389d1ccfaff68881988934c4943c7e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 10 Oct 2023 17:27:14 +0200 Subject: [PATCH 098/143] make hotfix nicer --- docs/src/literate-tutorials/adaptivity.jl | 22 +++++----- src/Adaptivity/AdaptiveCells.jl | 49 ++++++++++++++--------- src/L2_projection.jl | 9 +++-- test/test_p4est_example.jl | 27 +++++++++++++ 4 files changed, 73 insertions(+), 34 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 1b1bb6043b..7841357463 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,6 +1,8 @@ using Ferrite, FerriteGmsh, SparseArrays grid = togrid("l.msh"); -grid = ForestBWG(grid,10) +grid = ForestBWG(grid,25) +Ferrite.refine_all!(grid,1) +Ferrite.refine_all!(grid,2) struct Elasticity G::Float64 @@ -15,7 +17,7 @@ function material_routine(material::Elasticity, ε::SymmetricTensor{2}) end E = 200e3 # Young's modulus [MPa] -ν = 0.3 # Poisson's ratio [-] +ν = 0.2 # Poisson's ratio [-] material = Elasticity(E/2(1+ν), E/3(1-2ν)); function assemble_cell!(ke, fe, cellvalues, material, ue) @@ -131,10 +133,8 @@ function solve_adaptive(initial_grid) cellvalues_tensorial = CellValues(qr, ip); finished = false i = 1 - grid = deepcopy(initial_grid) - pvd = paraview_collection("elasticity_amr.pvd"); - while !finished && i<=20 - @show i + grid = initial_grid + while !finished transfered_grid, hnodes = Ferrite.creategrid(grid) u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) σ_gp, σ_gp_sc = compute_fluxes(u,dh) @@ -148,8 +148,8 @@ function solve_adaptive(initial_grid) error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) - error += norm((σ_gp_sc[cellid][q_point] - σ_dof_at_sc )) - error *= getdetJdV(cellvalues_tensorial,q_point) + #error += norm((σ_dof_at_sc - σ_gp_sc[cellid][1])/σ_gp_sc[cellid][1]) + error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) * getdetJdV(cellvalues_tensorial,q_point) end if error > 0.01 push!(cells_to_refine,cellid) @@ -161,18 +161,20 @@ function solve_adaptive(initial_grid) vtk_point_data(vtk, projector, σ_dof, "stress") vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") vtk_cell_data(vtk, error_arr, "error") - pvd[i] = vtk end Ferrite.refine!(grid,cells_to_refine) transfered_grid, hnodes = Ferrite.creategrid(grid) + vtk_grid("unbalanced.vtu", dh) do vtk + end Ferrite.balanceforest!(grid) transfered_grid, hnodes = Ferrite.creategrid(grid) + vtk_grid("balanced.vtu", dh) do vtk + end i += 1 if isempty(cells_to_refine) finished = true end end - vtk_save(pvd); end solve_adaptive(grid) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index f182bae78b..c1c9e77007 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -18,13 +18,13 @@ end OctantBWG(dim::Integer, l::Integer, b::Integer, m::Integer) Construct an `octant` based on dimension `dim`, level `l`, amount of levels `b` and morton index `m` """ -function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: Integer +function OctantBWG(dim::Integer, l::T1, m::T2, b::T1=_maxlevel[dim-1]) where {T1 <: Integer, T2 <: Integer} @assert l ≤ b #maximum refinement level exceeded - @assert m ≤ (one(T)+one(T))^(dim*l) - x,y,z = (zero(T),zero(T),zero(T)) + @assert m ≤ (one(T1)+one(T1))^(dim*l) + x,y,z = (zero(T1),zero(T1),zero(T1)) h = Int32(_compute_size(b,l)) - _zero = zero(T) - _one = one(T) + _zero = zero(T1) + _one = one(T1) _two = _one + _one for i in _zero:l-_one x = x | (h*((m-_one) & _two^(dim*i))÷_two^((dim-_one)*i)) @@ -32,18 +32,23 @@ function OctantBWG(dim::Integer, l::T, m::T, b::T=_maxlevel[dim-1]) where T <: I z = z | (h*((m-_one) & _two^(dim*i+_two))÷_two^((dim-_one)*i+_two)) end if dim < 3 - OctantBWG{2,4,4,T}(l,(x,y)) + OctantBWG{2,4,4,T1}(l,(x,y)) else - OctantBWG{3,8,6,T}(l,(x,y,z)) + OctantBWG{3,8,6,T1}(l,(x,y,z)) end end -OctantBWG(dim::Int,l::Int,m::Int,b::Int=_maxlevel[dim-1]) = OctantBWG(dim,Int32(l),Int32(m),Int32(b)) -OctantBWG(dim::Int,l::Int,m::Int,b::Int32) = OctantBWG(dim,Int32(l),Int32(m),b) -OctantBWG(dim::Int,l::Int32,m::Int,b::Int32) = OctantBWG(dim,l,Int32(m),b) -OctantBWG(level::Int,coords::NTuple) = OctantBWG(Int32(level),Int32.(coords)) -OctantBWG(level::Int32,coords::NTuple) = OctantBWG(level,Int32.(coords)) -OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim = OctantBWG{dim,2^dim,2*dim,Int32}(level,coords) +#OctantBWG(dim::Int,l::Int,m::Int,b::Int=_maxlevel[dim-1]) = OctantBWG(dim,l,m,b) +#OctantBWG(dim::Int,l::Int,m::Int,b::Int32) = OctantBWG(dim,l,m,b) +#OctantBWG(dim::Int,l::Int32,m::Int,b::Int32) = OctantBWG(dim,l,Int32(m),b) +function OctantBWG(level::Int,coords::NTuple) + dim = length(coords) + nnodes = 2^dim + nfaces = 2*dim + OctantBWG{dim,nnodes,nfaces,eltype(coords)}(level,coords) +end +#OctantBWG(level::Int32,coords::NTuple) = OctantBWG(level,Int32.(coords)) +#OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim = OctantBWG{dim,2^dim,2*dim,Int32}(level,coords) # From BWG 2011 # > The octant coordinates are stored as integers of a fixed number b of bits, @@ -76,6 +81,7 @@ morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2< Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) root(dim::T) where T<:Integer = zero(OctantBWG{dim,2^dim,2*dim}) +Base.eltype(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = T ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N # TODO change to how many corners ncorners(o::OctantBWG) = ncorners(typeof(o)) @@ -302,12 +308,13 @@ function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where { deleteat!(octree.leaves,leave_idx-shift+one(T):leave_idx-shift+window_length) end -OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6,Int32}([zero(OctantBWG{3,8,6})],Int32(b),nodes) -OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4,Int32}([zero(OctantBWG{2,4,4})],Int32(b),nodes) +OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6,Int64}([zero(OctantBWG{3,8,6})],Int64(b),nodes) +OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4,Int64}([zero(OctantBWG{2,4,4})],Int64(b),nodes) OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4,4}(cell.nodes,b) OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) +Base.eltype(::Type{OctreeBWG{dim,N,M,T}}) where {dim,N,M,T} = T function inside(oct::OctantBWG{dim},b) where dim maxsize = _maximum_size(b) @@ -474,11 +481,13 @@ function getncells(grid::ForestBWG) return numcells end -function getcells(forest::ForestBWG{dim}) where dim - celltype = dim == 2 ? OctantBWG{2,4,4,Int32} : OctantBWG{3,8,6,Int32} +function getcells(forest::ForestBWG{dim,C}) where {dim,C} + treetype = C ncells = getncells(forest) - cellvector = Vector{celltype}(undef,ncells) - o = one(Int32) + nnodes = 2^dim + nfaces = 2*dim + cellvector = Vector{OctantBWG{dim,nnodes,nfaces,eltype(C)}}(undef,ncells) + o = one(eltype(C)) cellid = o for tree in forest.cells for leaf in tree.leaves @@ -840,7 +849,7 @@ function isancestor(o1,o2,b) end # TODO verify and generalize -function contains_face(mface::Tuple{Tuple{T,T},Tuple{T,T}},sface::Tuple{Tuple{T,T},Tuple{T,T}}) where T +function contains_face(mface::Tuple{Tuple{T1,T1},Tuple{T1,T1}},sface::Tuple{Tuple{T2,T2},Tuple{T2,T2}}) where {T1<:Integer,T2<:Integer} if mface[1][1] == sface[1][1] && mface[2][1] == sface[2][1] # vertical return mface[1][2] ≤ sface[1][2] ≤ sface[2][2] ≤ mface[2][2] elseif mface[1][2] == sface[1][2] && mface[2][2] == sface[2][2] # horizontal diff --git a/src/L2_projection.jl b/src/L2_projection.jl index c7427bf7ca..72160c46ae 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -225,13 +225,14 @@ function _project(vars, proj::L2Projector, fe_values::AbstractValues, M::Integer f[dof, :] += fe[num, :] end end - + # Correctly apply affine constraints projected_vals = similar(f) + rhsdata = get_rhs_data(ch,proj.M_cholesky) + apply!(proj.M_cholesky,ch) for (i,col) in enumerate(eachcol(f)) - _M = deepcopy(proj.M_cholesky) - apply!(_M, col, ch) - u = _M \ col + apply_rhs!(rhsdata, col, ch) + u = proj.M_cholesky \ col apply!(u, ch) projected_vals[:,i] = u end diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 4fda1efa12..53a151f7c7 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -142,6 +142,29 @@ end end # module ConvergenceTestHelper +function compute_fluxes(cellvalues::CellValues{<:ScalarInterpolation}, dh::DofHandler, a::AbstractVector{T}) where T + + n = getnbasefunctions(cellvalues) + cell_dofs = zeros(Int, n) + nqp = getnquadpoints(cellvalues) + + # Allocate storage for the fluxes to store + q = [Vec{2,T}[] for _ in 1:getncells(dh.grid)] + + for (cell_num, cell) in enumerate(CellIterator(dh)) + q_cell = q[cell_num] + celldofs!(cell_dofs, dh, cell_num) + aᵉ = a[cell_dofs] + reinit!(cellvalues, cell) + + for q_point in 1:nqp + q_qp = - function_gradient(cellvalues, q_point, aᵉ) + push!(q_cell, q_qp) + end + end + return q +end + @testset "convergence analysis" begin @testset "$interpolation" for interpolation in ( Lagrange{RefQuadrilateral, 1}(), @@ -163,9 +186,13 @@ end # module ConvergenceTestHelper grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) + q_gp = compute_fluxes(cellvalues, dh, u); + projector = L2Projector(interpolation, grid_transfered); + q_projected = project(projector, q_gp, qr); L2norm, _ = ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2, adaptive_grid) vtk_grid("p4est_test$(i).vtu",dh) do vtk vtk_point_data(vtk,dh,u) + vtk_point_data(vtk, projector, q_projected, "q") end i += 1 end From 26f5d413d7a3efc8408eddca451afb33969c2eb3 Mon Sep 17 00:00:00 2001 From: termi-official Date: Tue, 10 Oct 2023 18:52:30 +0200 Subject: [PATCH 099/143] Fix affine constaint for y component in elasticity example. --- docs/src/literate-tutorials/adaptivity.jl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 7841357463..e72c9e6ea6 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -75,9 +75,13 @@ function solve(grid,hnodes) ch = ConstraintHandler(dh) add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.01, 2)) + # One set of linear contraints per hanging node for (hdof,mdof) in hnodes - lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) - add!(ch,lc) + # One constraint per component + for d in 1:dim + lc = AffineConstraint(vdict[1][hdof]+d-1,[vdict[1][m]+d-1 => 0.5 for m in mdof],0.0) + add!(ch,lc) + end end close!(ch); From 9503bd72bce1640577315f94fcb2ea936510ee1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 11 Oct 2023 16:59:15 +0200 Subject: [PATCH 100/143] add vertex permutations to creategrid --- src/Adaptivity/AdaptiveCells.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index c1c9e77007..2a32d6274a 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -535,6 +535,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} sizehint!(nodes,getncells(forest)*2^dim) _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + node_map = dim < 3 ? node_map₂ : node_map₃ + node_map_inv = dim < 3 ? node_map₂_inv : node_map₃_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() pivot_nodeid = 1 @@ -552,13 +554,13 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} for (k,tree) in enumerate(forest.cells) _vertices = vertices(root(dim),tree.b) for (vi,v) in enumerate(_vertices) - vertex_neighbor = forest.topology.vertex_vertex_neighbor[k,vi] + vertex_neighbor = forest.topology.vertex_vertex_neighbor[k,node_map[vi]] if length(vertex_neighbor) == 0 continue end if k < vertex_neighbor[1][1] #delete!(nodes,(k,v)) - new_v = vertex(root(dim),vertex_neighbor[1][2],tree.b) + new_v = vertex(root(dim),node_map[vertex_neighbor[1][2]],tree.b) new_k = vertex_neighbor[1][1] nodeids[(k,v)] = nodeids[(new_k,new_v)] nodeowners[(k,v)] = (new_k,new_v) @@ -604,7 +606,6 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} celltype = dim < 3 ? Quadrilateral : Hexahedron cells = celltype[] cellnodes = zeros(Int,2^dim) - node_map = dim < 3 ? node_map₂ : node_map₃ for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves _vertices = vertices(leaf,tree.b) From fab6539e52ae4428da408023b3aaf584c9b0c3ba Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Tue, 9 Apr 2024 12:37:13 +0200 Subject: [PATCH 101/143] [X-PR] p4est/p8est face transformation fix (#890) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @termi-official included face orientation information such that arbitrary meshes can be used. Face orientation information are necessary when transferring the lazy adaptive representation to an actual Ferrite.Grid or equivalently to the same information but in another construct. Face orientation is included in the `creategrid` function but probably missing in the balancing of the forrest w.r.t. interoctree operation --------- Co-authored-by: Maximilian Köhler Co-authored-by: termi-official <9196588+termi-official@users.noreply.github.com> --- docs/src/assets/references.bib | 30 ++ src/Adaptivity/AdaptiveCells.jl | 497 ++++++++++++++++++++++---------- src/Adaptivity/visualization.jl | 2 +- src/Grid/grid_generators.jl | 17 ++ test/test_p4est.jl | 147 +++++++++- test/test_p4est_example.jl | 16 +- 6 files changed, 547 insertions(+), 162 deletions(-) diff --git a/docs/src/assets/references.bib b/docs/src/assets/references.bib index 2366a92481..e933afebf2 100644 --- a/docs/src/assets/references.bib +++ b/docs/src/assets/references.bib @@ -63,3 +63,33 @@ @phdthesis{Cenanovic2017 school={J\o{}pk\o{}ping University, School of Engineering}, year=2017 } +@article{BWG2011, + title={p4est: Scalable algorithms for parallel adaptive mesh refinement on forests of octrees}, + author={Burstedde, Carsten and Wilcox, Lucas C and Ghattas, Omar}, + journal={SIAM Journal on Scientific Computing}, + volume={33}, + number={3}, + pages={1103--1133}, + year={2011}, + publisher={SIAM} +} +@article{IBWG2015, + title={Recursive algorithms for distributed forests of octrees}, + author={Isaac, Tobin and Burstedde, Carsten and Wilcox, Lucas C and Ghattas, Omar}, + journal={SIAM Journal on Scientific Computing}, + volume={37}, + number={5}, + pages={C497--C531}, + year={2015}, + publisher={SIAM} +} +@article{SSB2008, + title={Bottom-up construction and 2: 1 balance refinement of linear octrees in parallel}, + author={Sundar, Hari and Sampath, Rahul S and Biros, George}, + journal={SIAM Journal on Scientific Computing}, + volume={30}, + number={5}, + pages={2675--2708}, + year={2008}, + publisher={SIAM} +} diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index 2a32d6274a..cbb71a81a7 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -1,3 +1,8 @@ +# TODO we should remove the mixture of indices. Maybe with these: +# - struct FaceIndexBWG ... end +# - struct QuadrilateralBWG ... end +# - struct HexahedronBWG ... end + abstract type AbstractAdaptiveGrid{dim} <: AbstractGrid{dim} end abstract type AbstractAdaptiveCell{refshape <: AbstractRefShape} <: AbstractCell{refshape} end @@ -7,7 +12,7 @@ function set_maxlevel(dim::Integer,maxlevel::Integer) _maxlevel[dim-1] = maxlevel end -struct OctantBWG{dim, N, M, T} <: AbstractCell{RefHypercube{dim}} +struct OctantBWG{dim, N, T} <: AbstractCell{RefHypercube{dim}} #Refinement level l::T #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} @@ -32,9 +37,9 @@ function OctantBWG(dim::Integer, l::T1, m::T2, b::T1=_maxlevel[dim-1]) where {T1 z = z | (h*((m-_one) & _two^(dim*i+_two))÷_two^((dim-_one)*i+_two)) end if dim < 3 - OctantBWG{2,4,4,T1}(l,(x,y)) + OctantBWG{2,4,T1}(l,(x,y)) else - OctantBWG{3,8,6,T1}(l,(x,y,z)) + OctantBWG{3,8,T1}(l,(x,y,z)) end end @@ -44,25 +49,27 @@ end function OctantBWG(level::Int,coords::NTuple) dim = length(coords) nnodes = 2^dim - nfaces = 2*dim - OctantBWG{dim,nnodes,nfaces,eltype(coords)}(level,coords) + OctantBWG{dim,nnodes,eltype(coords)}(level,coords) end #OctantBWG(level::Int32,coords::NTuple) = OctantBWG(level,Int32.(coords)) #OctantBWG(level::Int32, coords::NTuple{dim,Int32}) where dim = OctantBWG{dim,2^dim,2*dim,Int32}(level,coords) -# From BWG 2011 -# > The octant coordinates are stored as integers of a fixed number b of bits, -# > where the highest (leftmost) bit represents the first vertical level of the -# > octree (counting the root as level zero), the second highest bit the second level of the octree, and so on. -# Morton Index can thus be constructed by interleaving the integer bits: -# m(Oct) := (y_b,x_b,y_b-1,x_b-1,...y0,x0)_2 -# further we assume the following -# > Due to the two-complement representation of integers in practically all current hardware, -# > where the highest digit denotes the negated appropriate power of two, bitwise operations as used, -# > for example, in Algorithm 1 yield the correct result even for negative coordinates. -# also from BWG 2011 -# TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ -function morton(octant::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Integer} +""" +From [BWG2011](@citet); +> The octant coordinates are stored as integers of a fixed number b of bits, +> where the highest (leftmost) bit represents the first vertical level of the +> octree (counting the root as level zero), the second highest bit the second level of the octree, and so on. +A morton index can thus be constructed by interleaving the integer bits: +m(Oct) := (y_b,x_b,y_b-1,x_b-1,...y0,x0)_2 +further we assume the following +> Due to the two-complement representation of integers in practically all current hardware, +> where the highest digit denotes the negated appropriate power of two, bitwise operations as used, +> for example, in Algorithm 1 yield the correct result even for negative coordinates. +also from [BWG2011](@citet) + +TODO: use LUT method from https://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ +""" +function morton(octant::OctantBWG{dim,N,T},l::T,b::T) where {dim,N,T<:Integer} o = one(T) z = zero(T) id = zero(widen(eltype(octant.xyz))) @@ -76,24 +83,24 @@ function morton(octant::OctantBWG{dim,N,M,T},l::T,b::T) where {dim,N,M,T<:Intege # discard the bit information about deeper levels return (id >> ((b-l)*dim))+o end -morton(octant::OctantBWG{dim,N,M,T1},l::T2,b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = morton(octant,T1(l),T1(b)) +morton(octant::OctantBWG{dim,N,T1},l::T2,b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = morton(octant,T1(l),T1(b)) -Base.zero(::Type{OctantBWG{3, 8, 6}}) = OctantBWG(3, 0, 1) -Base.zero(::Type{OctantBWG{2, 4, 4}}) = OctantBWG(2, 0, 1) -root(dim::T) where T<:Integer = zero(OctantBWG{dim,2^dim,2*dim}) -Base.eltype(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = T +Base.zero(::Type{OctantBWG{3, 8}}) = OctantBWG(3, 0, 1) +Base.zero(::Type{OctantBWG{2, 4}}) = OctantBWG(2, 0, 1) +root(dim::T) where T<:Integer = zero(OctantBWG{dim,2^dim}) +Base.eltype(::Type{OctantBWG{dim,N,T}}) where {dim,N,T} = T -ncorners(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N # TODO change to how many corners +ncorners(::Type{OctantBWG{dim,N,T}}) where {dim,N,T} = N # TODO change to how many corners ncorners(o::OctantBWG) = ncorners(typeof(o)) -nnodes(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N +nnodes(::Type{OctantBWG{dim,N,T}}) where {dim,N,T} = N nnodes(o::OctantBWG) = ncorners(typeof(o)) -nchilds(::Type{OctantBWG{dim,N,M,T}}) where {dim,N,M,T} = N +nchilds(::Type{OctantBWG{dim,N,T}}) where {dim,N,T} = N nchilds(o::OctantBWG) = nchilds(typeof(o))# Follow z order, x before y before z for faces, edges and corners Base.isequal(o1::OctantBWG, o2::OctantBWG) = (o1.l == o2.l) && (o1.xyz == o2.xyz) """ o1::OctantBWG < o2::OctantBWG -Implements Algorithm 2.1 of IBWG 2015. +Implements Algorithm 2.1 of [IBWG2015](@citet). Checks first if mortonid is smaller and later if level is smaller. Thus, ancestors precede descendants (preordering). """ @@ -106,7 +113,7 @@ function Base.isless(o1::OctantBWG, o2::OctantBWG) end end -function children(octant::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} +function children(octant::OctantBWG{dim,N,T}, b::Integer) where {dim,N,T} o = one(T) _nchilds = nchilds(octant) startid = morton(octant,octant.l+o,b) @@ -140,7 +147,7 @@ Base.show(io::IO, ::MIME"text/plain", f::OctantFaceIndex) = print(io, "O-Face $( Base.show(io::IO, f::OctantFaceIndex) = print(io, "O-Face $(f.idx)") vertex(octant::OctantBWG, c::OctantCornerIndex, b::Integer) = vertex(octant,c.idx,b) -function vertex(octant::OctantBWG{dim,N,M,T}, c::Integer, b::Integer) where {dim,N,M,T} +function vertex(octant::OctantBWG{dim,N,T}, c::Integer, b::Integer) where {dim,N,T} h = T(_compute_size(b,octant.l)) return ntuple(d->((c-1) & (2^(d-1))) == 0 ? octant.xyz[d] : octant.xyz[d] + h ,dim) end @@ -174,10 +181,10 @@ end """ boundaryset(o::OctantBWG{2}, i::Integer, b::Integer -implements two dimensional boundaryset table from Fig.4.1 IBWG 2015 +implements two dimensional boundaryset table from Fig.4.1 [IBWG2015](@citet) TODO: could be done little bit less ugly """ -function boundaryset(o::OctantBWG{2,N,M,T}, i::Integer, b::Integer) where {N,M,T} +function boundaryset(o::OctantBWG{2,N,T}, i::Integer, b::Integer) where {N,T} if i==1 return Set((OctantCornerIndex(1),OctantFaceIndex(1),OctantFaceIndex(3))) elseif i==2 @@ -193,10 +200,10 @@ end """ boundaryset(o::OctantBWG{3}, i::Integer, b::Integer -implements three dimensional boundaryset table from Fig.4.1 IBWG 2015 +implements three dimensional boundaryset table from Fig.4.1 [IBWG2015](@citet) TODO: could be done little bit less ugly """ -function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T} +function boundaryset(o::OctantBWG{3,N,T}, i::Integer, b::Integer) where {N,T} if i==1 return Set((OctantCornerIndex(1),OctantEdgeIndex(1),OctantEdgeIndex(5),OctantEdgeIndex(9), OctantFaceIndex(1),OctantFaceIndex(3),OctantFaceIndex(5))) elseif i==2 @@ -219,12 +226,12 @@ function boundaryset(o::OctantBWG{3,N,M,T}, i::Integer, b::Integer) where {N,M,T end """ - find_range_boundaries(f::OctantBWG{dim,N,M,T}, l::OctantBWG{dim,N,M,T}, s::OctantBWG{dim,N,M,T}, idxset, b) - find_range_boundaries(s::OctantBWG{dim,N,M,T}, idxset, b) -Algorithm 4.2 of IBWG 2015 + find_range_boundaries(f::OctantBWG{dim,N,T}, l::OctantBWG{dim,N,T}, s::OctantBWG{dim,N,T}, idxset, b) + find_range_boundaries(s::OctantBWG{dim,N,T}, idxset, b) +Algorithm 4.2 of [IBWG2015](@citet) TODO: write tests """ -function find_range_boundaries(f::OctantBWG{dim,N,M,T1}, l::OctantBWG{dim,N,M,T1}, s::OctantBWG{dim,N,M,T1}, idxset::Set{OctantIndex{T2}}, b) where {dim,N,M,T1,T2} +function find_range_boundaries(f::OctantBWG{dim,N,T1}, l::OctantBWG{dim,N,T1}, s::OctantBWG{dim,N,T1}, idxset::Set{OctantIndex{T2}}, b) where {dim,N,T1,T2} o = one(T1) if isempty(idxset) || s.l == b return idxset @@ -271,14 +278,14 @@ function isrelevant(xyz::NTuple{dim,T},leafsuppₚ::Set{<:OctantBWG}) where {dim return true end -struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{RefHypercube{dim}} - leaves::Vector{OctantBWG{dim,N,M,T}} +struct OctreeBWG{dim,N,T} <: AbstractAdaptiveCell{RefHypercube{dim}} + leaves::Vector{OctantBWG{dim,N,T}} #maximum refinement level b::T nodes::NTuple{N,Int} end -function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} +function refine!(octree::OctreeBWG{dim,N,T}, pivot_octant::OctantBWG{dim,N,T}) where {dim,N,T<:Integer} if !(pivot_octant.l + 1 <= octree.b) return end @@ -293,7 +300,7 @@ function refine!(octree::OctreeBWG{dim,N,M,T}, pivot_octant::OctantBWG{dim,N,M,T end end -function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where {dim,N,M,T<:Integer} +function coarsen!(octree::OctreeBWG{dim,N,T}, o::OctantBWG{dim,N,T}) where {dim,N,T<:Integer} _two = T(2) leave_idx = findfirst(x->x==o,octree.leaves) shift = child_id(o,octree.b) - one(T) @@ -308,13 +315,13 @@ function coarsen!(octree::OctreeBWG{dim,N,M,T}, o::OctantBWG{dim,N,M,T}) where { deleteat!(octree.leaves,leave_idx-shift+one(T):leave_idx-shift+window_length) end -OctreeBWG{3,8,6}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,6,Int64}([zero(OctantBWG{3,8,6})],Int64(b),nodes) -OctreeBWG{2,4,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,4,Int64}([zero(OctantBWG{2,4,4})],Int64(b),nodes) -OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4,4}(cell.nodes,b) -OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8,6}(cell.nodes,b) +OctreeBWG{3,8}(nodes::NTuple,b=_maxlevel[2]) = OctreeBWG{3,8,Int64}([zero(OctantBWG{3,8})],Int64(b),nodes) +OctreeBWG{2,4}(nodes::NTuple,b=_maxlevel[1]) = OctreeBWG{2,4,Int64}([zero(OctantBWG{2,4})],Int64(b),nodes) +OctreeBWG(cell::Quadrilateral,b=_maxlevel[2]) = OctreeBWG{2,4}(cell.nodes,b) +OctreeBWG(cell::Hexahedron,b=_maxlevel[1]) = OctreeBWG{3,8}(cell.nodes,b) Base.length(tree::OctreeBWG) = length(tree.leaves) -Base.eltype(::Type{OctreeBWG{dim,N,M,T}}) where {dim,N,M,T} = T +Base.eltype(::Type{OctreeBWG{dim,N,T}}) where {dim,N,T} = T function inside(oct::OctantBWG{dim},b) where dim maxsize = _maximum_size(b) @@ -327,9 +334,9 @@ inside(tree::OctreeBWG{dim},oct::OctantBWG{dim}) where dim = inside(oct,tree.b) """ split_array(octree::OctreeBWG, a::OctantBWG) split_array(octantarray, a::OctantBWG, b::Integer) -Algorithm 3.3 of IBWG2015. Efficient binary search +Algorithm 3.3 of [IBWG2015](@citet). Efficient binary search. """ -function split_array(octantarray, a::OctantBWG{dim,N,M,T}, b::Integer) where {dim,N,M,T} +function split_array(octantarray, a::OctantBWG{dim,N,T}, b::Integer) where {dim,N,T} o = one(T) 𝐤 = T[i==1 ? 1 : length(octantarray)+1 for i in 1:2^dim+1] for i in 2:2^dim @@ -352,7 +359,7 @@ end split_array(tree::OctreeBWG, a::OctantBWG) = split_array(tree.leaves, a, tree.b) -function search(octantarray, a::OctantBWG{dim,N,M,T1}, idxset::Vector{T2}, b::Integer, Match=match) where {dim,N,M,T1<:Integer,T2} +function search(octantarray, a::OctantBWG{dim,N,T1}, idxset::Vector{T2}, b::Integer, Match=match) where {dim,N,T1<:Integer,T2} isempty(octantarray) && return isleaf = (length(octantarray) == 1 && a ∈ octantarray) ? true : false idxset_match = eltype(idxset)[] @@ -375,7 +382,7 @@ search(tree::OctreeBWG, a::OctantBWG, idxset, Match=match) = search(tree.leaves, """ match(o::OctantBWG, isleaf::Bool, q) -from IBWG2015 +from [IBWG2015](@citet) > match returns true if there is a leaf r ∈ 𝒪 that is a descendant of o > such that match_q(r) = true, and is allowed to return a false positive > (i.e., true even if match_q(r) = false for all descendants leaves of o) @@ -391,8 +398,8 @@ end """ ForestBWG{dim, C<:AbstractAdaptiveCell, T<:Real} <: AbstractAdaptiveGrid{dim} -`p4est` adaptive grid implementation based on Burstedde, Wilcox, Ghattas [2011] -and Isaac, Burstedde, Wilcox, Ghattas [2015] +`p4est` adaptive grid implementation based on [BWG2011](@citet) +and [IBWG2015](@citet). ## Constructor ForestBWG(grid::AbstractGrid{dim}, b=_maxlevel[dim-1]) where dim @@ -485,8 +492,7 @@ function getcells(forest::ForestBWG{dim,C}) where {dim,C} treetype = C ncells = getncells(forest) nnodes = 2^dim - nfaces = 2*dim - cellvector = Vector{OctantBWG{dim,nnodes,nfaces,eltype(C)}}(undef,ncells) + cellvector = Vector{OctantBWG{dim,nnodes,eltype(C)}}(undef,ncells) o = one(eltype(C)) cellid = o for tree in forest.cells @@ -502,7 +508,7 @@ function getcells(forest::ForestBWG{dim}, cellid::Int) where dim @warn "Slow dispatch, consider to call `getcells(forest)` once instead" maxlog=1 #TODO doc page for performance #TODO should nleaves be saved by forest? nleaves = length.(forest.cells) # cells=trees - #TODO remove that later by for loop or IBWG 2015 iterator approach + #TODO remove that later by for loop or [IBWG2015](@citet) iterator approach nleaves_cumsum = cumsum(nleaves) k = findfirst(x->cellid<=x,nleaves_cumsum) #TODO is this actually correct? @@ -529,7 +535,7 @@ end transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) -#TODO: this function should wrap the LNodes Iterator of IBWG2015 +#TODO: this function should wrap the LNodes Iterator of [IBWG2015](@citet) function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Vector{Tuple{Int,NTuple{dim,Int32}}}() sizehint!(nodes,getncells(forest)*2^dim) @@ -539,6 +545,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} node_map_inv = dim < 3 ? node_map₂_inv : node_map₃_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() + + # Phase 1: Assign node owners intra-octree pivot_nodeid = 1 for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves @@ -551,72 +559,125 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end end end + + # Phase 2: Assign node owners inter-octree for (k,tree) in enumerate(forest.cells) _vertices = vertices(root(dim),tree.b) - for (vi,v) in enumerate(_vertices) - vertex_neighbor = forest.topology.vertex_vertex_neighbor[k,node_map[vi]] - if length(vertex_neighbor) == 0 - continue - end - if k < vertex_neighbor[1][1] - #delete!(nodes,(k,v)) - new_v = vertex(root(dim),node_map[vertex_neighbor[1][2]],tree.b) - new_k = vertex_neighbor[1][1] - nodeids[(k,v)] = nodeids[(new_k,new_v)] - nodeowners[(k,v)] = (new_k,new_v) + # Vertex neighbors + @debug println("Setting vertex neighbors for octree $k") + for (v,vc) in enumerate(_vertices) + vertex_neighbor = forest.topology.vertex_vertex_neighbor[k,node_map[v]] + for (k′, v′) in vertex_neighbor + @debug println(" pair $v $v′") + if k > k′ + #delete!(nodes,(k,v)) + new_v = vertex(root(dim),node_map[v′],tree.b) + nodeids[(k,vc)] = nodeids[(k′,new_v)] + nodeowners[(k,vc)] = (k′,new_v) + @debug println(" Matching $vc (local) to $new_v (neighbor)") + end end + # TODO check if we need to also update the face neighbors end - _faces = faces(root(dim),tree.b) - for (fi,f) in enumerate(_faces) # fi in p4est notation - face_neighbor = forest.topology.face_face_neighbor[k,_perm[fi]] - if length(face_neighbor) == 0 - continue - end - k′ = face_neighbor[1][1] - neighbor_vertices = vertices.(forest.cells[k′].leaves,forest.cells[k′].b) - if k < k′ - if fi < 3 - parallel_axis = 1 - elseif 3 ≤ fi < 5 - parallel_axis = 2 - elseif 5 ≤ fi < 7 - parallel_axis = 3 + if dim > 1 + _faces = faces(root(dim),tree.b) + # Face neighbors + @debug println("Updating face neighbors for octree $k") + for (f,fc) in enumerate(_faces) # f in p4est notation + f_axis_index, f_axis_sign = divrem(f-1,2) + face_neighbor = forest.topology.face_face_neighbor[k,_perm[f]] + if length(face_neighbor) == 0 + continue end - for leaf in tree.leaves - for v in vertices(leaf,tree.b) - if v[parallel_axis] == f[1][parallel_axis] == f[2][parallel_axis] - cache_octant = OctantBWG(leaf.l,v) - cache_octant = transform_face(forest,k′,_perminv[face_neighbor[1][2]],cache_octant) # after transform - #TODO check if its worth to change this comparison from ∈ nodes to ∈ all nodes of k' - #if (k′,cache_octant.xyz) ∈ nodes - if any((cache_octant.xyz,) .∈ neighbor_vertices) - #delete!(nodes,(k,v)) - nodeids[(k,v)] = nodeids[(k′,cache_octant.xyz)] - nodeowners[(k,v)] = (k′,cache_octant.xyz) + @debug @assert length(face_neighbor) == 1 + k′, f′_ferrite = face_neighbor[1] + f′ = _perminv[f′_ferrite] + if k > k′ # Owner + tree′ = forest.cells[k′] + for leaf in tree.leaves + if f_axis_sign == 1 # positive face + if leaf.xyz[f_axis_index + 1] < 2^tree.b-2^(tree.b-leaf.l) + @debug println(" Rejecting $leaf") + continue + end + else # negative face + if leaf.xyz[f_axis_index + 1] > 0 + @debug println(" Rejecting $leaf") + continue + end + end + neighbor_candidate = transform_face(forest,k′,f′,leaf) + # Candidate must be the face opposite to f' + f′candidate = ((f′ - 1) ⊻ 1) + 1 + fnodes = face(leaf, f , tree.b) + fnodes_neighbor = face(neighbor_candidate, f′candidate, tree′.b) + r = compute_face_orientation(forest,k,f) + @debug println(" Matching $fnodes (local) to $fnodes_neighbor (neighbor)") + if dim == 2 + if r == 0 # same orientation + for i ∈ 1:2 + if haskey(nodeids, (k′,fnodes_neighbor[i])) + nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[i])] + nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[i]) + end + end + else + for i ∈ 1:2 + if haskey(nodeids, (k′,fnodes_neighbor[3-i])) + nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[3-i])] + nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[3-i]) + end + end end + else + @error "Not implemented for $dim dimensions." end end end end end if dim > 2 - #TODO add egede dupplication check + #TODO add egde duplication check + @error "Edge deduplication not implemented yet." end end + + # Phase 3: Compute unique physical nodes + nodeids_dedup = Dict{Int,Int}() + next_nodeid = 1 + for (kv,nodeid) in nodeids + if !haskey(nodeids_dedup, nodeid) + nodeids_dedup[nodeid] = next_nodeid + next_nodeid += 1 + end + end + nodes_physical_all = transform_pointBWG(forest,nodes) + nodes_physical = zeros(eltype(nodes_physical_all), next_nodeid-1) + for (ni, (kv,nodeid)) in enumerate(nodeids) + nodes_physical[nodeids_dedup[nodeid]] = nodes_physical_all[nodeid] + end + + # Phase 4: Generate cells celltype = dim < 3 ? Quadrilateral : Hexahedron cells = celltype[] cellnodes = zeros(Int,2^dim) for (k,tree) in enumerate(forest.cells) for leaf in tree.leaves _vertices = vertices(leaf,tree.b) - cellnodes = ntuple(i-> nodeids[nodeowners[(k,_vertices[i])]],length(_vertices)) + cellnodes = ntuple(i-> nodeids_dedup[nodeids[nodeowners[(k,_vertices[i])]]],length(_vertices)) push!(cells,celltype(ntuple(i->cellnodes[node_map[i]],length(cellnodes)))) end end + + # Phase 5: Generate grid and haning nodes facesets = reconstruct_facesets(forest) #TODO edge, node and cellsets - grid = Grid(cells,transform_pointBWG(forest,nodes) .|> Node, facesets=facesets) + grid = Grid(cells,nodes_physical .|> Node, facesets=facesets) hnodes = hangingnodes(forest, nodeids, nodeowners) - return grid, hnodes + hnodes_dedup = Dict{Int64, Vector{Int64}}() + for (constrained,constainers) in hnodes + hnodes_dedup[nodeids_dedup[constrained]] = [nodeids_dedup[constainer] for constainer in constainers] + end + return grid, hnodes_dedup end function reconstruct_facesets(forest::ForestBWG{dim}) where dim @@ -699,8 +760,10 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim return hnodes end -# Algorithm 17 of BWG Paper -# TODO need further work for dimension agnostic case +""" +Algorithm 17 of [BWG2011](@citet) +TODO need further work for dimension agnostic case +""" function balanceforest!(forest::ForestBWG{dim}) where dim perm_face = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm perm_face_inv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv @@ -721,6 +784,7 @@ function balanceforest!(forest::ForestBWG{dim}) where dim if s_i <= 4 #corner neighbor, only true for 2D see possibleneighbors cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] isempty(cc) && continue + @assert length(cc) == 1 # FIXME there can be more than 1 vertex neighbor cc = cc[1] k′, c′ = cc[1], perm_corner_inv[cc[2]] o′ = transform_corner(forest,k′,c′,o) @@ -737,6 +801,7 @@ function balanceforest!(forest::ForestBWG{dim}) where dim s_i -= 4 fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] isempty(fc) && continue + @debug @assert length(fc) == 1 fc = fc[1] k′, f′ = fc[1], perm_face_inv[fc[2]] o′ = transform_face(forest,k′,f′,o) @@ -761,9 +826,11 @@ function balanceforest!(forest::ForestBWG{dim}) where dim #end end -# Algorithm 7 of preprint Sundar, Sampath, Biros -# https://padas.oden.utexas.edu/static/papers/OctreeBalance21.pdf -# TODO optimise the unnecessary allocations +""" +Algorithm 7 of [SSB2008](@citet) + +TODO optimise the unnecessary allocations +""" function balancetree(tree::OctreeBWG) if length(tree.leaves) == 1 return tree @@ -799,8 +866,11 @@ function balancetree(tree::OctreeBWG) return OctreeBWG(R,tree.b,tree.nodes) end -# Algorithm 8 of preprint Sundar, Sampath, Biros -# inverted the algorithm to delete! instead of add incrementally to a new array +""" +Algorithm 8 of [SSB2008](@citet) + +Inverted the algorithm to delete! instead of add incrementally to a new array +""" function linearise!(leaves::Vector{T},b) where T<:OctantBWG inds = [i for i in 1:length(leaves)-1 if isancestor(leaves[i],leaves[i+1],b)] deleteat!(leaves,inds) @@ -1012,7 +1082,7 @@ note the following quote from Bursedde et al: 4, . . . , 7 being the four children on top of the children 0, . . . , 3. shifted by 1 due to julia 1 based indexing """ -function child_id(octant::OctantBWG{dim,N,M,T},b::Integer=_maxlevel[2]) where {dim,N,M,T<:Integer} +function child_id(octant::OctantBWG{dim,N,T},b::Integer=_maxlevel[2]) where {dim,N,T<:Integer} i = 0x00 t = T(2) z = zero(T) @@ -1026,10 +1096,10 @@ end """ ancestor_id(octant::OctantBWG, l::Integer, b::Integer) -Algorithm 3.2 of IBWG 2015 that generalizes `child_id` for different queried levels. +Algorithm 3.2 of [IBWG2015](@citet) that generalizes `child_id` for different queried levels. Applied to a single octree, i.e. the array of leaves, yields a monotonic sequence """ -function ancestor_id(octant::OctantBWG{dim,N,M,T}, l::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T<:Integer} +function ancestor_id(octant::OctantBWG{dim,N,T}, l::Integer, b::Integer=_maxlevel[dim-1]) where {dim,N,T<:Integer} @assert 0 < l ≤ octant.l i = 0x00 t = T(2) @@ -1041,7 +1111,7 @@ function ancestor_id(octant::OctantBWG{dim,N,M,T}, l::Integer, b::Integer=_maxle return i+0x01 end -function parent(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T} +function parent(octant::OctantBWG{dim,N,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,T} if octant.l > zero(T) h = T(_compute_size(b,octant.l)) l = octant.l - one(T) @@ -1057,16 +1127,16 @@ Given an `octant`, computes the two smallest possible octants that fit into the of `octant`, respectively. These computed octants are called first and last descendants of `octant` since they are connected to `octant` by a path down the octree to the maximum level `b` """ -function descendants(octant::OctantBWG{dim,N,M,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,M,T} +function descendants(octant::OctantBWG{dim,N,T}, b::Integer=_maxlevel[dim-1]) where {dim,N,T} l1 = b; l2 = b h = T(_compute_size(b,octant.l)) return OctantBWG(l1,octant.xyz), OctantBWG(l2,octant.xyz .+ (h-one(T))) end """ - face_neighbor(octant::OctantBWG{dim,N,M,T}, f::T, b::T=_maxlevel[2]) -> OctantBWG{3,N,M,T} + face_neighbor(octant::OctantBWG{dim,N,T}, f::T, b::T=_maxlevel[2]) -> OctantBWG{3,N,T} Intraoctree face neighbor for a given faceindex `f` (in p4est, i.e. z order convention) and specified maximum refinement level `b`. -Implements Algorithm 5 of BWG p4est paper. +Implements Algorithm 5 of [BWG2011](@citet). x-------x-------x | | | @@ -1083,7 +1153,7 @@ Then, the computed face neighbor will be octant 2 with `xyz=(1,0)`. Note that the function is not sensitive in terms of leaving the octree boundaries. For the above example, a query for face index 1 (marked as `o`) will return an octant outside of the octree with `xyz=(-1,0)`. """ -function face_neighbor(octant::OctantBWG{3,N,M,T}, f::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} +function face_neighbor(octant::OctantBWG{3,N,T}, f::T, b::T=_maxlevel[2]) where {N,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y,z = octant.xyz @@ -1092,7 +1162,7 @@ function face_neighbor(octant::OctantBWG{3,N,M,T}, f::T, b::T=_maxlevel[2]) wher z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) return OctantBWG(l,(x,y,z)) end -function face_neighbor(octant::OctantBWG{2,N,M,T}, f::T, b::T=_maxlevel[1]) where {N,M,T<:Integer} +function face_neighbor(octant::OctantBWG{2,N,T}, f::T, b::T=_maxlevel[1]) where {N,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y = octant.xyz @@ -1100,13 +1170,42 @@ function face_neighbor(octant::OctantBWG{2,N,M,T}, f::T, b::T=_maxlevel[1]) wher y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) return OctantBWG(l,(x,y)) end -face_neighbor(o::OctantBWG{dim,N,M,T1}, f::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) +face_neighbor(o::OctantBWG{dim,N,T1}, f::T2, b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) + +reference_faces_bwg(::Type{RefHypercube{2}}) = ((1,3) , (2,4), (1,2), (3,4)) +reference_faces_bwg(::Type{RefHypercube{3}}) = ((1,3,5,7) , (2,4,6,8), (1,2,5,6), (3,4,7,8), (1,2,3,4), (5,6,7,8)) # p4est consistent ordering +# reference_faces_bwg(::Type{RefHypercube{3}}) = ((1,3,7,5) , (2,4,8,6), (1,2,6,5), (3,4,8,7), (1,2,4,4), (5,6,8,7)) # Note that this does NOT follow P4est order! """ - transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T1,T2} - transform_face(forest::ForestBWG, f::FaceIndex, o::OctantBWG{dim,N,M,T2}) -> OctantBWG{dim,N,M,T2} -Interoctree coordinate transformation of an given octant `o` to the octree `k` coordinate system by virtually pushing `o`s coordinate system through `k`'s face `f`. -Implements Algorithm 8 of BWG p4est paper. + compute_face_orientation(forest::ForestBWG, k::Integer, f::Integer) +Slow implementation for the determination of the face orientation of face `f` from octree `k` following definition 2.1 from [BWG2011](@citet). + +TODO use table 3 for more vroom +""" +function compute_face_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{dim,<:Any,T2}}, k::T1, f::T1) where {dim,T1,T2} + f_perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) + f_perminv = (dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv) + n_perm = (dim == 2 ? node_map₂ : node_map₃) + n_perminv = (dim == 2 ? node_map₂_inv : node_map₃_inv) + + f_ferrite = f_perm[f] + k′, f′_ferrite = getneighborhood(forest,FaceIndex(k,f_ferrite))[1] + f′ = f_perminv[f′_ferrite] + reffacenodes = reference_faces_bwg(RefHypercube{dim}) + nodes_f = [forest.cells[k].nodes[n_perm[ni]] for ni in reffacenodes[f]] + nodes_f′ = [forest.cells[k′].nodes[n_perm[ni]] for ni in reffacenodes[f′]] + if f > f′ + return T2(findfirst(isequal(nodes_f′[1]), nodes_f)-1) + else + return T2(findfirst(isequal(nodes_f[1]), nodes_f′)-1) + end +end + +""" + transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T1,T2} + transform_face_remote(forest::ForestBWG, f::FaceIndex, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T2} +Interoctree coordinate transformation of an given octant `o` to the face-neighboring of octree `k` by virtually pushing `o`s coordinate system through `k`s face `f`. +Implements Algorithm 8 of [BWG2011](@citet). x-------x-------x | | | @@ -1121,54 +1220,152 @@ Implements Algorithm 8 of BWG p4est paper. Consider 4 octrees with a single leaf each and a maximum refinement level of 1 This function transforms octant 1 into the coordinate system of octant 2 by specifying `k=2` and `f=1`. While in the own octree coordinate system octant 1 is at `xyz=(0,0)`, the returned and transformed octant is located at `xyz=(-2,0)` -TODO: this is working for 2D except rotation, 3D I don't know """ -function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} +function transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) where {dim,N,T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) - #currently rotation not encoded - perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) + _perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) _perminv = (dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv) - kprime, fprime = getneighborhood(forest,FaceIndex(k,perm[f]))[1] - fprime = _perminv[fprime] - sprime = _one - (((f - _one) & _one) ⊻ ((fprime - _one) & _one)) - s = zeros(T2,3) - b = zeros(T2,3) - a = zeros(T2,3) - r = zero(T2) #no rotation information in face_neighbor currently - a[3] = (f-_one) ÷ 2; b[3] = (fprime-_one) ÷ 2 + k′, f′ = getneighborhood(forest,FaceIndex(k,_perm[f]))[1] + f′ = _perminv[f′] + s′ = _one - (((f - _one) & _one) ⊻ ((f′ - _one) & _one)) + s = zeros(T2,dim-1) + a = zeros(T2,3) # Coordinate axes of f + b = zeros(T2,3) # Coordinate axes of f' + r = compute_face_orientation(forest,k,f) + a[3] = (f - _one) ÷ 2; b[3] = (f′ - _one) ÷ 2 # origin and target normal axis if dim == 2 - a[1] = 1 - a[3]; b[1] = 1 - b[3]; s[1] = r #no rotation as of now + a[1] = 1 - a[3]; b[1] = 1 - b[3]; s[1] = r else a[1] = (f < 3) ? 1 : 0; a[2] = (f < 5) ? 2 : 1 - #u = ℛ[1,f] ⊻ ℛ[1,fprime] ⊻ T2((r == 1) | (r == 3)) - b[1] = (fprime < 3) ? 1 : 0; b[2] = (fprime < 5) ? 2 : 1 # r = 0 -> index 1 - #v = T2(ℛ[f,fprime] == 1) - s[1] = r & 1; s[2] = r & 2 + u = (ℛ[1,f] - _one) ⊻ (ℛ[1,f′] - _one) ⊻ (((r == 0) | (r == 3))) + b[u+1] = (f′ < 3) ? 1 : 0; b[1-u+1] = (f′ < 5) ? 2 : 1 # r = 0 -> index 1 + if ℛ[f,f′] == 1+1 # R is one-based + s[2] = r & 1; s[1] = r & 2 + else + s[1] = r & 1; s[2] = r & 2 + end end maxlevel = forest.cells[1].b l = o.l; g = 2^maxlevel - 2^(maxlevel-l) xyz = zeros(T2,dim) - xyz[b[1] + 1] = T2((s[1] == 0) ? o.xyz[a[1] + 1] : g - o.xyz[a[1] + 1]) - xyz[b[3] + 1] = T2((_two*(fprime & 1) - 1)*2^maxlevel + sprime*g + (1-2*sprime)*o.xyz[a[3] + 1]) + xyz[b[1] + _one] = T2((s[1] == 0) ? o.xyz[a[1] + _one] : g - o.xyz[a[1] + _one]) + xyz[b[3] + _one] = T2(((_two*((f′ - _one) & 1)) - _one)*2^maxlevel + s′*g + (1-2*s′)*o.xyz[a[3] + _one]) if dim == 2 return OctantBWG(l,(xyz[1],xyz[2])) else - xyz[b[2] + 1] = T2((s[2] == 0) ? o.xyz[a[1] + 1] : g - o.xyz[a[2] + 1]) + xyz[b[2] + _one] = T2((s[2] == 0) ? o.xyz[a[2] + _one] : g - o.xyz[a[2] + _one]) return OctantBWG(l,(xyz[1],xyz[2],xyz[3])) end end +transform_face_remote(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face_remote(forest,f[1],f[2],oct) + +function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2}) where {T1<:Integer,T2<:Integer} + _one = one(T2) + _two = T2(2) + _perm = 𝒱₂_perm + _perminv = 𝒱₂_perm_inv + k′, f′ = getneighborhood(forest,FaceIndex(k,_perm[f]))[1] + f′ = _perminv[f′] + + r = compute_face_orientation(forest,k,f) + # Coordinate axes of f + a = ( + f ≤ 2, # tangent + f > 2 # normal + ) + a_sign = _two*((f - _one) & 1) - _one + # Coordinate axes of f' + b = ( + f′ ≤ 2, # tangent + f′ > 2 # normal + ) + # b_sign = _two*(f′ & 1) - _one + + maxlevel = forest.cells[1].b + depth_offset = 2^maxlevel - 2^(maxlevel-o.l) + + s′ = _one - (((f - _one) & _one) ⊻ ((f′ - _one) & _one)) # arithmetic switch: TODO understand this. + + # xyz = zeros(T2, 2) + # xyz[a[1] + _one] = T2((r == 0) ? o.xyz[b[1] + _one] : depth_offset - o.xyz[b[1] + _one]) + # xyz[a[2] + _one] = T2(a_sign*2^maxlevel + s′*depth_offset + (1-2*s′)*o.xyz[b[2] + _one]) + # return OctantBWG(o.l,(xyz[1],xyz[2])) + + # We can do this because the permutation and inverse permutation are the same + xyz = ( + T2((r == 0) ? o.xyz[b[1] + _one] : depth_offset - o.xyz[b[1] + _one]), + T2(a_sign*2^maxlevel + s′*depth_offset + (1-2*s′)*o.xyz[b[2] + _one]) + ) + return OctantBWG(o.l,(xyz[a[1] + _one],xyz[a[2] + _one])) +end + +function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{3,<:Any,T2}) where {T1<:Integer,T2<:Integer} + _one = one(T2) + _two = T2(2) + _perm = 𝒱₃_perm + _perminv = 𝒱₃_perm_inv + k′, f′ = getneighborhood(forest,FaceIndex(k,_perm[f]))[1] + f′ = _perminv[f′] + s′ = _one - (((f - _one) & _one) ⊻ ((f′ - _one) & _one)) + r = compute_face_orientation(forest,k,f) + + # Coordinate axes of f + a = ( + (f ≤ 2) ? 1 : 0, + (f ≤ 4) ? 2 : 1, + (f - _one) ÷ 2 + ) + a_sign = _two*((f - _one) & 1) - _one + + # Coordinate axes of f' + b = if Bool(ℛ[1,f] - _one) ⊻ Bool(ℛ[1,f′] - _one) ⊻ (((r == 0) || (r == 3))) # What is this condition exactly? + ( + (f′ < 5) ? 2 : 1, + (f′ < 3) ? 1 : 0, + (f′ - _one) ÷ 2 + ) + else + ( + (f′ < 3) ? 1 : 0, + (f′ < 5) ? 2 : 1, + (f′ - _one) ÷ 2 + ) + end + # b_sign = _two*(f′ & 1) - _one + + s = if ℛ[f,f′] == 1+1 # R is one-based + (r & 2, r & 1) + else + (r & 1, r & 2) + end + maxlevel = forest.cells[1].b + depth_offset = 2^maxlevel - 2^(maxlevel-o.l) + xyz = zeros(T2,3) + xyz[a[1] + _one] = T2((s[1] == 0) ? o.xyz[b[1] + _one] : depth_offset - o.xyz[b[1] + _one]) + xyz[a[2] + _one] = T2((s[2] == 0) ? o.xyz[b[2] + _one] : depth_offset - o.xyz[b[2] + _one]) + xyz[a[3] + _one] = T2(a_sign*2^maxlevel + s′*depth_offset + (1-2*s′)*o.xyz[b[3] + _one]) + return OctantBWG(o.l,(xyz[1],xyz[2],xyz[3])) + + # xyz = ( + # T2((s[1] == 0) ? o.xyz[b[1] + _one] : depth_offset - o.xyz[b[1] + _one]), + # T2((s[2] == 0) ? o.xyz[b[2] + _one] : depth_offset - o.xyz[b[2] + _one]), + # T2(a_sign*2^maxlevel + s′*depth_offset + (1-2*s′)*o.xyz[b[3] + _one]) + # ) + # return OctantBWG(o.l,(xyz[a[1] + _one],xyz[a[2] + _one],xyz[a[3] + _one])) +end + transform_face(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face(forest,f[1],f[2],oct) """ transform_corner(forest,k,c',oct) transform_corner(forest,v::VertexIndex,oct) -Algorithm 12 in p4est paper to transform corner into different octree coordinate system +Algorithm 12 in [BWG2011](@citet) to transform corner into different octree coordinate system Note: in Algorithm 12 is c as a argument, but it's never used, therefore I removed it """ -function transform_corner(forest::ForestBWG,k::T1,c′::T1,oct::OctantBWG{dim,N,M,T2}) where {dim,N,M,T1<:Integer,T2<:Integer} +function transform_corner(forest::ForestBWG,k::T1,c′::T1,oct::OctantBWG{dim,N,T2}) where {dim,N,T1<:Integer,T2<:Integer} # make a dispatch that returns only the coordinates? b = forest.cells[k].b l = oct.l; g = 2^b - 2^(b-l) @@ -1184,7 +1381,7 @@ transform_corner(forest::ForestBWG,v::VertexIndex,oct::OctantBWG) = transform_co edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) Computes the edge neighbor octant which is only connected by the edge `e` to `octant` """ -function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} +function edge_neighbor(octant::OctantBWG{3,N,T}, e::T, b::T=_maxlevel[2]) where {N,T<:Integer} @assert 1 ≤ e ≤ 12 e -= one(T) l = octant.l @@ -1212,13 +1409,13 @@ function edge_neighbor(octant::OctantBWG{3,N,M,T}, e::T, b::T=_maxlevel[2]) wher error("edge case not found") end end -edge_neighbor(o::OctantBWG{3,N,M,T1}, e::T2, b::T3) where {N,M,T1<:Integer,T2<:Integer,T3<:Integer} = edge_neighbor(o,T1(e),T1(b)) +edge_neighbor(o::OctantBWG{3,N,T1}, e::T2, b::T3) where {N,T1<:Integer,T2<:Integer,T3<:Integer} = edge_neighbor(o,T1(e),T1(b)) """ corner_neighbor(octant::OctantBWG, c::Integer, b::Integer) Computes the corner neighbor octant which is only connected by the corner `c` to `octant` """ -function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) where {N,M,T<:Integer} +function corner_neighbor(octant::OctantBWG{3,N,T}, c::T, b::T=_maxlevel[2]) where {N,T<:Integer} c -= one(T) l = octant.l h = T(_compute_size(b,octant.l)) @@ -1231,7 +1428,7 @@ function corner_neighbor(octant::OctantBWG{3,N,M,T}, c::T, b::T=_maxlevel[2]) wh return OctantBWG(l,(x,y,z)) end -function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) where {N,M,T<:Integer} +function corner_neighbor(octant::OctantBWG{2,N,T}, c::T, b::T=_maxlevel[1]) where {N,T<:Integer} c -= one(T) l = octant.l h = _compute_size(b,octant.l) @@ -1242,7 +1439,7 @@ function corner_neighbor(octant::OctantBWG{2,N,M,T}, c::T, b::T=_maxlevel[1]) wh y = oy + ((c & _two) - _one)*h return OctantBWG(l,(x,y)) end -corner_neighbor(o::OctantBWG{dim,N,M,T1}, c::T2, b::T3) where {dim,N,M,T1<:Integer,T2<:Integer,T3<:Integer} = corner_neighbor(o,T1(c),T1(b)) +corner_neighbor(o::OctantBWG{dim,N,T1}, c::T2, b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = corner_neighbor(o,T1(c),T1(b)) function corner_face_participation(dim::T,c::T) where T<:Integer if dim == 2 @@ -1367,18 +1564,18 @@ const 𝒱₂_perm_inv = [3 4 1] -const 𝒱₃_perm = [2 - 4 +const 𝒱₃_perm = [5 3 - 5 + 2 + 4 1 6] const 𝒱₃_perm_inv = [5 - 1 3 2 4 + 1 6] const ℛ = [1 2 2 1 1 2 @@ -1427,11 +1624,13 @@ const opposite_face_3 = [2, 6, 5] +# Node indices permutation from p4est idx to Ferrite idx const node_map₂ = [1, 2, 4, 3] +# Node indices permutation from Ferrite idx to p4est idx const node_map₂_inv = [1, 2, 4, diff --git a/src/Adaptivity/visualization.jl b/src/Adaptivity/visualization.jl index 4f07c7b02a..fa5fe4eb85 100644 --- a/src/Adaptivity/visualization.jl +++ b/src/Adaptivity/visualization.jl @@ -22,7 +22,7 @@ function visualize_grid(forest::ForestBWG{dim}) where dim y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 GLMakie.lines!(ax,x,y,color=:black) else - faceids = [faceids[1], faceids[2], faceids[4], faceids[3]] + faceids = [faceids[1], faceids[2], faceids[4], faceids[3], faceids[1]] x = octant_physical_coordinates[faceids,1] + (octant_physical_coordinates[faceids,1] .- center[1])*0.02 y = octant_physical_coordinates[faceids,2] + (octant_physical_coordinates[faceids,2] .- center[2])*0.02 z = octant_physical_coordinates[faceids,3] + (octant_physical_coordinates[faceids,3] .- center[3])*0.02 diff --git a/src/Grid/grid_generators.jl b/src/Grid/grid_generators.jl index 38bebc88f0..be10fe4de8 100644 --- a/src/Grid/grid_generators.jl +++ b/src/Grid/grid_generators.jl @@ -565,3 +565,20 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: return Grid(cells, nodes, facesets=facesets, boundary_matrix=boundary_matrix) end + +function generate_simple_disc_grid(::Type{Quadrilateral}, n; radius= 1.0) + nnodes = 2n + 1 + θ = deg2rad(360/2n) + + nodepos = Vec((0.0,radius)) + nodes = [rotate(nodepos, θ*i) for i ∈ 0:(2n-1)] + push!(nodes, Vec((0.0,0.0))) + + elements = [Quadrilateral((2i-1==0 ? nnodes-1 : 2i-1,2i,2i+1 == nnodes ? 1 : 2i+1,nnodes)) for i ∈ 1:n] + + facesets = Dict( + "boundary" => Set([FaceIndex(i,1) for i ∈ 1:n]) ∪ Set([FaceIndex(i,2) for i ∈ 1:n]), + ) + + return Grid(elements, Node.(nodes); facesets=facesets) +end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index d5a2731347..c1836f951f 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -17,7 +17,7 @@ @test Ferrite._face_edge_corners(9,1) == (1,3) @test Ferrite._face_edge_corners(10,2) == (1,3) @test Ferrite._face_edge_corners(12,2) == (2,4) - + @test Ferrite.𝒱₃[1,:] == Ferrite.𝒰[1:4,1] == Ferrite._face_corners(3,1) @test Ferrite.𝒱₃[2,:] == Ferrite.𝒰[1:4,2] == Ferrite._face_corners(3,2) @test Ferrite.𝒱₃[3,:] == Ferrite.𝒰[5:8,1] == Ferrite._face_corners(3,3) @@ -28,7 +28,7 @@ @test Ferrite._edge_corners(1) == [1,2] @test Ferrite._edge_corners(4) == [7,8] @test Ferrite._edge_corners(12,2) == 8 - + #Test Figure 3a) of Burstedde, Wilcox, Ghattas [2011] test_ξs = (1,2,3,4) @test Ferrite._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs @@ -101,7 +101,7 @@ end o = Ferrite.OctantBWG(0,(0,0,0)) @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(0,0,0)), Ferrite.OctantBWG(2,(3,3,3))) @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(0,0,0)), Ferrite.OctantBWG(3,(7,7,7))) - + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),1,3) == Ferrite.OctantBWG(2,(2,-2,-2)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),4,3) == Ferrite.OctantBWG(2,(2,2,2)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),6,3) == Ferrite.OctantBWG(2,(4,0,-2)) @@ -132,11 +132,97 @@ end # Octant level 3 size == 2^3/2 = 1 # test translation constructor grid = generate_grid(Quadrilateral,(2,2)) + # Rotate face topologically + grid.cells[2] = Quadrilateral((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1])) + # This is our root mesh + # x-----------x-----------x + # |4 4 3|4 4 3| + # | | | + # | ^ | ^ | + # |1 | 2|1 | 2| + # | +--> | +--> | + # | | | + # |1 3 2|1 3 2| + # x-----------x-----------x + # |4 4 3|3 2 2| + # | | | + # | ^ | ^ | + # |1 | 2|4 | 3| + # | +--> | <--+ | + # | | | + # |1 3 2|4 1 1| + # x-----------x-----------x adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells @test cell isa OctreeBWG @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) end + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,1), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(-8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(0,-8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,3), adaptive_grid.cells[2].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(0,-8)) + o = adaptive_grid.cells[1].leaves[1] + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), o) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), o) == OctantBWG(0,(0,-8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,1), o) == OctantBWG(0,(-8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,3), o) == OctantBWG(0,(0,-8)) + + (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + @test length(grid_new.nodes) == 9 + @test length(hnodes) == 0 + + grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) + grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) + # Now this is our root mesh + # x-----------x-----------x + # |4 4 3|4 3 3| + # | | | + # | ^ | <--+ | + # |1 | 2|2 | 1| + # | +--> | v | + # | | | + # |1 3 2|1 4 2| + # x-----------x-----------x + # |4 4 3|3 2 2| + # | | | + # | ^ | ^ | + # |1 | 2|4 | 3| + # | +--> | <--+ | + # | | | + # |1 3 2|4 1 1| + # x-----------x-----------x + adaptive_grid = ForestBWG(grid,3) + for cell in adaptive_grid.cells + @test cell isa OctreeBWG + @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) + end + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,2), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(0,-8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,4), adaptive_grid.cells[2].leaves[1]) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(0,8)) + o = adaptive_grid.cells[1].leaves[1] + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), o) == OctantBWG(0,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), o) == OctantBWG(0,(0,-8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,2), o) == OctantBWG(0,(8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,4), o) == OctantBWG(0,(0,8)) + + #simple first and second level refinement # first case # x-----------x-----------x @@ -147,7 +233,7 @@ end # | | | # | | | # | | | - # x-----x-----x-----------| + # x-----x-----x-----------x # | | | | # | | | | # | | | | @@ -162,11 +248,23 @@ end @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == OctantBWG(1,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == OctantBWG(1,(4,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(1,(0,-4)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(1,(4,-4)) + + (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + @test length(grid_new.nodes) == 19 + @test length(hnodes) == 4 + # octree holds now 3 first level and 4 second level @test length(adaptive_grid.cells[1].leaves) == 7 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) @test octant == OctantBWG(2,2,m,adaptive_grid.cells[1].b) end + + # second case # x-----------x-----------x # | | | @@ -187,6 +285,29 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) + + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[2]) == OctantBWG(1,(0,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == OctantBWG(2,(4,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == OctantBWG(2,(6,8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[3]) == OctantBWG(1,(0,-4)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(2,(4,-2)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(2,(6,-2)) + + (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + @test length(grid_new.nodes) == 19 + @test length(hnodes) == 4 + + # more complex neighborhoods + grid = Ferrite.generate_simple_disc_grid(Quadrilateral, 6) + grid.cells[2] = Quadrilateral((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1])) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) + + (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + @test length(grid_new.nodes) == 23 + @test length(hnodes) == 4 + ################################################################## ####uniform refinement and coarsening for all cells and levels#### ################################################################## @@ -247,6 +368,24 @@ end @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end end + + # Reproducer test for Fig.3 BWG 11 + grid = generate_grid(Hexahedron,(2,1,1)) + # (a) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[3]) + @test adaptive_grid.cells[2].leaves[3+4] == OctantBWG(2,(0,4,2)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[2].leaves[3+4]) == OctantBWG(2,(8,4,2)) + # (b) Rotate elements topologically + grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[6], grid.cells[1].nodes[7], grid.cells[1].nodes[8], grid.cells[1].nodes[5])) + grid.cells[2] = Hexahedron((grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[8], grid.cells[2].nodes[5], grid.cells[2].nodes[6], grid.cells[2].nodes[7])) + # grid.cells[2] = Hexahedron((grid.cells[2].nodes[1], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[8], grid.cells[2].nodes[6], grid.cells[2].nodes[2], grid.cells[2].nodes[7], grid.cells[2].nodes[5])) How to rotate along diagonal? :) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + @test adaptive_grid.cells[2].leaves[6] == OctantBWG(2,(2,0,2)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,3), adaptive_grid.cells[2].leaves[6]) == OctantBWG(2,(4,-2,2)) end @testset "ForestBWG AbstractGrid Interfacing" begin diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 53a151f7c7..a815864d62 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -105,7 +105,7 @@ function check_and_compute_convergence(dh, u, cellvalues, testatol, forest) end end Ferrite.refine!(forest,marked_cells) - #Ferrite.balanceforest!(forest) + Ferrite.balanceforest!(forest) L2norm, L∞norm end @@ -186,14 +186,14 @@ end grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) - q_gp = compute_fluxes(cellvalues, dh, u); - projector = L2Projector(interpolation, grid_transfered); - q_projected = project(projector, q_gp, qr); + # q_gp = compute_fluxes(cellvalues, dh, u); + # projector = L2Projector(interpolation, grid_transfered); + # q_projected = project(projector, q_gp, qr); L2norm, _ = ConvergenceTestHelper.check_and_compute_convergence(dh, u, cellvalues, 1e-2, adaptive_grid) - vtk_grid("p4est_test$(i).vtu",dh) do vtk - vtk_point_data(vtk,dh,u) - vtk_point_data(vtk, projector, q_projected, "q") - end + # vtk_grid("p4est_test$(i).vtu",dh) do vtk + # vtk_point_data(vtk,dh,u) + # vtk_point_data(vtk, projector, q_projected, "q") + # end i += 1 end end From 061172945ef263b01ac640b7c6708ab3d65b8730 Mon Sep 17 00:00:00 2001 From: Dennis Ogiermann Date: Thu, 11 Apr 2024 20:46:27 +0200 Subject: [PATCH 102/143] p4est constraint handler integration (#900) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Baseline with bug. * Test 1 * Fix derp and update test. * ... * Cleanup * Maxis suggestion * Update src/Dofs/ConstraintHandler.jl Co-authored-by: Maximilian Köhler --------- Co-authored-by: Maximilian Köhler --- docs/src/literate-tutorials/adaptivity.jl | 26 ++++----- .../src/literate-tutorials/heat_adaptivity.jl | 35 +++++++----- src/Adaptivity/AdaptiveCells.jl | 3 +- src/Dofs/ConstraintHandler.jl | 44 +++++++++++++++ src/Dofs/DofHandler.jl | 50 +++++++++-------- src/Export/VTK.jl | 4 +- src/Ferrite.jl | 1 + src/Grid/ncgrid.jl | 55 +++++++++++++++++++ src/L2_projection.jl | 12 ++-- src/exports.jl | 1 + test/test_p4est.jl | 16 +++--- test/test_p4est_example.jl | 17 +++--- 12 files changed, 182 insertions(+), 82 deletions(-) create mode 100644 src/Grid/ncgrid.jl diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index e72c9e6ea6..9b6f13163a 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -61,7 +61,7 @@ function assemble_global!(K, f, a, dh, cellvalues, material) return K, f end -function solve(grid,hnodes) +function solve(grid) dim = 2 order = 1 # linear interpolation ip = Lagrange{RefQuadrilateral, order}()^dim # vector valued interpolation @@ -70,19 +70,12 @@ function solve(grid,hnodes) dh = DofHandler(grid) add!(dh, :u, ip) - dh, vdict, edict, fdict = Ferrite.__close!(dh); + close!(dh); ch = ConstraintHandler(dh) + add!(ch, Ferrite.ConformityConstraint(:u)) add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.01, 2)) - # One set of linear contraints per hanging node - for (hdof,mdof) in hnodes - # One constraint per component - for d in 1:dim - lc = AffineConstraint(vdict[1][hdof]+d-1,[vdict[1][m]+d-1 => 0.5 for m in mdof],0.0) - add!(ch,lc) - end - end close!(ch); K = create_sparsity_pattern(dh,ch) @@ -92,7 +85,7 @@ function solve(grid,hnodes) apply!(K, f, ch) u = K \ f; apply!(u,ch) - return u,dh,ch,cellvalues,vdict + return u,dh,ch,cellvalues end function compute_fluxes(u,dh) @@ -139,10 +132,10 @@ function solve_adaptive(initial_grid) i = 1 grid = initial_grid while !finished - transfered_grid, hnodes = Ferrite.creategrid(grid) - u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) + transfered_grid = Ferrite.creategrid(grid) + u,dh,ch,cv = solve(transfered_grid) σ_gp, σ_gp_sc = compute_fluxes(u,dh) - projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid; hnodes=hnodes) + projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid) σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) cells_to_refine = Int[] error_arr = Float64[] @@ -166,14 +159,15 @@ function solve_adaptive(initial_grid) vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") vtk_cell_data(vtk, error_arr, "error") end + Ferrite.refine!(grid,cells_to_refine) - transfered_grid, hnodes = Ferrite.creategrid(grid) vtk_grid("unbalanced.vtu", dh) do vtk end + Ferrite.balanceforest!(grid) - transfered_grid, hnodes = Ferrite.creategrid(grid) vtk_grid("balanced.vtu", dh) do vtk end + i += 1 if isempty(cells_to_refine) finished = true diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index 254dd9519f..9a55ad50b7 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -1,5 +1,13 @@ using Ferrite, FerriteGmsh, SparseArrays -grid = generate_grid(Quadrilateral, (4,4)); +grid = generate_grid(Quadrilateral, (8,8)); +function random_deformation_field(x) + if any(x .≈ -1.0) || any(x .≈ 1.0) + return x + else + Vec{2}(x .+ (rand(2).-0.5)*0.15) + end +end +transform_coordinates!(grid, random_deformation_field) grid = ForestBWG(grid,10) analytical_solution(x) = atan(2*(norm(x)-0.5)/0.02) @@ -45,7 +53,7 @@ function assemble_global!(K, f, a, dh, cellvalues) return K, f end -function solve(grid, hnodes) +function solve(grid) dim = 2 order = 1 # linear interpolation ip = Lagrange{RefQuadrilateral, order}() # vector valued interpolation @@ -54,17 +62,14 @@ function solve(grid, hnodes) dh = DofHandler(grid) add!(dh, :u, ip) - dh, vdict, edict, fdict = Ferrite.__close!(dh); + close!(dh); ch = ConstraintHandler(dh) + add!(ch, ConformityConstraint(:u)) add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfaceset(grid, "left"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfaceset(grid, "bottom"), (x, t) -> 0.0)) - for (hdof,mdof) in hnodes - lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) - add!(ch,lc) - end close!(ch); K = create_sparsity_pattern(dh,ch) @@ -74,7 +79,7 @@ function solve(grid, hnodes) apply!(K, f, ch) u = K \ f; apply!(u,ch) - return u,dh,ch,cellvalues,vdict + return u,dh,ch,cellvalues end function compute_fluxes(u,dh) @@ -122,10 +127,12 @@ function solve_adaptive(initial_grid) pvd = paraview_collection("heat_amr.pvd"); while !finished && i<=10 @show i - transfered_grid, hnodes = Ferrite.creategrid(grid) - u,dh,ch,cv,vdict = solve(transfered_grid,hnodes) + transfered_grid = Ferrite.creategrid(grid) + vtk_grid("heat_amr-grid_$i", transfered_grid) do vtk + end + u,dh,ch,cv = solve(transfered_grid) σ_gp, σ_gp_sc = compute_fluxes(u,dh) - projector = L2Projector(Lagrange{RefQuadrilateral, 1}(), transfered_grid; hnodes=hnodes) + projector = L2Projector(Lagrange{RefQuadrilateral, 1}(), transfered_grid) σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) cells_to_refine = Int[] error_arr = Float64[] @@ -152,10 +159,10 @@ function solve_adaptive(initial_grid) vtk_cell_data(vtk, error_arr, "error") pvd[i] = vtk end - Ferrite.refine!(grid,cells_to_refine) - transfered_grid, hnodes = Ferrite.creategrid(grid) + + Ferrite.refine!(grid, cells_to_refine) Ferrite.balanceforest!(grid) - transfered_grid, hnodes = Ferrite.creategrid(grid) + i += 1 if isempty(cells_to_refine) finished = true diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/AdaptiveCells.jl index cbb71a81a7..272772b19d 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/AdaptiveCells.jl @@ -671,13 +671,12 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} # Phase 5: Generate grid and haning nodes facesets = reconstruct_facesets(forest) #TODO edge, node and cellsets - grid = Grid(cells,nodes_physical .|> Node, facesets=facesets) hnodes = hangingnodes(forest, nodeids, nodeowners) hnodes_dedup = Dict{Int64, Vector{Int64}}() for (constrained,constainers) in hnodes hnodes_dedup[nodeids_dedup[constrained]] = [nodeids_dedup[constainer] for constainer in constainers] end - return grid, hnodes_dedup + return NonConformingGrid(cells, nodes_physical .|> Node, facesets=facesets, conformity_info=hnodes_dedup) end function reconstruct_facesets(forest::ForestBWG{dim}) where dim diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index 87448e0247..ae3417cfd1 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -98,6 +98,49 @@ function ConstraintHandler(dh::AbstractDofHandler) ) end +""" + This constraint can be passed to the constraint handler when working with non-conforming meshes to + add the affine constraints required to make the associated interpolation conforming. + + For a full example visit the AMR tutorial. +""" +struct ConformityConstraint + field_name::Symbol +end + +function add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:Grid}}, cc::ConformityConstraint) + @warn "Trying to add conformity constraint to $(cc.field_name) on a conforming grid. Skipping." +end + +function add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:NonConformingGrid}}, cc::ConformityConstraint) + @assert length(ch.dh.field_names) == 1 "Multiple fields not supported yet." + @assert cc.field_name ∈ ch.dh.field_names "Field $(cc.field_name) not found in provided dof handler. Available fields are $(ch.dh.field_names)." + # One set of linear contraints per hanging node + for sdh in ch.dh.subdofhandlers + field_idx = _find_field(sdh, cc.field_name) + field_idx !== nothing && _add_conformity_constraint(ch, field_idx, sdh.field_interpolations[field_idx]) + end +end + +function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::ScalarInterpolation) + for (hdof,mdof) in ch.dh.grid.conformity_info + @debug @assert length(mdof) == 2 + lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof],[ch.dh.vertexdicts[field_index][m] => 0.5 for m in mdof], 0.0) + add!(ch,lc) + end +end + +function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::VectorizedInterpolation{vdim}) where vdim + for (hdof,mdof) in ch.dh.grid.conformity_info + @debug @assert length(mdof) == 2 + # One constraint per component + for vd in 1:vdim + lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof]+vd-1,[ch.dh.vertexdicts[field_index][m]+vd-1 => 0.5 for m in mdof], 0.0) # TODO change for other interpolation types than linear + add!(ch,lc) + end + end +end + """ RHSData @@ -218,6 +261,7 @@ function close!(ch::ConstraintHandler) i == 0 && continue icoeffs = ch.dofcoefficients[i] if !(icoeffs === nothing || isempty(icoeffs)) + @debug @info "Nested affine constraint detected for $coeffs and $i" error("nested affine constraints currently not supported") end end diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index b1cc515290..a8215c422b 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -68,7 +68,7 @@ function _print_field_information(io::IO, sdh::SubDofHandler) end """ - DofHandler(grid::Grid) + DofHandler(grid::AbstractGrid) Construct a `DofHandler` based on `grid`. Supports: - `Grid`s with or without concrete element type (E.g. "mixed" grids with several different element types.) @@ -85,12 +85,25 @@ struct DofHandler{dim,G<:AbstractGrid{dim}} <: AbstractDofHandler closed::ScalarWrapper{Bool} grid::G ndofs::ScalarWrapper{Int} + # Maps from entity to dofs + # `vertexdict` keeps track of the visited vertices. The first dof added to vertex v is + # stored in vertexdict[v]. + vertexdicts::Vector{Vector{Int}} + # `edgedict` keeps track of the visited edges, this will only be used for a 3D problem. + # An edge is uniquely determined by two global vertices, with global direction going + # from low to high vertex number. + edgedicts::Vector{Dict{Tuple{Int,Int}, Int}} + # `facedict` keeps track of the visited faces. We only need to store the first dof we + # add to the face since currently more dofs per face isn't supported. In + # 2D a face (i.e. a line) is uniquely determined by 2 vertices, and in 3D a face (i.e. a + # surface) is uniquely determined by 3 vertices. + facedicts::Vector{Dict{NTuple{dim,Int}, Int}} end function DofHandler(grid::G) where {dim, G <: AbstractGrid{dim}} ncells = getncells(grid) sdhs = SubDofHandler{DofHandler{dim, G}}[] - DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), ScalarWrapper(false), grid, ScalarWrapper(-1)) + DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), ScalarWrapper(false), grid, ScalarWrapper(-1), [Int[]], Dict{Tuple{Int,Int}}[], Dict{NTuple{dim,Int}}[]) end function Base.show(io::IO, ::MIME"text/plain", dh::DofHandler) @@ -300,23 +313,16 @@ function __close!(dh::DofHandler{dim}) where {dim} end numfields = length(dh.field_names) - # NOTE: Maybe it makes sense to store *Index in the dicts instead. + resize!(dh.vertexdicts, numfields) + resize!(dh.edgedicts, numfields) + resize!(dh.facedicts, numfields) - # `vertexdict` keeps track of the visited vertices. The first dof added to vertex v is - # stored in vertexdict[v]. - # TODO: No need to allocate this vector for fields that don't have vertex dofs - vertexdicts = [zeros(Int, getnnodes(get_grid(dh))) for _ in 1:numfields] - - # `edgedict` keeps track of the visited edges, this will only be used for a 3D problem. - # An edge is uniquely determined by two global vertices, with global direction going - # from low to high vertex number. - edgedicts = [Dict{Tuple{Int,Int}, Int}() for _ in 1:numfields] - - # `facedict` keeps track of the visited faces. We only need to store the first dof we - # add to the face since currently more dofs per face isn't supported. In - # 2D a face (i.e. a line) is uniquely determined by 2 vertices, and in 3D a face (i.e. a - # surface) is uniquely determined by 3 vertices. - facedicts = [Dict{NTuple{dim,Int}, Int}() for _ in 1:numfields] + nnodes = getnnodes(get_grid(dh)) + for fi in 1:numfields + dh.vertexdicts[fi] = zeros(Int, nnodes) + dh.edgedicts[fi] = Dict{Tuple{Int,Int}, Int}() + dh.facedicts[fi] = Dict{NTuple{dim,Int}, Int}() + end # Set initial values nextdof = 1 # next free dof to distribute @@ -328,15 +334,15 @@ function __close!(dh::DofHandler{dim}) where {dim} sdh, sdhi, # TODO: Store in the SubDofHandler? nextdof, - vertexdicts, - edgedicts, - facedicts, + dh.vertexdicts, + dh.edgedicts, + dh.facedicts, ) end dh.ndofs[] = maximum(dh.cell_dofs; init=0) dh.closed[] = true - return dh, vertexdicts, edgedicts, facedicts + return dh, dh.vertexdicts, dh.edgedicts, dh.facedicts end diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index 1b537f9da5..5bccabb8ec 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -57,7 +57,7 @@ Return a `DatasetFile` that data can be appended to, see The keyword arguments are forwarded to `WriteVTK.vtk_grid`, see [Data Formatting Options](https://juliavtk.github.io/WriteVTK.jl/stable/grids/syntax/#Data-formatting-options) """ -function WriteVTK.vtk_grid(filename::AbstractString, grid::Grid{dim,C,T}; kwargs...) where {dim,C,T} +function WriteVTK.vtk_grid(filename::AbstractString, grid::Union{Grid{dim,<:Any,T}, NonConformingGrid{dim,<:Any,T}}; kwargs...) where {dim,T} cls = MeshCell[] for cell in getcells(grid) celltype = Ferrite.cell_to_vtkcell(typeof(cell)) @@ -113,7 +113,7 @@ function component_names(::Type{S}) where S return names end -function vtk_nodeset(vtk::WriteVTK.DatasetFile, grid::Grid{dim}, nodeset::String) where {dim} +function vtk_nodeset(vtk::WriteVTK.DatasetFile, grid::AbstractGrid, nodeset::String) where {dim} z = zeros(getnnodes(grid)) z[collect(getnodeset(grid, nodeset))] .= 1.0 vtk_point_data(vtk, z, nodeset) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index c4883339ef..4ff854bf04 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -90,6 +90,7 @@ include("FEValues/face_integrals.jl") # Grid include("Grid/grid.jl") +include("Grid/ncgrid.jl") include("Grid/topology.jl") include("Grid/utils.jl") include("Grid/grid_generators.jl") diff --git a/src/Grid/ncgrid.jl b/src/Grid/ncgrid.jl new file mode 100644 index 0000000000..3265214378 --- /dev/null +++ b/src/Grid/ncgrid.jl @@ -0,0 +1,55 @@ +""" + NonConformingGrid{dim, C<:AbstractCell, T<:Real, CIT} <: AbstractGrid} + +A `NonConformingGrid` is a collection of `Cells` and `Node`s which covers the computational domain, together with Sets of cells, nodes, faces +and assocaited information about the conformity. +There are multiple helper structures to apply boundary conditions or define subdomains. They are gathered in the `cellsets`, `nodesets`, +`facesets`, `edgesets` and `vertexsets`. + +This grid serves as an entry point for non-intrusive adaptive grid libraries. + +# Fields +- `cells::Vector{C}`: stores all cells of the grid +- `nodes::Vector{Node{dim,T}}`: stores the `dim` dimensional nodes of the grid +- `cellsets::Dict{String,Set{Int}}`: maps a `String` key to a `Set` of cell ids +- `nodesets::Dict{String,Set{Int}}`: maps a `String` key to a `Set` of global node ids +- `facesets::Dict{String,Set{FaceIndex}}`: maps a `String` to a `Set` of `Set{FaceIndex} (global_cell_id, local_face_id)` +- `edgesets::Dict{String,Set{EdgeIndex}}`: maps a `String` to a `Set` of `Set{EdgeIndex} (global_cell_id, local_edge_id` +- `vertexsets::Dict{String,Set{VertexIndex}}`: maps a `String` key to a `Set` of local vertex ids +- `conformity_info::CIT`: a container for conformity information +- `boundary_matrix::SparseMatrixCSC{Bool,Int}`: optional, only needed by `onboundary` to check if a cell is on the boundary, see, e.g. Helmholtz example +""" +mutable struct NonConformingGrid{dim,C<:AbstractCell,T<:Real,CIT} <: AbstractGrid{dim} + cells::Vector{C} + nodes::Vector{Node{dim,T}} + # Sets + cellsets::Dict{String,Set{Int}} + nodesets::Dict{String,Set{Int}} + facesets::Dict{String,Set{FaceIndex}} + edgesets::Dict{String,Set{EdgeIndex}} + vertexsets::Dict{String,Set{VertexIndex}} + conformity_info::CIT # TODO refine + # Boundary matrix (faces per cell × cell) + boundary_matrix::SparseMatrixCSC{Bool,Int} +end + +function NonConformingGrid( + cells::Vector{C}, + nodes::Vector{Node{dim,T}}; + cellsets::Dict{String,Set{Int}}=Dict{String,Set{Int}}(), + nodesets::Dict{String,Set{Int}}=Dict{String,Set{Int}}(), + facesets::Dict{String,Set{FaceIndex}}=Dict{String,Set{FaceIndex}}(), + edgesets::Dict{String,Set{EdgeIndex}}=Dict{String,Set{EdgeIndex}}(), + vertexsets::Dict{String,Set{VertexIndex}}=Dict{String,Set{VertexIndex}}(), + conformity_info, + boundary_matrix::SparseMatrixCSC{Bool,Int}=spzeros(Bool, 0, 0) + ) where {dim,C,T} + return NonConformingGrid(cells, nodes, cellsets, nodesets, facesets, edgesets, vertexsets, conformity_info, boundary_matrix) +end + +get_coordinate_type(::NonConformingGrid{dim,C,T}) where {dim,C,T} = Vec{dim,T} + +function transform_coordinates!(g::NonConformingGrid, f::Function) + replace!(n -> Node(f(get_node_coordinate(n))), g.nodes) + return g +end diff --git a/src/L2_projection.jl b/src/L2_projection.jl index 72160c46ae..f87442f176 100644 --- a/src/L2_projection.jl +++ b/src/L2_projection.jl @@ -38,8 +38,7 @@ function L2Projector( grid::AbstractGrid; qr_lhs::QuadratureRule = _mass_qr(func_ip), set = 1:getncells(grid), - geom_ip::Interpolation = default_interpolation(getcelltype(grid, first(set))), - hnodes=nothing, + geom_ip::Interpolation = default_interpolation(getcelltype(grid, first(set))) ) # TODO: Maybe this should not be allowed? We always assume to project scalar entries. @@ -55,14 +54,11 @@ function L2Projector( dh = DofHandler(grid) sdh = SubDofHandler(dh, Set(set)) add!(sdh, :_, func_ip) # we need to create the field, but the interpolation is not used here - dh, vdict, _ = __close!(dh) + close!(dh) ch_hanging = ConstraintHandler(dh) - if hnodes !== nothing - for (hdof,mdof) in hnodes - lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) - add!(ch_hanging,lc) - end + if grid isa NonConformingGrid + add!(ch_hanging, ConformityConstraint(:_)) end close!(ch_hanging); M = _assemble_L2_matrix(fe_values_mass, set, dh, ch_hanging) # the "mass" matrix diff --git a/src/exports.jl b/src/exports.jl index eb36bdb38c..c2c40081b5 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -128,6 +128,7 @@ export collect_periodic_faces!, PeriodicFacePair, AffineConstraint, + ConformityConstraint, update!, apply!, apply_rhs!, diff --git a/test/test_p4est.jl b/test/test_p4est.jl index c1836f951f..b4709b5301 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -175,9 +175,9 @@ end @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,1), o) == OctantBWG(0,(-8,0)) @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,3), o) == OctantBWG(0,(0,-8)) - (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.creategrid(adaptive_grid) @test length(grid_new.nodes) == 9 - @test length(hnodes) == 0 + @test length(grid_new.conformity_info) == 0 grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) @@ -254,9 +254,9 @@ end @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(1,(0,-4)) @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(1,(4,-4)) - (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 - @test length(hnodes) == 4 + @test length(grid_new.conformity_info) == 4 # octree holds now 3 first level and 4 second level @test length(adaptive_grid.cells[1].leaves) == 7 @@ -293,9 +293,9 @@ end @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(2,(4,-2)) @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(2,(6,-2)) - (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 - @test length(hnodes) == 4 + @test length(grid_new.conformity_info) == 4 # more complex neighborhoods grid = Ferrite.generate_simple_disc_grid(Quadrilateral, 6) @@ -304,9 +304,9 @@ end Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) - (grid_new, hnodes) = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.creategrid(adaptive_grid) @test length(grid_new.nodes) == 23 - @test length(hnodes) == 4 + @test length(grid_new.conformity_info) == 4 ################################################################## ####uniform refinement and coarsening for all cells and levels#### diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index a815864d62..158e963c45 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -117,26 +117,23 @@ function solve(dh, ch, cellvalues) apply!(u,ch) end -function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N, hnodes) +function setup_poisson_problem(grid, interpolation, interpolation_geo, qr, N) # Construct Ferrite stuff dh = DofHandler(grid) add!(dh, :u, interpolation) - dh, vdict, edict, fdict = Ferrite.__close!(dh); - + close!(dh); + ch = ConstraintHandler(dh); ∂Ω = union( values(Ferrite.getfacesets(grid))... ); dbc = Dirichlet(:u, ∂Ω, (x, t) -> analytical_solution(x)) add!(ch, dbc); - for (hdof,mdof) in hnodes - lc = AffineConstraint(vdict[1][hdof],[vdict[1][m] => 0.5 for m in mdof],0.0) - add!(ch,lc) - end + add!(ch, ConformityConstraint(:u)) close!(ch); cellvalues = CellValues(qr, interpolation, interpolation_geo); - + return dh, ch, cellvalues end @@ -183,8 +180,8 @@ end # ... and then pray to the gods of convergence. i = 0 while L2norm > 1e-3 && i < 8 - grid_transfered, hnodes = Ferrite.creategrid(adaptive_grid) - dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N, hnodes) + grid_transfered = Ferrite.creategrid(adaptive_grid) + dh, ch, cellvalues = ConvergenceTestHelper.setup_poisson_problem(grid_transfered, interpolation, interpolation_geo, qr, N) u = ConvergenceTestHelper.solve(dh, ch, cellvalues) # q_gp = compute_fluxes(cellvalues, dh, u); # projector = L2Projector(interpolation, grid_transfered); From f57cfe2c2d01422a1ecc51207ec9fca82a17c1dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 30 Apr 2024 16:51:34 +0200 Subject: [PATCH 103/143] [X-PR] p4est edge operations and consistent corner operation (#902) * first version without test of transform_edge * non working transform_edge due to wrong edge axis vector b * add more edge_neighbor tests, change the algorithm according to paper and include bool for transform corner in balancing * probably works * remote and not remote version * change bool flag in balancing according to paper for transform_corner * add transform_corner_remote * start tests for corner, correct docs for corner and start 3D transform tests * add first rounds of 3D tests * remove unnecessary dim parametrization and start some tests for creategrid * contains_face for 3d case by checking sface centroid in plane of mface * three dimensional interoctree branch in creategrid * first working version with edge deduplication and rotation invariance; reorder some stuff in creategrid and more tests needed * cleanup * possibleneighbors 3D dispatch * intratree balancing in 3D works and is tested * first inter octree 3D balancing, not sure if it works and if not where the problem lies * rotation tests for balancing; seems to work * start of hanging edges * initial hanging edges with two small tests * failing interoctree hanging nodes detection for rated; hanging faces need rotation * rotation and easy tests in two and three dimensions * bit of docs, one more test and a bit of code review * more review suggestions * constants for loop * use iscenter in exlusive edge neighborhood hanging nodes branch * sane renaming for *_neighbor in hangingnodes; error branch for hanging edge; more strict hanging nodes test * arrange balancing in functions * delete unnecessary function * rename file to BWG.jl * add corner balancing in 2D * regression test for corner balancing 2D * try to fix 1.6 ci * regression test but rotated * edge balancing over new topologic connection * add todo statement for corner balancing in 3D * maybe final balancing version --- src/Adaptivity/{AdaptiveCells.jl => BWG.jl} | 786 ++++++++++++++------ src/Export/VTK.jl | 2 +- src/Ferrite.jl | 2 +- test/test_p4est.jl | 568 +++++++++++++- 4 files changed, 1098 insertions(+), 260 deletions(-) rename src/Adaptivity/{AdaptiveCells.jl => BWG.jl} (64%) diff --git a/src/Adaptivity/AdaptiveCells.jl b/src/Adaptivity/BWG.jl similarity index 64% rename from src/Adaptivity/AdaptiveCells.jl rename to src/Adaptivity/BWG.jl index 272772b19d..4ec8ac1959 100644 --- a/src/Adaptivity/AdaptiveCells.jl +++ b/src/Adaptivity/BWG.jl @@ -6,6 +6,10 @@ abstract type AbstractAdaptiveGrid{dim} <: AbstractGrid{dim} end abstract type AbstractAdaptiveCell{refshape <: AbstractRefShape} <: AbstractCell{refshape} end +const ncorners_face3D = 4 +const ncorners_face2D = 2 +const ncorners_edge = ncorners_face2D + _maxlevel = [30,19] function set_maxlevel(dim::Integer,maxlevel::Integer) @@ -46,7 +50,7 @@ end #OctantBWG(dim::Int,l::Int,m::Int,b::Int=_maxlevel[dim-1]) = OctantBWG(dim,l,m,b) #OctantBWG(dim::Int,l::Int,m::Int,b::Int32) = OctantBWG(dim,l,m,b) #OctantBWG(dim::Int,l::Int32,m::Int,b::Int32) = OctantBWG(dim,l,Int32(m),b) -function OctantBWG(level::Int,coords::NTuple) +function OctantBWG(level::T,coords::NTuple) where T <: Integer dim = length(coords) nnodes = 2^dim OctantBWG{dim,nnodes,eltype(coords)}(level,coords) @@ -179,6 +183,8 @@ function edge(octant::OctantBWG{3}, e::Integer, b::Integer) return ntuple(i->vertex(octant,cornerid[i], b),2) end +edges(octant::OctantBWG{3}, b::Integer) = ntuple(i->edge(octant,i,b),12) + """ boundaryset(o::OctantBWG{2}, i::Integer, b::Integer implements two dimensional boundaryset table from Fig.4.1 [IBWG2015](@citet) @@ -459,6 +465,7 @@ function refine!(forest::ForestBWG, cellid::Integer) end function refine!(forest::ForestBWG, cellids::Vector{<:Integer}) + sort!(cellids) ncells = getncells(forest) shift = 0 for cellid in cellids @@ -535,7 +542,29 @@ end transform_pointBWG(forest, vertices) = transform_pointBWG.((forest,), first.(vertices), last.(vertices)) +""" + rotation_permutation(::Val{2},r,i) -> i′ +computes based on the rotation indicator `r` ∈ {0,1} and a given corner index `i` ∈ {1,2} the permuted corner index `i′` +""" +function rotation_permutation(r,i) + i′ = r == 0 ? i : 3-i + return i′ +end + +""" + rotation_permutation(f,f′,r,i) -> i′ +computes based on the rotation indicator `r` ∈ {0,...,3} and a given corner index `i` ∈ {1,...,4} the permuted corner index `i′` +See Table 3 and Theorem 2.2 [BWG2011](@citet). +""" +function rotation_permutation(f,f′,r,i) + return 𝒫[𝒬[ℛ[f,f′],r+1],i] +end + #TODO: this function should wrap the LNodes Iterator of [IBWG2015](@citet) +""" + creategrid(forest::ForestBWG) -> NonConformingGrid +Materializes a p4est forest into a NonConformingGrid that can be used as usual to solve a finite element problem. +""" function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} nodes = Vector{Tuple{Int,NTuple{dim,Int32}}}() sizehint!(nodes,getncells(forest)*2^dim) @@ -585,12 +614,12 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} @debug println("Updating face neighbors for octree $k") for (f,fc) in enumerate(_faces) # f in p4est notation f_axis_index, f_axis_sign = divrem(f-1,2) - face_neighbor = forest.topology.face_face_neighbor[k,_perm[f]] - if length(face_neighbor) == 0 + face_neighbor_ = forest.topology.face_face_neighbor[k,_perm[f]] + if length(face_neighbor_) == 0 continue end - @debug @assert length(face_neighbor) == 1 - k′, f′_ferrite = face_neighbor[1] + @debug @assert length(face_neighbor_) == 1 + k′, f′_ferrite = face_neighbor_[1] f′ = _perminv[f′_ferrite] if k > k′ # Owner tree′ = forest.cells[k′] @@ -614,31 +643,70 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} r = compute_face_orientation(forest,k,f) @debug println(" Matching $fnodes (local) to $fnodes_neighbor (neighbor)") if dim == 2 - if r == 0 # same orientation - for i ∈ 1:2 - if haskey(nodeids, (k′,fnodes_neighbor[i])) - nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[i])] - nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[i]) - end - end - else - for i ∈ 1:2 - if haskey(nodeids, (k′,fnodes_neighbor[3-i])) - nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[3-i])] - nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[3-i]) - end + for i ∈ 1:ncorners_face2D + i′ = rotation_permutation(r,i) + if haskey(nodeids, (k′,fnodes_neighbor[i′])) + nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[i′])] + nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[i′]) end end else - @error "Not implemented for $dim dimensions." + for i ∈ 1:ncorners_face3D + rotated_ξ = rotation_permutation(f′,f,r,i) + if haskey(nodeids, (k′,fnodes_neighbor[i])) + nodeids[(k,fnodes[rotated_ξ])] = nodeids[(k′,fnodes_neighbor[i])] + nodeowners[(k,fnodes[rotated_ξ])] = (k′,fnodes_neighbor[i]) + end + end end end end end end if dim > 2 - #TODO add egde duplication check - @error "Edge deduplication not implemented yet." + # edge neighbors + @debug println("Updating edge neighbors for octree $k") + for (e,ec) in enumerate(edges(root(dim),tree.b)) # e in p4est notation + e_axis_index, e_axis_sign = divrem(e-1,4) #first axis 0 (x), 1 (y), 2(z), second positive or negative direction + edge_neighbor_ = forest.topology.edge_edge_neighbor[k,edge_perm[e]] + if length(edge_neighbor_) == 0 + continue + end + @debug @assert length(edge_neighbor_) == 1 + k′, e′_ferrite = edge_neighbor_[1] + e′ = edge_perm_inv[e′_ferrite] + if k > k′ # Owner + tree′ = forest.cells[k′] + for leaf in tree.leaves + #debugging checks; should be inbounds anyway due to iteration + if e_axis_sign == 1 # positive edge + if leaf.xyz[e_axis_index + 1] < 2^tree.b-2^(tree.b-leaf.l) + @debug println(" Rejecting $leaf") + continue + end + else # negative edge + if leaf.xyz[e_axis_index + 1] > 0 + @debug println(" Rejecting $leaf") + continue + end + end + neighbor_candidate = transform_edge(forest,k′,e′,leaf, false) + # Candidate must be the edge opposite to e' + e′candidate = ((e′ - 1) ⊻ 1) + 1 + enodes = edge(leaf, e , tree.b) + enodes_neighbor = edge(neighbor_candidate, e′candidate, tree′.b) + r = compute_edge_orientation(forest,k,e) + @debug println(" Matching $enodes (local) to $enodes_neighbor (neighbor)") + for i ∈ 1:ncorners_edge + i′ = rotation_permutation(r,i) + if haskey(nodeids, (k′,enodes_neighbor[i′])) + nodeids[(k,enodes[i])] = nodeids[(k′,enodes_neighbor[i′])] + nodeowners[(k,enodes[i])] = (k′,enodes_neighbor[i′]) + end + end + end + end + end end end @@ -680,6 +748,8 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end function reconstruct_facesets(forest::ForestBWG{dim}) where dim + _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm + _perm_inv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv new_facesets = typeof(forest.facesets)() for (facesetname, faceset) in forest.facesets new_faceset = typeof(faceset)() @@ -687,11 +757,11 @@ function reconstruct_facesets(forest::ForestBWG{dim}) where dim pivot_tree = forest.cells[faceidx[1]] last_cellid = faceidx[1] != 1 ? sum(length,@view(forest.cells[1:(faceidx[1]-1)])) : 0 pivot_faceid = faceidx[2] - pivot_face = faces(root(dim),pivot_tree.b)[𝒱₂_perm_inv[pivot_faceid]] + pivot_face = faces(root(dim),pivot_tree.b)[_perm_inv[pivot_faceid]] for (leaf_idx,leaf) in enumerate(pivot_tree.leaves) for (leaf_face_idx,leaf_face) in enumerate(faces(leaf,pivot_tree.b)) if contains_face(pivot_face,leaf_face) - ferrite_leaf_face_idx = 𝒱₂_perm[leaf_face_idx] + ferrite_leaf_face_idx = _perm[leaf_face_idx] push!(new_faceset,FaceIndex(last_cellid+leaf_idx,ferrite_leaf_face_idx)) end end @@ -702,15 +772,20 @@ function reconstruct_facesets(forest::ForestBWG{dim}) where dim return new_facesets end +""" + hangingnodes(forest,nodeids,nodeowners) +Constructs a map from constrained nodeids to the ones that constraint +""" function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim _perm = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm _perminv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv + facetable = dim == 2 ? 𝒱₂ : 𝒱₃ opposite_face = dim == 2 ? opposite_face_2 : opposite_face_3 - #hnodes = Dict{Tuple{Int,NTuple{dim,Int32}},Vector{Tuple{Int,NTuple{dim,Int32}}}}() hnodes = Dict{Int,Vector{Int}}() for (k,tree) in enumerate(forest.cells) rootfaces = faces(root(dim),tree.b) for (l,leaf) in enumerate(tree.leaves) + c̃ = child_id(leaf,tree.b) if leaf == root(dim) continue end @@ -725,27 +800,90 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim if neighbor_candidate_idx !== nothing neighbor_candidate_faces = faces(neighbor_candidate,tree.b) nf = findfirst(x->x==pface,neighbor_candidate_faces) - #hnodes[(k,c)] = [(k,nc) for nc in neighbor_candidate_faces[nf]] hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k,nc)]] for nc in neighbor_candidate_faces[nf]] + if dim > 2 + vs = vertices(leaf,tree.b) + for ξ ∈ 1:ncorners_face3D + c′ = facetable[pface_i, ξ] + if c′ ∉ (c̃,c) + neighbor_candidate_edges = edges(neighbor_candidate,tree.b) + ne = findfirst(x->iscenter(vs[c′],x),neighbor_candidate_edges) + if ne !== nothing + hnodes[nodeids[nodeowners[(k,vs[c′])]]] = [nodeids[nodeowners[(k,ne)]] for ne in neighbor_candidate_edges[ne]] + end + end + end + end break end else #interoctree branch for (ri,rf) in enumerate(rootfaces) - face_neighbor = forest.topology.face_face_neighbor[k,_perm[ri]] - if length(face_neighbor) == 0 + face_neighbor_ = forest.topology.face_face_neighbor[k,_perm[ri]] + if length(face_neighbor_) == 0 continue end if contains_face(rf, pface) - k′ = face_neighbor[1][1] - ri′ = _perminv[face_neighbor[1][2]] + k′ = face_neighbor_[1][1] + ri′ = _perminv[face_neighbor_[1][2]] interoctree_neighbor = transform_face(forest, k′, ri′, neighbor_candidate) interoctree_neighbor_candidate_idx = findfirst(x->x==interoctree_neighbor,forest.cells[k′].leaves) if interoctree_neighbor_candidate_idx !== nothing + r = compute_face_orientation(forest,k,pface_i) neighbor_candidate_faces = faces(neighbor_candidate,forest.cells[k′].b) transformed_neighbor_faces = faces(interoctree_neighbor,forest.cells[k′].b) - nf = findfirst(x->x==pface,neighbor_candidate_faces) - #hnodes[(k,c)] = [(k′,nc) for nc in transformed_neighbor_faces[nf]] - hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k′,nc)]] for nc in transformed_neighbor_faces[nf]] + + fnodes = transformed_neighbor_faces[ri′] + vs = vertices(leaf,tree.b) + if dim > 2 + rotated_ξ = ntuple(i->rotation_permutation(ri′,ri,r,i),ncorners_face3D) + else + rotated_ξ = ntuple(i->rotation_permutation(r,i),ncorners_face2D) + end + hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k′,fnodes[ξ])]] for ξ in rotated_ξ] + + if dim > 2 + for ξ in rotated_ξ + c′ = facetable[pface_i, ξ] + if c′ ∉ (c̃,c) + neighbor_candidate_edges = edges(interoctree_neighbor,tree.b) + ne = findfirst(x->iscenter(vs[c′],x),neighbor_candidate_edges) + if ne !== nothing + hnodes[nodeids[nodeowners[(k,vs[c′])]]] = [nodeids[nodeowners[(k,ne)]] for ne in neighbor_candidate_edges[ne]] + end + end + end + end + break + end + end + end + end + end + end + if dim > 2 + parentedges = edges(parent_,tree.b) + for (pedge_i, pedge) in enumerate(parentedges) + if iscenter(c,pedge) #hanging node candidate + neighbor_candidate = edge_neighbor(parent_, pedge_i, tree.b) + for (ri,re) in enumerate(edges(root(dim),tree.b)) + edge_neighbor_ = forest.topology.edge_edge_neighbor[k,edge_perm[ri]] + if length(edge_neighbor_) == 0 + continue + end + if contains_edge(re, pedge) + k′ = edge_neighbor_[1][1] + ri′ = edge_perm_inv[edge_neighbor_[1][2]] + interoctree_neighbor = transform_edge(forest, k′, ri′, neighbor_candidate, true) + interoctree_neighbor_candidate_idx = findfirst(x->x==interoctree_neighbor,forest.cells[k′].leaves) + if interoctree_neighbor_candidate_idx !== nothing + neighbor_candidate_edges = edges(neighbor_candidate,forest.cells[k′].b) + transformed_neighbor_edges = edges(interoctree_neighbor,forest.cells[k′].b) + ne = findfirst(x->iscenter(c,x),neighbor_candidate_edges) + if ne !== nothing + hnodes[nodeids[nodeowners[(k,c)]]] = [nodeids[nodeowners[(k′,nc)]] for nc in transformed_neighbor_edges[ne]] + else + @error "things are messed up for hanging edge constraint interoctree at octree $k edge $(_perm[ri])" + end break end end @@ -759,58 +897,148 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim return hnodes end +function balance_corner(forest,k′,c′,o,s) + o.l == 1 && return # no balancing needed for pivot octant level == 1 + o′ = transform_corner(forest,k′,c′,o,false) + s′ = transform_corner(forest,k′,c′,s,true) #TODO verify the bool here; I think it's correct + neighbor_tree = forest.cells[k′] + if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves + if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves + refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) + end + end +end + +function balance_face(forest,k′,f′,o,s) + o.l == 1 && return # no balancing needed for pivot octant level == 1 + o′ = transform_face(forest,k′,f′,o) + s′ = transform_face(forest,k′,f′,s) + neighbor_tree = forest.cells[k′] + if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves + if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves + refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) + end + end +end + +function balance_edge(forest,k′,e′,o,s) + o.l == 1 && return # no balancing needed for pivot octant level == 1 + o′ = transform_edge(forest,k′,e′,o,false) + s′ = transform_edge(forest,k′,e′,s,true) + neighbor_tree = forest.cells[k′] + if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves + if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves + refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) + end + end +end """ + balanceforest!(forest) Algorithm 17 of [BWG2011](@citet) -TODO need further work for dimension agnostic case """ function balanceforest!(forest::ForestBWG{dim}) where dim perm_face = dim == 2 ? 𝒱₂_perm : 𝒱₃_perm perm_face_inv = dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv perm_corner = dim == 2 ? node_map₂ : node_map₃ perm_corner_inv = dim == 2 ? node_map₂_inv : node_map₃_inv - for k in 1:length(forest.cells) - tree = forest.cells[k] - balanced = balancetree(tree) - forest.cells[k] = balanced - root_ = root(dim) - for (o_i, o) in enumerate(forest.cells[k].leaves) - ss = possibleneighbors(o,o.l,tree.b,;insidetree=false) - isinside = inside.(ss,(tree.b,)) - notinsideidx = findall(.! isinside) - if !isempty(notinsideidx) - for s_i in notinsideidx - s = ss[s_i] - if s_i <= 4 #corner neighbor, only true for 2D see possibleneighbors - cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] - isempty(cc) && continue - @assert length(cc) == 1 # FIXME there can be more than 1 vertex neighbor - cc = cc[1] - k′, c′ = cc[1], perm_corner_inv[cc[2]] - o′ = transform_corner(forest,k′,c′,o) - s′ = transform_corner(forest,k′,c′,s) - neighbor_tree = forest.cells[cc[1]] - if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves - if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves - refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) - #else - # refine!(tree,o) + root_ = root(dim) + nrefcells = 0 + while nrefcells - getncells(forest) != 0 + for k in 1:length(forest.cells) + tree = forest.cells[k] + rootfaces = faces(root_,tree.b) + rootedges = dim == 3 ? edges(root_,tree.b) : nothing #TODO change + rootvertices = vertices(root_,tree.b) + balanced = balancetree(tree) + forest.cells[k] = balanced + nrefcells = getncells(forest) + for (o_i, o) in enumerate(forest.cells[k].leaves) + ss = possibleneighbors(o,o.l,tree.b,;insidetree=false) + isinside = inside.(ss,(tree.b,)) + notinsideidx = findall(.! isinside) + if !isempty(notinsideidx) + # s_i encodes the type of neighborhood, since it is the index of possibleneighbors + # see the docs of this function + for s_i in notinsideidx + s = ss[s_i] + if dim == 2 # need more clever s_i encoding + if s_i <= 4 #corner neighbor, only true for 2D see possibleneighbors + cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] + participating_faces_idx = findall(x->any(x .== s_i),𝒱₂) #TODO! optimize by using inverted table + pivot_faces = faces(o,tree.b) + if isempty(cc) + # the branch below checks if we are in a newly introduced topologic tree connection + # by checking if the corner neighbor is only accesible by transforming through a face + # TODO: enable a bool that either activates or deactivates the balancing over a corner + for face_idx in participating_faces_idx + face_idx = face_idx[1] + contained = contains_face(rootfaces[face_idx],pivot_faces[face_idx]) + if contained + fc = forest.topology.face_face_neighbor[k,perm_face[face_idx]] + isempty(fc) && continue + @assert length(fc) == 1 + fc = fc[1] + k′, f′ = fc[1], perm_face_inv[fc[2]] + balance_face(forest,k′,f′,o,s) + end + end + continue + else + @assert length(cc) == 1 + !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue + cc = cc[1] + k′, c′ = cc[1], perm_corner_inv[cc[2]] + balance_corner(forest,k′,c′,o,s) + end + else # face neighbor, only true for 2D + s_i -= 4 + fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] + isempty(fc) && continue + @assert length(fc) == 1 + fc = fc[1] + k′, f′ = fc[1], perm_face_inv[fc[2]] + balance_face(forest,k′,f′,o,s) end - end - else # face neighbor, only true for 2D - s_i -= 4 - fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] - isempty(fc) && continue - @debug @assert length(fc) == 1 - fc = fc[1] - k′, f′ = fc[1], perm_face_inv[fc[2]] - o′ = transform_face(forest,k′,f′,o) - s′ = transform_face(forest,k′,f′,s) - neighbor_tree = forest.cells[fc[1]] - if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves - if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves - refine!(neighbor_tree,parent(parent(s′,neighbor_tree.b),neighbor_tree.b)) - #else - # refine!(tree,o) + else #TODO collapse this 3D branch with more clever s_i encoding into the 2D branch + if s_i <= 8 #corner neighbor, only true for 2D see possibleneighbors + #TODO a check of new introduced corner neighbors aka corner balancing, see 2D branch + cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] + isempty(cc) && continue + @assert length(cc) == 1 # FIXME there can be more than 1 vertex neighbor + !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue + cc = cc[1] + k′, c′ = cc[1], perm_corner_inv[cc[2]] + balance_corner(forest,k′,c′,o,s) + elseif 8 < s_i <= 14 + s_i -= 8 + fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] + isempty(fc) && continue + @assert length(fc) == 1 + fc = fc[1] + k′, f′ = fc[1], perm_face_inv[fc[2]] + balance_face(forest,k′,f′,o,s) + else + s_i -= 14 + ec = forest.topology.edge_edge_neighbor[k,edge_perm[s_i]] + pivot_edge = edge(o,s_i,tree.b) + contained_face = findall(x->face_contains_edge(x,pivot_edge),rootfaces) + if !isempty(contained_face) && !contains_edge(rootedges[s_i],pivot_edge) #check if pivot edge in interior of rootface and not octree edge + for face_idx in contained_face + fc = forest.topology.face_face_neighbor[k,perm_face[face_idx]] + isempty(fc) && continue + @assert length(fc) == 1 + fc = fc[1] + k′, f′ = fc[1], perm_face_inv[fc[2]] + balance_face(forest,k′,f′,o,s) + end + continue + end + isempty(ec) && continue + @assert length(ec) == 1 + !contains_edge(rootedges[s_i],pivot_edge) && continue + ec = ec[1] + k′, e′ = ec[1], edge_perm_inv[ec[2]] + balance_edge(forest,k′,e′,o,s) end end end @@ -818,11 +1046,6 @@ function balanceforest!(forest::ForestBWG{dim}) where dim end end end - #for k in 1:length(forest.cells) - # tree = forest.cells[k] - # balanced = balancetree(tree) - # forest.cells[k] = balanced - #end end """ @@ -883,7 +1106,11 @@ function siblings(o::OctantBWG,b;include_self=false) return siblings end -# TODO make dimension agnostic +""" + possibleneighbors(o::OctantBWG{2},l,b;insidetree=true) +Returns a list of possible neighbors, where the first four are corner neighbors that are exclusively connected via a corner. +The other four possible neighbors are face neighbors. +""" function possibleneighbors(o::OctantBWG{2},l,b;insidetree=true) neighbors = ntuple(8) do i if i > 4 @@ -899,6 +1126,29 @@ function possibleneighbors(o::OctantBWG{2},l,b;insidetree=true) return neighbors end +""" + possibleneighbors(o::OctantBWG{3},l,b;insidetree=true) +Returns a list of possible neighbors, where the first eight are corner neighbors that are exclusively connected via a corner. +After the first eight corner neighbors, the 6 possible face neighbors follow and after them, the edge neighbors. +""" +function possibleneighbors(o::OctantBWG{3},l,b;insidetree=true) + neighbors = ntuple(26) do i + if 8 < i ≤ 14 + j = i - 8 + face_neighbor(o,j,b) + elseif 14 < i ≤ 26 + j = i - 14 + edge_neighbor(o,j,b) + else + corner_neighbor(o,i,b) + end + end + if insidetree + neighbors = filter(x->inside(x,b),neighbors) + end + return neighbors +end + """ isancestor(o1,o2,b) -> Bool Is o2 an ancestor of o1 @@ -918,7 +1168,6 @@ function isancestor(o1,o2,b) return ancestor end -# TODO verify and generalize function contains_face(mface::Tuple{Tuple{T1,T1},Tuple{T1,T1}},sface::Tuple{Tuple{T2,T2},Tuple{T2,T2}}) where {T1<:Integer,T2<:Integer} if mface[1][1] == sface[1][1] && mface[2][1] == sface[2][1] # vertical return mface[1][2] ≤ sface[1][2] ≤ sface[2][2] ≤ mface[2][2] @@ -929,6 +1178,42 @@ function contains_face(mface::Tuple{Tuple{T1,T1},Tuple{T1,T1}},sface::Tuple{Tupl end end +# currently checking if sface centroid lies in mface +# TODO should be checked if applicaple in general, I guess yes +function contains_face(mface::NTuple{4,Tuple{T1,T1,T1}}, sface::NTuple{4,Tuple{T2,T2,T2}}) where {T1<:Integer,T2<:Integer} + sface_center = center(sface) + lower_left = ntuple(i->minimum(getindex.(mface,i)),3) + top_right = ntuple(i->maximum(getindex.(mface,i)),3) + if (lower_left[1] ≤ sface_center[1] ≤ top_right[1]) && (lower_left[2] ≤ sface_center[2] ≤ top_right[2]) && (lower_left[3] ≤ sface_center[3] ≤ top_right[3]) + return true + else + return false + end +end + +function face_contains_edge(f::NTuple{4,Tuple{T1,T1,T1}},e::Tuple{Tuple{T2,T2,T2},Tuple{T2,T2,T2}}) where {T1<:Integer,T2<:Integer} + edge_center = center(e) + lower_left = ntuple(i->minimum(getindex.(f,i)),3) + top_right = ntuple(i->maximum(getindex.(f,i)),3) + if (lower_left[1] ≤ edge_center[1] ≤ top_right[1]) && (lower_left[2] ≤ edge_center[2] ≤ top_right[2]) && (lower_left[3] ≤ edge_center[3] ≤ top_right[3]) + return true + else + return false + end +end + +function contains_edge(medge::Tuple{Tuple{T1,T1,T1},Tuple{T1,T1,T1}},sedge::Tuple{Tuple{T2,T2,T2},Tuple{T2,T2,T2}}) where {T1<:Integer,T2<:Integer} + if (medge[1][1] == sedge[1][1] && medge[2][1] == sedge[2][1]) && (medge[1][2] == sedge[1][2] && medge[2][2] == sedge[2][2]) # x1 & x2 aligned + return medge[1][3] ≤ sedge[1][3] ≤ sedge[2][3] ≤ medge[2][3] + elseif (medge[1][1] == sedge[1][1] && medge[2][1] == sedge[2][1]) && (medge[1][3] == sedge[1][3] && medge[2][3] == sedge[2][3])# x1 & x3 aligned + return medge[1][2] ≤ sedge[1][2] ≤ sedge[2][2] ≤ medge[2][2] + elseif (medge[1][2] == sedge[1][2] && medge[2][2] == sedge[2][2]) && (medge[1][3] == sedge[1][3] && medge[2][3] == sedge[2][3])# x2 & x3 aligned + return medge[1][1] ≤ sedge[1][1] ≤ sedge[2][1] ≤ medge[2][1] + else + return false + end +end + function center(pivot_face) centerpoint = ntuple(i->0,length(pivot_face[1])) for c in pivot_face @@ -939,133 +1224,6 @@ end iscenter(c,f) = c == center(f) -#TODO unfinished, isreplaced logic fails -function creategridFB23(forest::ForestBWG{dim}) where dim - celltype = dim < 3 ? Quadrilateral : Hexahedron - opposite_corner = dim < 3 ? opposite_corner_2 : opposite_corner_3 - opposite_face = dim < 3 ? opposite_face_2 : opposite_face_3 - leaves = [Dict{Tuple{Int,Int},celltype}() for i in 1:length(forest.cells)] - isreplaced = zeros(Bool,getncells(forest)*nnodes(forest.cells[1])) - pivot_nodeid = 1 - for (k,tree) in enumerate(forest.cells) - for leaf in tree.leaves - mortonid = morton(leaf,tree.b,tree.b) - _nnodes = nnodes(leaf) - leaves[k][(leaf.l,mortonid)] = celltype(ntuple(i->pivot_nodeid+i-1,_nnodes)) - pivot_nodeid += _nnodes - end - end - for (k,tree) in enumerate(forest.cells) - for leaf in tree.leaves - leaf_mortonid = morton(leaf,tree.b,tree.b) - leaf_vertices = vertices(leaf,tree.b) - leaf_faces = faces(leaf,tree.b) - leaf_nodes = leaves[k][(leaf.l,leaf_mortonid)].nodes - for local_nodeid in 1:nnodes(leaf) - node_neighbor = corner_neighbor(leaf, local_nodeid, tree.b) - if !inside(tree,node_neighbor) - #TODO interoctree :) - continue - end - if node_neighbor.l == tree.b - candidates = (parent(node_neighbor,tree.b), node_neighbor) - elseif node_neighbor.l == 0 - continue - else - candidates = (parent(node_neighbor,tree.b), node_neighbor, children(node_neighbor,tree.b)[opposite_corner[local_nodeid]]) - end - for candidate in candidates - candidate_mortonid = morton(candidate,tree.b,tree.b) - owner = leaf_mortonid < candidate_mortonid - if !owner - continue - end - if haskey(leaves[k],(candidate.l,candidate_mortonid)) - v = vertex(candidate, opposite_corner[local_nodeid], tree.b) - if v == leaf_vertices[local_nodeid] - candidate_nodes = leaves[k][(candidate.l,candidate_mortonid)].nodes - isreplaced[candidate_nodes[opposite_corner[local_nodeid]]] = true - altered_nodetuple = replace(candidate_nodes,candidate_nodes[opposite_corner[local_nodeid]] => leaf_nodes[local_nodeid]) - leaves[k][(candidate.l,candidate_mortonid)] = celltype(altered_nodetuple) - end - end - end - end - for local_faceid in 1:2*dim #true for all hypercubes - _face_neighbor = face_neighbor(leaf, local_faceid, tree.b) - if !inside(tree, _face_neighbor) - #TODO interoctree :) - continue - end - if _face_neighbor.l == tree.b - candidates = (parent(_face_neighbor,tree.b), _face_neighbor) - elseif _face_neighbor.l == 0 - continue - else - kidz = children(_face_neighbor,tree.b) - if local_faceid < 3 - small_c1 = kidz[opposite_face[local_faceid]] - small_c2 = OctantBWG(dim,small_c1.l,morton(small_c1,small_c1.l,tree.b)+2,tree.b) - else #TODO add 3D case - small_c1 = kidz[opposite_face[local_faceid]] - small_c2 = OctantBWG(dim,small_c1.l,morton(small_c1,small_c1.l,tree.b)+1,tree.b) - end - if _face_neighbor.l - 1 != 0 - candidates = (parent(_face_neighbor,tree.b), _face_neighbor, small_c1, small_c2) - else - candidates = (_face_neighbor, small_c1, small_c2) - end - end - for candidate in candidates - candidate_mortonid = morton(candidate,tree.b,tree.b) - owner = leaf_mortonid < candidate_mortonid - if !owner - continue - end - if haskey(leaves[k],(candidate.l,candidate_mortonid)) - neighbor_face = face(candidate, opposite_face[local_faceid], tree.b) - pivot_face = leaf_faces[local_faceid] - contributing_nodes = @view 𝒱₂[local_faceid,:] - contributing_nodes_opposite = @view 𝒱₂[opposite_face[local_faceid],:] - if neighbor_face[1] == pivot_face[1] && neighbor_face[2] == pivot_face[2] - candidate_nodes = leaves[k][(candidate.l,candidate_mortonid)].nodes - altered_nodetuple = candidate_nodes - if candidate_nodes[contributing_nodes_opposite[1]] != leaf_nodes[contributing_nodes[1]] - #isreplaced[candidate_nodes[contributing_nodes_opposite[1]]] = true - altered_nodetuple = replace(altered_nodetuple,candidate_nodes[contributing_nodes_opposite[1]] => leaf_nodes[contributing_nodes[1]]) - end - if candidate_nodes[contributing_nodes_opposite[2]] != leaf_nodes[contributing_nodes[2]] - #isreplaced[candidate_nodes[contributing_nodes_opposite[2]]] = true - altered_nodetuple = replace(altered_nodetuple,candidate_nodes[contributing_nodes_opposite[2]] => leaf_nodes[contributing_nodes[2]]) - end - leaves[k][(candidate.l,candidate_mortonid)] = celltype(altered_nodetuple) - end - end - end - end - end - end - shift = zeros(Int,length(isreplaced)) - for (id,r) in enumerate(isreplaced) - if id == 1 - continue - end - if r - shift[id] = shift[id-1] + 1 - else - shift[id] = shift[id-1] - end - end - for k in 1:length(leaves) - for ((l,m),cell) in leaves[k] - old_nodes = cell.nodes - new_nodes = ntuple(n->old_nodes[n]-shift[old_nodes[n]],length(old_nodes)) - leaves[k][(l,m)] = celltype(new_nodes) - end - end - return leaves -end - function Base.show(io::IO, ::MIME"text/plain", agrid::ForestBWG) println(io, "ForestBWG with ") println(io, " $(getncells(agrid)) cells") @@ -1173,6 +1331,10 @@ face_neighbor(o::OctantBWG{dim,N,T1}, f::T2, b::T3) where {dim,N,T1<:Integer,T2< reference_faces_bwg(::Type{RefHypercube{2}}) = ((1,3) , (2,4), (1,2), (3,4)) reference_faces_bwg(::Type{RefHypercube{3}}) = ((1,3,5,7) , (2,4,6,8), (1,2,5,6), (3,4,7,8), (1,2,3,4), (5,6,7,8)) # p4est consistent ordering +reference_edges_bwg(::Type{RefHypercube{3}}) = ((𝒰[1,1],𝒰[1,2]), (𝒰[2,1],𝒰[2,2]), (𝒰[3,1],𝒰[3,2]), + (𝒰[4,1],𝒰[4,2]), (𝒰[5,1],𝒰[5,2]), (𝒰[6,1],𝒰[6,2]), + (𝒰[7,1],𝒰[7,2]), (𝒰[8,1],𝒰[8,2]), (𝒰[9,1],𝒰[9,2]), + (𝒰[10,1],𝒰[10,2]), (𝒰[11,1],𝒰[11,2]), (𝒰[12,1],𝒰[12,2])) # TODO maybe remove, unnecessary, can directly use the table # reference_faces_bwg(::Type{RefHypercube{3}}) = ((1,3,7,5) , (2,4,8,6), (1,2,6,5), (3,4,8,7), (1,2,4,4), (5,6,8,7)) # Note that this does NOT follow P4est order! """ @@ -1200,6 +1362,32 @@ function compute_face_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{dim,<:Any, end end +""" + compute_edge_orientation(forest::ForestBWG, k::Integer, e::Integer) +Slow implementation for the determination of the edge orientation of edge `e` from octree `k` following definition below Table 3 [BWG2011](@citet). + +TODO use some table? +""" +function compute_edge_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{3,<:Any,T2}}, k::T1, e::T1) where {T1,T2} + n_perm = node_map₃ + n_perminv = node_map₃_inv + e_perm = edge_perm + e_perminv = edge_perm_inv + + e_ferrite = e_perm[e] + k′, e′_ferrite = forest.topology.edge_edge_neighbor[k,e_ferrite][1] + e′ = e_perminv[e′_ferrite] + refedgenodes = reference_edges_bwg(RefHypercube{3}) + nodes_e = ntuple(i->forest.cells[k].nodes[n_perm[refedgenodes[e][i]]],length(refedgenodes[e])) + nodes_e′ = ntuple(i->forest.cells[k′].nodes[n_perm[refedgenodes[e′][i]]],length(refedgenodes[e′])) + if nodes_e == nodes_e′ + s = T2(0) + else + s = T2(1) + end + return s +end + """ transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T1,T2} transform_face_remote(forest::ForestBWG, f::FaceIndex, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T2} @@ -1358,55 +1546,147 @@ end transform_face(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face(forest,f[1],f[2],oct) """ - transform_corner(forest,k,c',oct) - transform_corner(forest,v::VertexIndex,oct) + transform_corner(forest,k,c',oct,inside::Bool) + transform_corner(forest,v::VertexIndex,oct,inside::Bool) -Algorithm 12 in [BWG2011](@citet) to transform corner into different octree coordinate system -Note: in Algorithm 12 is c as a argument, but it's never used, therefore I removed it +Algorithm 12 but with flipped logic in [BWG2011](@citet) to transform corner into different octree coordinate system +Implements flipped logic in the sense of pushing the Octant `oct` through vertex v and stays within octree coordinate system `k`. """ -function transform_corner(forest::ForestBWG,k::T1,c′::T1,oct::OctantBWG{dim,N,T2}) where {dim,N,T1<:Integer,T2<:Integer} +function transform_corner(forest::ForestBWG,k::T1,c::T1,oct::OctantBWG{dim,N,T2},inside::Bool) where {dim,N,T1<:Integer,T2<:Integer} + _perm = dim == 2 ? node_map₂ : node_map₃ + _perminv = dim == 2 ? node_map₂_inv : node_map₃_inv + k′, c′ = forest.topology.vertex_vertex_neighbor[k,_perm[c]][1] + k′, c′ = forest.topology.vertex_vertex_neighbor[k′,c′][1] #get the corner connection of neighbor to pivot oct + c′ = _perminv[c′] # make a dispatch that returns only the coordinates? b = forest.cells[k].b l = oct.l; g = 2^b - 2^(b-l) - _inside = inside(forest.cells[k],oct) - h⁻ = _inside ? 0 : -2^(b-l); h⁺ = _inside ? g : 2^b + h⁻ = inside ? 0 : -2^(b-l); h⁺ = inside ? g : 2^b xyz = ntuple(i->((c′-1) & 2^(i-1) == 0) ? h⁻ : h⁺,dim) return OctantBWG(l,xyz) end -transform_corner(forest::ForestBWG,v::VertexIndex,oct::OctantBWG) = transform_corner(forest,v[1],v[2],oct) +transform_corner(forest::ForestBWG,v::VertexIndex,oct::OctantBWG,inside) = transform_corner(forest,v[1],v[2],oct,inside) + +""" + transform_corner_remote(forest,k,c',oct,inside::Bool) + transform_corner_remote(forest,v::VertexIndex,oct,inside::Bool) + +Algorithm 12 in [BWG2011](@citet) to transform corner into different octree coordinate system. +Follows exactly the version of the paper by taking `oct` and looking from the neighbor octree coordinate system (neighboring to `k`,`v`) at `oct`. +""" +function transform_corner_remote(forest::ForestBWG,k::T1,c::T1,oct::OctantBWG{dim,N,T2},inside::Bool) where {dim,N,T1<:Integer,T2<:Integer} + _perm = dim == 2 ? node_map₂ : node_map₃ + _perminv = dim == 2 ? node_map₂_inv : node_map₃_inv + k′, c′ = forest.topology.vertex_vertex_neighbor[k,_perm[c]][1] + c′ = _perminv[c′] + # make a dispatch that returns only the coordinates? + b = forest.cells[k].b + l = oct.l; g = 2^b - 2^(b-l) + h⁻ = inside ? 0 : -2^(b-l); h⁺ = inside ? g : 2^b + xyz = ntuple(i->((c′-1) & 2^(i-1) == 0) ? h⁻ : h⁺,dim) + return OctantBWG(l,xyz) +end + +transform_corner_remote(forest::ForestBWG,v::VertexIndex,oct::OctantBWG,inside) = transform_corner_remote(forest,v[1],v[2],oct,inside) + + +""" + transform_edge_remote(forest,k,e,oct,inside::Bool) + transform_edge_remote(forest,e::Edgeindex,oct,inside::Bool) + +Algorithm 10 in [BWG2011](@citet) to transform edge into different octree coordinate system. +This function looks at the octant from the octree coordinate system of the neighbor that can be found at (k,e) +""" +function transform_edge_remote(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N,T2},inside::Bool) where {N,T1<:Integer,T2<:Integer} + _four = T2(4) + _one = T2(1) + _two = T2(2) + z = zero(T2) + e_perm = edge_perm + e_perminv = edge_perm_inv + + e_ferrite = e_perm[e] + k′, e′_ferrite = forest.topology.edge_edge_neighbor[k,e_ferrite][1] + e′ = e_perminv[e′_ferrite] + #see Algorithm 9, line 18 + 𝐛 = (((e′-_one) ÷ _four), + e′-_one < 4 ? 1 : 0, + e′-_one < 8 ? 2 : 1) + a₀ = ((e-_one) ÷ _four) #subtract 1 based index + a₀ += _one #add it again + b = forest.cells[k].b + l = oct.l; g = _two^b - _two^(b-l) + h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b + s = compute_edge_orientation(forest,k,e) + xyz = zeros(T2,3) + xyz[𝐛[1]+_one] = s*g+(_one-(_two*s))*oct.xyz[a₀] + xyz[𝐛[2]+_one] = ((e′-_one) & 1) == 0 ? h⁻ : h⁺ + xyz[𝐛[3]+_one] = ((e′-_one) & 2) == 0 ? h⁻ : h⁺ + return OctantBWG(l,(xyz[1],xyz[2],xyz[3])) +end + +transform_edge_remote(forest::ForestBWG,e::EdgeIndex,oct::OctantBWG,inside) = transform_edge_remote(forest,e[1],e[2],oct,inside) + +""" + transform_edge(forest,k,e,oct,inside::Bool) + transform_edge(forest,e::Edgeindex,oct,inside::Bool) + +Algorithm 10 in [BWG2011](@citet) to transform cedge into different octree coordinate system but reversed logic. +See `transform_edge_remote` with logic from paper. +In this function we stick to the coordinate system of the pivot tree k and transform an octant through edge e into this k-th octree coordinate system. +""" +function transform_edge(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N,T2},inside::Bool) where {N,T1<:Integer,T2<:Integer} + _four = T2(4) + _one = T2(1) + _two = T2(2) + z = zero(T2) + e_perm = edge_perm + e_perminv = edge_perm_inv + + e_ferrite = e_perm[e] + k′, e′_ferrite = forest.topology.edge_edge_neighbor[k,e_ferrite][1] + k′, e′_ferrite = forest.topology.edge_edge_neighbor[k′,e′_ferrite][1] #get pivot connection from neighbor perspective + e′ = e_perminv[e′_ferrite] + #see Algorithm 9, line 18 + 𝐛 = (((e′-_one) ÷ _four), + e′-_one < 4 ? 1 : 0, + e′-_one < 8 ? 2 : 1) + a₀ = ((e-_one) ÷ _four) #subtract 1 based index + a₀ += _one #add it again + b = forest.cells[k].b + l = oct.l; g = _two^b - _two^(b-l) + h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b + s = compute_edge_orientation(forest,k′,e′) + xyz = zeros(T2,3) + xyz[𝐛[1]+_one] = s*g+(_one-(_two*s))*oct.xyz[a₀] + xyz[𝐛[2]+_one] = ((e′-_one) & 1) == 0 ? h⁻ : h⁺ + xyz[𝐛[3]+_one] = ((e′-_one) & 2) == 0 ? h⁻ : h⁺ + return OctantBWG(l,(xyz[1],xyz[2],xyz[3])) +end + +transform_edge(forest::ForestBWG,e::EdgeIndex,oct::OctantBWG,inside) = transform_edge(forest,e[1],e[2],oct,inside) """ edge_neighbor(octant::OctantBWG, e::Integer, b::Integer) -Computes the edge neighbor octant which is only connected by the edge `e` to `octant` +Computes the edge neighbor octant which is only connected by the edge `e` to `octant`. """ function edge_neighbor(octant::OctantBWG{3,N,T}, e::T, b::T=_maxlevel[2]) where {N,T<:Integer} @assert 1 ≤ e ≤ 12 - e -= one(T) - l = octant.l _one = one(T) _two = T(2) + e -= _one + l = octant.l h = T(_compute_size(b,octant.l)) ox,oy,oz = octant.xyz - case = e ÷ T(4) - if case == zero(T) - x = ox - y = oy + (_two*(e & _one) - one(T))*h - z = oz + ((e & _two) - _one)*h - return OctantBWG(l,(x,y,z)) - elseif case == one(T) - x = ox + (_two*(e & _one) - _one)*h - y = oy - z = oz + ((e & _two) - _one)*h - return OctantBWG(l,(x,y,z)) - elseif case == _two - x = ox + (_two*(e & _one) - _one)*h - y = oy + ((e & _two) - _one)*h - z = oz - return OctantBWG(l,(x,y,z)) - else - error("edge case not found") - end + 𝐚 = (e ÷ 4, + e < 4 ? 1 : 0, + e < 8 ? 2 : 1) + xyz = zeros(T,3) + xyz[𝐚[1]+_one] = octant.xyz[𝐚[1]+_one] + xyz[𝐚[2]+_one] = octant.xyz[𝐚[2]+_one] + (_two*(e&_one)-_one)*h + xyz[𝐚[3]+_one] = octant.xyz[𝐚[3]+_one] + ((e & _two)-_one)*h + return OctantBWG(l,(xyz[1],xyz[2],xyz[3])) end edge_neighbor(o::OctantBWG{3,N,T1}, e::T2, b::T3) where {N,T1<:Integer,T2<:Integer,T3<:Integer} = edge_neighbor(o,T1(e),T1(b)) @@ -1577,6 +1857,34 @@ const 𝒱₃_perm_inv = [5 1 6] +# edge indices permutation from p4est idx to Ferrite idx +const edge_perm = [1 + 3 + 5 + 7 + 4 + 2 + 8 + 6 + 9 + 10 + 12 + 11] + +# edge indices permutation from Ferrite idx to p4est idx +const edge_perm_inv = [1 + 6 + 2 + 5 + 3 + 8 + 4 + 7 + 9 + 10 + 12 + 11] + const ℛ = [1 2 2 1 1 2 3 1 1 2 2 1 3 1 1 2 2 1 diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index 5bccabb8ec..2ab7f92c4f 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -113,7 +113,7 @@ function component_names(::Type{S}) where S return names end -function vtk_nodeset(vtk::WriteVTK.DatasetFile, grid::AbstractGrid, nodeset::String) where {dim} +function vtk_nodeset(vtk::WriteVTK.DatasetFile, grid::AbstractGrid, nodeset::String) z = zeros(getnnodes(grid)) z[collect(getnodeset(grid, nodeset))] .= 1.0 vtk_point_data(vtk, z, nodeset) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 4ff854bf04..963bb39929 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -97,7 +97,7 @@ include("Grid/grid_generators.jl") include("Grid/coloring.jl") # Adaptiviy -include(joinpath("Adaptivity", "AdaptiveCells.jl")) +include("Adaptivity/BWG.jl") # Dofs include("Dofs/DofHandler.jl") diff --git a/test/test_p4est.jl b/test/test_p4est.jl index b4709b5301..f314d2848a 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -36,6 +36,24 @@ @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) end +@testset "Index Permutation" begin + for i in 1:length(Ferrite.edge_perm) + @test i == Ferrite.edge_perm_inv[Ferrite.edge_perm[i]] + end + for i in 1:length(Ferrite.𝒱₂_perm) + @test i == Ferrite.𝒱₂_perm_inv[Ferrite.𝒱₂_perm[i]] + end + for i in 1:length(Ferrite.𝒱₃_perm) + @test i == Ferrite.𝒱₃_perm_inv[Ferrite.𝒱₃_perm[i]] + end + for i in 1:length(Ferrite.node_map₂) + @test i == Ferrite.node_map₂_inv[Ferrite.node_map₂[i]] + end + for i in 1:length(Ferrite.node_map₃) + @test i == Ferrite.node_map₃_inv[Ferrite.node_map₃[i]] + end +end + @testset "OctantBWG Encoding" begin # # Tests from Figure 3a) and 3b) of Burstedde et al o = Ferrite.OctantBWG(3,2,21,3) @@ -110,8 +128,20 @@ end @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),1,4) == Ferrite.OctantBWG(3,(0,-2,-2)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),12,4) == Ferrite.OctantBWG(3,(2,2,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),1,4) == Ferrite.OctantBWG(2,(0,-4,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),2,4) == Ferrite.OctantBWG(2,(0,4,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),3,4) == Ferrite.OctantBWG(2,(0,-4,4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),4,4) == Ferrite.OctantBWG(2,(0,4,4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),5,4) == Ferrite.OctantBWG(2,(-4,0,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),6,4) == Ferrite.OctantBWG(2,(4,0,-4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),7,4) == Ferrite.OctantBWG(2,(-4,0,4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),8,4) == Ferrite.OctantBWG(2,(4,0,4)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),9,4) == Ferrite.OctantBWG(2,(-4,-4,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),10,4) == Ferrite.OctantBWG(2,(4,-4,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),11,4) == Ferrite.OctantBWG(2,(-4,4,0)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),12,4) == Ferrite.OctantBWG(2,(4,4,0)) + @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),1,4) == Ferrite.OctantBWG(1,(0,-8,-8)) @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),12,4) == Ferrite.OctantBWG(1,(8,8,0)) @@ -181,24 +211,24 @@ end grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) - # Now this is our root mesh - # x-----------x-----------x - # |4 4 3|4 3 3| - # | | | - # | ^ | <--+ | - # |1 | 2|2 | 1| - # | +--> | v | - # | | | - # |1 3 2|1 4 2| - # x-----------x-----------x - # |4 4 3|3 2 2| - # | | | - # | ^ | ^ | - # |1 | 2|4 | 3| - # | +--> | <--+ | - # | | | - # |1 3 2|4 1 1| - # x-----------x-----------x + # root mesh in Ferrite notation in p4est notation + # x-----------x-----------x x-----------x-----------x + # |4 3 3|2 1 1| |3 4 4|2 3 1| + # | | | | | | + # | ^ | <--+ | | ^ | <--+ | + # |4 | 2|2 | 4| |1 | 2|2 | 1| + # | +--> | v | | +--> | v | + # | | | | | | + # |1 1 2|3 3 4| |1 3 2|4 4 3| + # x-----------x-----------x x-----------x-----------x + # |4 3 3|3 2 2| |3 4 4|4 4 1| + # | | | | | | + # | ^ | ^ | | ^ | ^ | + # |4 | 2|3 | 1| |1 | 2|2 | 1| + # | +--> | <--+ | | +--> | <--+ | + # | | | | | | + # |1 1 2|4 4 1| |1 3 2|3 3 1| + # x-----------x-----------x x-----------x-----------x adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells @test cell isa OctreeBWG @@ -212,6 +242,12 @@ end @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(0,-8)) @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,4), adaptive_grid.cells[2].leaves[1]) == OctantBWG(0,(8,0)) @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(0,8)) + + #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,8)) + #@test Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,-8)) + #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.transform_corner(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1],false) == OctantBWG(0,(8,8)) + #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.transform_corner(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,-8)) + o = adaptive_grid.cells[1].leaves[1] @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0)) @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8)) @@ -334,8 +370,110 @@ end # now do the same with 3D # some ascii picasso can insert here something beautiful ######################### + # TODO add some test with higher refinement level which failed in my REPl (I think 8 should fail) + # TODO add some rotation and more elaborate case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + o = adaptive_grid.cells[1].leaves[1] + + # faces + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(-8,0,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,-8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,6), o) == OctantBWG(0,(0,0,8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,6), o) == OctantBWG(0,(0,0,-8)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,1), o) == OctantBWG(0,(-8,0,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,1), o) == OctantBWG(0,(8,0,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,3), o) == OctantBWG(0,(0,-8,0)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,3), o) == OctantBWG(0,(0,8,0)) + @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,5), o) == OctantBWG(0,(0,0,-8)) + @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,5), o) == OctantBWG(0,(0,0,8)) + + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,1), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,1), o) + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,3), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,3), o) + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,5), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,5), o) + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,2), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,2), o) + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,4), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,4), o) + @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,6), o) + @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,6), o) + + #corners + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,1), o, false) == OctantBWG(0,(-8,-8,-8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,2), o, false) == OctantBWG(0,(8,-8,-8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,3), o, false) == OctantBWG(0,(-8,8,-8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,4), o, false) == OctantBWG(0,(8,8,-8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,5), o, false) == OctantBWG(0,(-8,-8,8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,6), o, false) == OctantBWG(0,(8,-8,8)) + @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,7), o, false) == OctantBWG(0,(-8,8,8)) + @test Ferrite.transform_corner(adaptive_grid, VertexIndex(1,8), o, false) == OctantBWG(0,(8,8,8)) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,1), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,2), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,3), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,4), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,5), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,6), o, false) + @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,7), o, false) + Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,8), o, false) == OctantBWG(0,(-8,-8,-8)) + + #edges + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,1), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,2), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,3), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,1), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,2), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,3), o, false) + @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,4), o, false) == OctantBWG(0,(0,8,8)) + @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,4), o, false) == OctantBWG(0,(0,-8,-8)) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,5), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,6), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,7), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,5), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,6), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,7), o, false) + @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,8), o, false) == OctantBWG(0,(8,0,8)) + @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,8), o, false) == OctantBWG(0,(-8,0,-8)) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,9), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,10), o, false) + @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,11), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,9), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,10), o, false) + @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,11), o, false) + @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,12), o, false) == OctantBWG(0,(8,8,0)) + @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,12), o, false) == OctantBWG(0,(-8,-8,0)) + + # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) + # This is our root mesh top view + # x-----------x-----------x + # |6 3 5|8 4 7| + # | | | + # | ^ | ^ | + # |2 | 1|1 | 2| + # | <--+ | +--> | + # | | | + # |7 4 8|5 3 6| + # x-----------x-----------x + # |8 4 7|8 4 7| + # | | | + # | ^ | ^ | + # |1 | 2|1 | 2| + # | +--> | +--> | + # | | | + # |5 3 6|5 3 6| + # x-----------x-----------x + # Rotate face topologically + grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) + grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) adaptive_grid = ForestBWG(grid,3) + @test Ferrite.transform_corner(adaptive_grid,7,3,OctantBWG(0,(0,0,0)),false) == OctantBWG(0,(-8,8,-8)) + + #refinement Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 8 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) @@ -399,6 +537,8 @@ end end @testset "Balancing" begin + #2D cases + #simple one quad with one additional non-allowed non-conformity level grid = generate_grid(Quadrilateral,(1,1)) adaptive_grid = ForestBWG(grid,3) Ferrite.refine_all!(adaptive_grid,1) @@ -408,6 +548,7 @@ end balanced = Ferrite.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 16 + #more complex non-conformity level 3 and 4 that needs to be balanced adaptive_grid = ForestBWG(grid,5) Ferrite.refine_all!(adaptive_grid,1) Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) @@ -420,7 +561,6 @@ end balanced = Ferrite.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 64 - grid = generate_grid(Quadrilateral,(2,1)) adaptive_grid = ForestBWG(grid,2) Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @@ -434,4 +574,394 @@ end Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) Ferrite.balanceforest!(adaptive_grid) @test Ferrite.getncells(adaptive_grid) == 19 + + # 2D example with balancing over a corner connection that is not within the topology tables + grid = generate_grid(Quadrilateral,(2,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[5]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 23 + + #corner balance case but rotated + grid = generate_grid(Quadrilateral,(2,1)) + grid.cells[1] = Quadrilateral((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1])) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 23 + + # 3D case intra treee simple test, non conformity level 2 + grid = generate_grid(Hexahedron,(1,1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + @test length(balanced.leaves) == 43 + + #3D case intra tree non conformity level 3 at two different places + adaptive_grid = ForestBWG(grid,4) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[28]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[29]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[37]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[39]) + balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + @test length(balanced.leaves) == 127 + + #3D case inter tree non conformity level 3 at two different places + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,4) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + #Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + #Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + transfered_grid_ref = Ferrite.creategrid(adaptive_grid) + + # Rotate three dimensional case + grid = generate_grid(Hexahedron,(2,2,2)) + # This is our root mesh top view + # x-----------x-----------x + # |7 2 6|8 4 7| + # | | | + # | ^ | ^ | + # |4 | 3|1 | 2| + # | <--+ | +--> | + # | | | + # |8 1 5|5 3 6| + # x-----------x-----------x + # |8 4 7|8 4 7| + # | | | + # | ^ | ^ | + # |1 | 2|1 | 2| + # | +--> | +--> | + # | | | + # |5 3 6|5 3 6| + # x-----------x-----------x + # Rotate face topologically + grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) + grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + #Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == length(transfered_grid_ref.cells) + @test length(transfered_grid.cells) == 92 + + # edge balancing for new introduced connection that is not within topology table + grid = generate_grid(Hexahedron, (2,1,1)); + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid, [1,2]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid, [4]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid, [5]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 51 + + #another edge balancing case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid,1) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid,[2,4,6,8]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid,34) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 134 + + #yet another edge balancing case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid,1) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid,[2,4,6,8]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid,30) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 120 + + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 15 + + #yet another edge balancing case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 43 + + #yet another edge balancing case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[10]) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[3]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 71 + + #yet another edge balancing case + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.balanceforest!(adaptive_grid) + Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[7]) + Ferrite.balanceforest!(adaptive_grid) + @test Ferrite.getncells(adaptive_grid) == 120 +end + +@testset "Materializing Grid" begin + ################################################# + ############ structured 2D examples ############# + ################################################# + + # 2D case with a single tree + grid = generate_grid(Quadrilateral,(1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 10 + @test length(transfered_grid.nodes) == 19 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + #2D case with four trees and somewhat refinement pattern + grid = generate_grid(Quadrilateral,(2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 22 + @test length(transfered_grid.nodes) == 35 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + #more random refinement + grid = generate_grid(Quadrilateral,(3,3)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) + Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[3]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[3]) + Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[5]) + Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 45 + @test length(transfered_grid.nodes) == 76 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + ################################################# + ############ structured 3D examples ############# + ################################################# + + # 3D case with a single tree + grid = generate_grid(Hexahedron,(1,1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 8+7+7 + @test length(transfered_grid.nodes) == 65 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + # Test only Interoctree by face connection + grid = generate_grid(Hexahedron,(2,1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 16 + @test length(transfered_grid.nodes) == 45 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + #rotate the case around + grid = generate_grid(Hexahedron,(1,2,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 16 + @test length(transfered_grid.nodes) == 45 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + grid = generate_grid(Hexahedron,(1,1,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 16 + @test length(transfered_grid.nodes) == 45 + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 8^2 + @test length(transfered_grid.nodes) == 125 # 5 per edge + @test unique(transfered_grid.nodes) == transfered_grid.nodes + + # Rotate three dimensional case + grid = generate_grid(Hexahedron,(2,2,2)) + # Rotate face topologically + grid.cells[2] = Hexahedron((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[4+2], grid.cells[2].nodes[4+3], grid.cells[2].nodes[4+4], grid.cells[2].nodes[4+1])) + grid.cells[2] = Hexahedron((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[4+2], grid.cells[2].nodes[4+3], grid.cells[2].nodes[4+4], grid.cells[2].nodes[4+1])) + # This is our root mesh bottom view + # x-----------x-----------x + # |4 4 3|4 4 3| + # | | | + # | ^ | ^ | + # |1 | 2|1 | 2| + # | +--> | +--> | + # | | | + # |1 3 2|1 3 2| + # x-----------x-----------x + # |4 4 3|3 2 2| + # | | | + # | ^ | ^ | + # |1 | 2|4 | 3| + # | +--> | <--+ | + # | | | + # |1 3 2|4 1 1| + # x-----------x-----------x + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.cells) == 8^2 + @test length(transfered_grid.nodes) == 125 # 5 per edge + @test unique(transfered_grid.nodes) == transfered_grid.nodes + #TODO iterate over all rotated versions and check if det J > 0 +end + +@testset "hanging nodes" begin + #Easy Intraoctree + grid = generate_grid(Hexahedron,(1,1,1)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine_all!(adaptive_grid,1) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + # x-----------x-----------x + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # x-----x-----x-----------x + # | | | | + # | | | | + # | | | | + # x-----x-----x | + # | | | | + # | | | | + # | | | | + # x-----x-----x-----------x + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.conformity_info) == 12 + + # Easy Interoctree + grid = generate_grid(Hexahedron,(2,2,2)) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + # x-----------x-----------x + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # | | | + # x-----x-----x-----------x + # | | | | + # | | | | + # | | | | + # x-----x-----x | + # | | | | + # | | | | + # | | | | + # x-----x-----x-----------x + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test length(transfered_grid.conformity_info) == 12 + + #rotate the case from above in the first cell around + grid = generate_grid(Hexahedron,(2,2,2)) + # Rotate face topologically + grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[4+2], grid.cells[1].nodes[4+3], grid.cells[1].nodes[4+4], grid.cells[1].nodes[4+1])) + grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[4+2], grid.cells[1].nodes[4+3], grid.cells[1].nodes[4+4], grid.cells[1].nodes[4+1])) + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid_rotated = Ferrite.creategrid(adaptive_grid) + @test transfered_grid_rotated.conformity_info[5] == [28,25] + @test transfered_grid_rotated.conformity_info[20] == [10,15] + @test transfered_grid_rotated.conformity_info[30] == [15,18] + @test transfered_grid_rotated.conformity_info[1] == [2,18] + @test transfered_grid_rotated.conformity_info[19] == [16,28] + @test transfered_grid_rotated.conformity_info[22] == [10,15,2,18] + @test transfered_grid_rotated.conformity_info[41] == [10,16,2,28] + @test transfered_grid_rotated.conformity_info[43] == [18,25] + @test transfered_grid_rotated.conformity_info[11] == [10,2] + @test transfered_grid_rotated.conformity_info[36] == [2,28] + @test transfered_grid_rotated.conformity_info[40] == [10,16] + @test transfered_grid_rotated.conformity_info[38] == [2,18,28,25] + @test length(transfered_grid_rotated.conformity_info) == 12 + + #2D rotated case + grid = generate_grid(Quadrilateral,(2,2)) + # Rotate face topologically + grid.cells[2] = Quadrilateral((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1])) + # This is our root mesh + # x-----------x-----------x + # |4 4 3|4 4 3| + # | | | + # | ^ | ^ | + # |1 | 2|1 | 2| + # | +--> | +--> | + # | | | + # |1 3 2|1 3 2| + # x-----------x-----------x + # |4 4 3|3 2 2| + # | | | + # | ^ | ^ | + # |1 | 2|4 | 3| + # | +--> | <--+ | + # | | | + # |1 3 2|4 1 1| + # x-----------x-----------x + adaptive_grid = ForestBWG(grid,3) + Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + transfered_grid_rotated = Ferrite.creategrid(adaptive_grid) + @test transfered_grid_rotated.conformity_info[11] == [1,7] + @test transfered_grid_rotated.conformity_info[10] == [3,7] end From ecaae642ef9f7e59fab60f90047219e9b9092d36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 2 May 2024 11:14:52 +0200 Subject: [PATCH 104/143] multiple corner connections in balancing --- src/Adaptivity/BWG.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 4ec8ac1959..cf3ccbf170 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -984,11 +984,11 @@ function balanceforest!(forest::ForestBWG{dim}) where dim end continue else - @assert length(cc) == 1 - !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue - cc = cc[1] - k′, c′ = cc[1], perm_corner_inv[cc[2]] - balance_corner(forest,k′,c′,o,s) + for corner_connection in cc + !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue + k′, c′ = corner_connection[1], perm_corner_inv[corner_connection[2]] + balance_corner(forest,k′,c′,o,s) + end end else # face neighbor, only true for 2D s_i -= 4 @@ -1004,11 +1004,11 @@ function balanceforest!(forest::ForestBWG{dim}) where dim #TODO a check of new introduced corner neighbors aka corner balancing, see 2D branch cc = forest.topology.vertex_vertex_neighbor[k,perm_corner[s_i]] isempty(cc) && continue - @assert length(cc) == 1 # FIXME there can be more than 1 vertex neighbor - !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue - cc = cc[1] - k′, c′ = cc[1], perm_corner_inv[cc[2]] - balance_corner(forest,k′,c′,o,s) + for corner_connection in cc + !(vertex(o,s_i,tree.b) == rootvertices[s_i]) && continue + k′, c′ = corner_connection[1], perm_corner_inv[corner_connection[2]] + balance_corner(forest,k′,c′,o,s) + end elseif 8 < s_i <= 14 s_i -= 8 fc = forest.topology.face_face_neighbor[k,perm_face[s_i]] From c7f3c6fe72eeb37bcb3d563a036beb31b7b0e770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 9 May 2024 10:50:45 +0200 Subject: [PATCH 105/143] remove changes in linear_elasticity.jl docs --- docs/src/literate-tutorials/linear_elasticity.jl | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/src/literate-tutorials/linear_elasticity.jl diff --git a/docs/src/literate-tutorials/linear_elasticity.jl b/docs/src/literate-tutorials/linear_elasticity.jl new file mode 100644 index 0000000000..3381be54a6 --- /dev/null +++ b/docs/src/literate-tutorials/linear_elasticity.jl @@ -0,0 +1,3 @@ +# # Linear elasticity +# +# TBW From 359c821c49235b8730311d76c198e2940c7b778b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 9 May 2024 11:28:23 +0200 Subject: [PATCH 106/143] start some devdocs --- docs/src/devdocs/AMR.md | 180 ++++++++++++++++++++++++++++++++++++++ docs/src/devdocs/index.md | 2 +- 2 files changed, 181 insertions(+), 1 deletion(-) create mode 100644 docs/src/devdocs/AMR.md diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md new file mode 100644 index 0000000000..6fc1ee58f9 --- /dev/null +++ b/docs/src/devdocs/AMR.md @@ -0,0 +1,180 @@ +# AMR + +## P4est + +All of it is based on these papers: + +- [BWG2011](@citet) +- [IBWG2015](@citet) + +where almost everything is implemented in a serial way from the first paper. +Only certain specific algorithms of the second paper are implemented and there is a lot of open work to include the iterators of the second paper. +Look into the issues of Ferrite.jl and search for the AMR tag. + +### Important Concepts + +One of the most important concepts, where everything is based on, are space filling curves (SFC). +In particular, [Z-order (also named Morton order, Morton space-filling curves)](https://en.wikipedia.org/wiki/Z-order_curve) are used in p4est. +The basic idea is that each Octant (in 3D) or quadrant (in 2D) can be encoded by 2 quantities + +- the level `l` +- the lower left (front) coordinates `xyz` + +Based on them a unique identifier, the morton index, can be computed. +The mapping from (`l`, `xyz`) -> `mortonidx(l,xyz)` is bijective, meaning we can flip the approach +and can construct each octant/quadrant solely by the `mortonidx` and a given level `l`. + +The current implementation of an octant looks currently like this: +```julia +struct OctantBWG{dim, N, T} <: AbstractCell{RefHypercube{dim}} + #Refinement level + l::T + #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} + xyz::NTuple{dim,T} +end +``` +whenever coordinates are considered we follow the z order logic, meaning x before y before z. + +The octree is implemented as: +```julia +struct OctreeBWG{dim,N,T} <: AbstractAdaptiveCell{RefHypercube{dim}} + leaves::Vector{OctantBWG{dim,N,T}} + #maximum refinement level + b::T + nodes::NTuple{N,Int} +end +``` + +So, only the leaves of the tree are stored and not any intermediate refinement level. +The field `b` is the maximum refinement level and is crucial. This parameter determines the size of the octree coordinate system. +The octree coordinate system is the coordinate system in which the coordinates `xyz` of any `octant::OctantBWG` are described. +This coordinate system goes from [0,2^b]^{dim}. The size of an octant is always 1 at the lowest possible level `b`. + +### Examples + +Let's say the maximum octree level is $b=3$, then the coordinate system is in 2D $[0,2^3]^2 = [0, 8]^2$. +So, our root is on level 0 of size 8 and has the lower left coordinates `(0,0)` + +```julia +# different constructors available, first one OctantBWG(dim,level,mortonid,maximumlevel) +# other possibility by giving directly level and a tuple of coordinates OctantBWG(level,(x,y)) +julia> oct = OctantBWG(2,0,1,3) +OctantBWG{2,4,4} + l = 0 + xy = 0,0 +``` +The size of octants at a specific level can be computed by a simple operation +```julia +julia> Ferrite._compute_size(3,0) +8 +``` +Now, to fully understand the octree coordinate system we go a level down, i.e. we cut the space in $x$ and $y$ in half. +This means, that the octants are now of size 4. +```julia +julia> Ferrite._compute_size(3,1) +4 +``` +Construct all level 1 octants based on mortonid: +```julia +# note the arguments are dim,level,mortonid,maximumlevel +julia> oct = OctantBWG(2,1,1,3) +OctantBWG{2,4,4} + l = 1 + xy = 0,0 + +julia> o = OctantBWG(2,1,2,3) +OctantBWG{2,4,4} + l = 1 + xy = 4,0 + +julia> o = OctantBWG(2,1,3,3) +OctantBWG{2,4,4} + l = 1 + xy = 0,4 + +julia> o = OctantBWG(2,1,4,3) +OctantBWG{2,4,4} + l = 1 + xy = 4,4 +``` + +So, the morton index is on **one** specific level just a x before y before z "cell" or "element" identifier +``` +x-----------x-----------x +| | | +| | | +| 3 | 4 | +| | | +| | | +x-----------x-----------x +| | | +| | | +| 1 | 2 | +| | | +| | | +x-----------x-----------x +``` + +The operation to compute octants/quadrants is cheap, since it is just bitshifting. +An important aspect of the morton index is that it's only consecutive on **one** level in this specific implementation. +Note that other implementation exists that incorporate the level integer within the morton identifier and by that have a unique identifier across levels. +If you have a tree like this below: + +``` +x-----------x-----------x +| | | +| | | +| 9 | 10 | +| | | +| | | +x-----x--x--x-----------x +| |6 |7 | | +| 3 x--x--x | +| |4 |5 | | +x-----x--x--x 8 | +| | | | +| 1 | 2 | | +x-----x-----x-----------x +``` + +you would maybe think this is the morton index, but strictly speaking it is not. +What we see above is just the `leafindex`, i.e. the index where you find this leaf in the `leaves` array of `OctreeBWG`. +Let's try to construct the lower right based on the morton index on level 1 + +```julia +julia> o = OctantBWG(2,1,8,3) +ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) +Stacktrace: + [1] OctantBWG(dim::Int64, l::Int32, m::Int32, b::Int32) + @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:23 + [2] OctantBWG(dim::Int64, l::Int64, m::Int64, b::Int64) + @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:43 + [3] top-level scope + @ REPL[93]:1 +``` + +The assertion expresses that it is not possible to construct a morton index 8 octant, since the upper bound of the morton index is 4 on level 1. +The morton index of the lower right cell is 2 on level 1. + +```julia +julia> o = OctantBWG(2,1,2,3) +OctantBWG{2,4,4} + l = 1 + xy = 4,0 +``` + +### Intraoctree operation + +```@docs +Ferrite.corner_neighbor +Ferrite.edge_neighbor +Ferrite.face_neighbor +``` + +### Interoctree operation + +```@docs +Ferrite.transform_corner +Ferrite.transform_edge +Ferrite.transform_face +``` diff --git a/docs/src/devdocs/index.md b/docs/src/devdocs/index.md index 9c16b83d7c..aafd41f00f 100644 --- a/docs/src/devdocs/index.md +++ b/docs/src/devdocs/index.md @@ -5,5 +5,5 @@ developing the library. ```@contents Depth = 1 -Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md"] +Pages = ["reference_cells.md", "interpolations.md", "elements.md", "FEValues.md", "dofhandler.md", "performance.md", "AMR.md"] ``` From aed19efbb69cd9c70bdc0ca6ab081aa4e3def841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 9 May 2024 12:31:32 +0200 Subject: [PATCH 107/143] update docstrings and remove md file from home directory (moved into devdocs) --- p4est-ferrite.md | 178 ------------------------------------------ src/Adaptivity/BWG.jl | 23 ++++++ 2 files changed, 23 insertions(+), 178 deletions(-) delete mode 100644 p4est-ferrite.md diff --git a/p4est-ferrite.md b/p4est-ferrite.md deleted file mode 100644 index 3092d45863..0000000000 --- a/p4est-ferrite.md +++ /dev/null @@ -1,178 +0,0 @@ -# P4est in Julia with Ferrite - -All of it is based on these papers: - -- [Original p4est paper](https://p4est.github.io/papers/BursteddeWilcoxGhattas11.pdf) -- [Extension to anisotropic refinement, aka p6est](https://epubs.siam.org/doi/10.1137/140974407) -- [Extension to RefTet elements and in depth explanations](https://bonndoc.ulb.uni-bonn.de/xmlui/handle/20.500.11811/7661); basically monography about t8code -- [Lucas sent me this, lots of algorithms I could need](https://epubs.siam.org/doi/epdf/10.1137/140970963) - -## Important Concepts - -One of the most important concepts, where everything is based on, are space filling curves (SFC). -In particular, [Z-order (also named Morton order, Morton space-filling curves)](https://en.wikipedia.org/wiki/Z-order_curve) are used in p4est. -The basic idea is that each Octant (in 3D) or quadrant (in 2D) can be encoded by 2 quantities - -- the level `l` -- the lower left (front) coordinates `xyz` - -Based on them a unique identifier, the morton index, can be computed. -The good part is, that the mapping from (`l`, `xyz`) -> `mortonidx(l,xyz)` is bijective, meaning we can flip the approach -and can construct each octant/quadrant solely by the `mortonidx`. - -The current implementation of an octant looks currently like this: -```julia -struct OctantBWG{dim, N, M, T} <: AbstractCell{dim,N,M} - #Refinement level - l::T - #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} - xyz::NTuple{dim,T} -end -``` -whenever coordinates are considered we follow the z order logic, meaning x before y before z. - -The octree is implemented as: -```julia -struct OctreeBWG{dim,N,M,T} <: AbstractAdaptiveCell{dim,N,M} - leaves::Vector{OctantBWG{dim,N,M,T}} - #maximum refinement level - b::T - nodes::NTuple{N,Int} -end -``` -So, only the leaves of the tree are stored and not any intermediate refinement level. -The field `b` is the maximum refinement level and is crucial. This parameter determines the size of the octree coordinate system. -The octree coordinate system is the coordinate system in which the coordinates `xyz` of any `octant::OctantBWG` are described. -This coordinate system goes from [0,2^b]^{dim}. The size of an octant is always 1 at the lowest possible level `b`. - -### Examples - -Let's say the maximum octree level is $b=3$, then the coordinate system is in 2D $[0,2^3]^2 = [0, 8]^2$. -So, our root is on level 0 of size 8 and has the lower left coordinates `(0,0)` - -```julia -# different constructors available, first one OctantBWG(dim,level,mortonid,maximumlevel) -# other possibility by giving directly level and a tuple of coordinates OctantBWG(level,(x,y)) -julia> oct = OctantBWG(2,0,1,3) -OctantBWG{2,4,4} - l = 0 - xy = 0,0 -``` -The size of octants at a specific level can be computed by a simple operation -```julia -#_compute_size(b::Integer, l::Integer) in Ferrite at /home/mkoehler/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:375 -julia> Ferrite._compute_size(3,0) -8 -``` -Now, to fully understand the octree coordinate system we go a level down, i.e. we cut the space in x and y in half. -This means, that the octrees now of size 4. -```julia -julia> Ferrite._compute_size(3,1) -4 -``` -Construct all level 1 octants based on mortonid: -```julia -# note the arguments are dim,level,mortonid,maximumlevel -julia> oct = OctantBWG(2,1,1,3) -OctantBWG{2,4,4} - l = 1 - xy = 0,0 - -julia> o = OctantBWG(2,1,2,3) -OctantBWG{2,4,4} - l = 1 - xy = 4,0 - -julia> o = OctantBWG(2,1,3,3) -OctantBWG{2,4,4} - l = 1 - xy = 0,4 - -julia> o = OctantBWG(2,1,4,3) -OctantBWG{2,4,4} - l = 1 - xy = 4,4 -``` - -So, the morton index is on **one** specific level just a x before y before z "cell" or "element" identifier -``` -x-----------x-----------x -| | | -| | | -| 3 | 4 | -| | | -| | | -x-----------x-----------x -| | | -| | | -| 1 | 2 | -| | | -| | | -x-----------x-----------x -``` - -The good news: it super cheap to compute octants/quadrants. -An important aspect of the morton index is that it's only consecutive on **one** level. -If you have a tree like this below: - -``` -x-----------x-----------x -| | | -| | | -| 9 | 10 | -| | | -| | | -x-----x--x--x-----------x -| |6 |7 | | -| 3 x--x--x | -| |4 |5 | | -x-----x--x--x 8 | -| | | | -| 1 | 2 | | -x-----x-----x-----------x -``` - -you would maybe think this is the morton index, but strictly speaking it is not. -What we see above is just the `leafindex`, i.e. the index where you find this leaf in the `leaves` array of `OctreeBWG`. -Let's try to construct the lower right based on the morton index on level 1 - -```julia -julia> o = OctantBWG(2,1,8,3) -ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) -Stacktrace: - [1] OctantBWG(dim::Int64, l::Int32, m::Int32, b::Int32) - @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:23 - [2] OctantBWG(dim::Int64, l::Int64, m::Int64, b::Int64) - @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:43 - [3] top-level scope - @ REPL[93]:1 -``` - -The assertion expresses that it is not possible to construct a morton index 8 octant, since the upper bound of the morton index is 4 on level 1. -The morton index of the lower right cell is of course 2 on level 1. - - -## Current state and open questions - -I implemented basic functionality to constructs and operate on octants/octrees. -In particular, I implemented from the p4est paper the Algorithms 1-7,14,15 (and refinement equivalent). - -Currently, I'm at fulfilling the `AbstractGrid` interface which has some tricky parts. -All of the functionality is serial, nothing in terms of distributed is implemented. - -### What we already can do -- refine octants -- coarsen octants -- compute neighbors of octants -- take a `Grid` and make a `ForestBWG` out of it - -### Open questions -- How to count the nodes? 4th paper has an answer to that which is called `LNodes`. -Algorithm 6.2, which relies on Iterate, Algorithm 5.3. This alrogithm in turn depends on Algorithm 5.2. and 5.1 - -### Open TODOs -- Implement the Iterator of IBWG 2015 -- Coordinate transformation from octree coordinate system to physical coordinate system -- Octant dispatches that are required to fulfill `<:AbstractCell` -- Morton index can be computed much faster by methods I commented above the function -- more efficient `getcells(forest,i)` diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index cf3ccbf170..949aa28949 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -1448,6 +1448,29 @@ end transform_face_remote(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face_remote(forest,f[1],f[2],oct) +""" + transform_face(forest::ForestBWG, k', f', o::OctantBWG) -> OctantBWG + transform_face(forest::ForestBWG, f'::FaceIndex, o::OctantBWG) -> OctantBWG +Interoctree coordinate transformation of an given octant `o` that lies outside of the pivot octree `k`, namely in neighbor octree `k'`. +However, the coordinate of `o` is given in octree coordinates of `k`. +Thus, this algorithm implements the transformation of the octree coordinates of `o` into the octree coordinates of `k'`. +Useful in order to check whether or not a possible neighbor exists in a neighboring octree. +Implements Algorithm 8 of [BWG2011](@citet). + + x-------x-------x + | | | + | 3 | 4 | + | | | + x-------x-------x + | | | + | 1 * 2 | + | | | + x-------x-------x + +Consider 4 octrees with a single leaf each and a maximum refinement level of 1 +This function transforms octant 1 into the coordinate system of octant 2 by specifying `k=1` and `f=2`. +While from the perspective of octree coordinates `k=2` octant 1 is at `xyz=(-2,0)`, the returned and transformed octant is located at `xyz=(0,0)` +""" function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2}) where {T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) From a453798c5281b44d8577572ac27c4643cac5c61d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 9 May 2024 12:39:49 +0200 Subject: [PATCH 108/143] a bit more devdocs --- docs/src/devdocs/AMR.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index 6fc1ee58f9..d0eabdbf74 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -165,6 +165,10 @@ OctantBWG{2,4,4} ### Intraoctree operation +Intraoctree operation stay within one octree and compute octants that are attached in some way to a pivot octant `o`. +These operations are useful to collect unique entities within a single octree or to compute possible neighbors of `o`. +[BWG2011](@citet) Algorithm 5, 6, and 7 describe the following intraoctree operations: + ```@docs Ferrite.corner_neighbor Ferrite.edge_neighbor @@ -173,8 +177,23 @@ Ferrite.face_neighbor ### Interoctree operation +Interoctree operation are in contrast to intraoctree operation by computing octant transformations across different octrees. +Thereby, one needs to account for topological connections between the octrees as well as possible rotations of the octrees. +[BWG2011](@citet) Algorithm 8, 10, and 12 explain the algorithms that are implemented in the following functions: + ```@docs Ferrite.transform_corner Ferrite.transform_edge Ferrite.transform_face ``` + +Note that we flipped the input and to expected output logic a bit to the proposed algorithms of the paper. +However, the original proposed versions are implemented as well in: + +```@docs +Ferrite.transform_corner_remote +Ferrite.transform_edge_remote +Ferrite.transform_face_remote +``` + +despite being never used in the code base so far. From 6b0caa85547a6c8be481672ebc86dd0c11f0dd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 9 May 2024 19:27:58 +0200 Subject: [PATCH 109/143] more devdocs --- docs/src/devdocs/AMR.md | 15 +++++++++++++++ src/Adaptivity/BWG.jl | 25 +++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index d0eabdbf74..a5a02e490b 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -163,6 +163,20 @@ OctantBWG{2,4,4} xy = 4,0 ``` +### Octant operation + +There are multiple useful functions to compute information about an octant e.g. parent, childs, etc. + +```@docs +Ferrite.isancestor +Ferrite.morton +Ferrite.children +Ferrite.vertices(octant::OctantBWG, b::Integer) +Ferrite.edges(octant::OctantBWG{3}, b::Integer) +Ferrite.faces(octant::OctantBWG, b::Integer) +Ferrite.transform_pointBWG +``` + ### Intraoctree operation Intraoctree operation stay within one octree and compute octants that are attached in some way to a pivot octant `o`. @@ -173,6 +187,7 @@ These operations are useful to collect unique entities within a single octree or Ferrite.corner_neighbor Ferrite.edge_neighbor Ferrite.face_neighbor +Ferrite.possibleneighbors ``` ### Interoctree operation diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 949aa28949..6ff0eeecb8 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -63,8 +63,8 @@ From [BWG2011](@citet); > The octant coordinates are stored as integers of a fixed number b of bits, > where the highest (leftmost) bit represents the first vertical level of the > octree (counting the root as level zero), the second highest bit the second level of the octree, and so on. -A morton index can thus be constructed by interleaving the integer bits: -m(Oct) := (y_b,x_b,y_b-1,x_b-1,...y0,x0)_2 +A morton index can thus be constructed by interleaving the integer bits (2D): +\$m(\\text{Oct}) := (y_b,x_b,y_{b-1},x_{b-1},...y_0,x_0)_2\$ further we assume the following > Due to the two-complement representation of integers in practically all current hardware, > where the highest digit denotes the negated appropriate power of two, bitwise operations as used, @@ -117,6 +117,10 @@ function Base.isless(o1::OctantBWG, o2::OctantBWG) end end +""" + children(octant::OctantBWG{dim,N,T}, b::Integer) -> NTuple{M,OctantBWG} +Computes all childern of `octant` +""" function children(octant::OctantBWG{dim,N,T}, b::Integer) where {dim,N,T} o = one(T) _nchilds = nchilds(octant) @@ -156,6 +160,11 @@ function vertex(octant::OctantBWG{dim,N,T}, c::Integer, b::Integer) where {dim,N return ntuple(d->((c-1) & (2^(d-1))) == 0 ? octant.xyz[d] : octant.xyz[d] + h ,dim) end +""" + vertices(octant::OctantBWG{dim}, b::Integer) + +Computes all vertices of a given `octant`. Each vertex is encoded within the octree coordinates i.e. by integers. +""" function vertices(octant::OctantBWG{dim},b::Integer) where {dim} _nvertices = 2^dim return ntuple(i->vertex(octant,i,b),_nvertices) @@ -172,6 +181,12 @@ function face(octant::OctantBWG{3}, f::Integer, b::Integer) return ntuple(i->vertex(octant, cornerid[i], b),4) end +""" + faces(octant::OctantBWG{dim}, b::Integer) + +Computes all faces of a given `octant`. Each face is encoded within the octree coordinates i.e. by integers. +Further, each face consists of either two two-dimensional integer coordinates or four three-dimensional integer coordinates. +""" function faces(octant::OctantBWG{dim}, b::Integer) where dim _nfaces = 2*dim return ntuple(i->face(octant,i,b),_nfaces) @@ -183,6 +198,12 @@ function edge(octant::OctantBWG{3}, e::Integer, b::Integer) return ntuple(i->vertex(octant,cornerid[i], b),2) end +""" + edges(octant::OctantBWG{dim}, b::Integer) + +Computes all edges of a given `octant`. Each edge is encoded within the octree coordinates i.e. by integers. +Further, each edge consists of two three-dimensional integer coordinates. +""" edges(octant::OctantBWG{3}, b::Integer) = ntuple(i->edge(octant,i,b),12) """ From 08ad4d89b25a44a3405ab1907175cdeb2bda63bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 10:36:45 +0200 Subject: [PATCH 110/143] move things into submodule --- src/Adaptivity/AMR.jl | 18 + src/Adaptivity/BWG.jl | 52 +- src/Adaptivity/constraints.jl | 43 ++ src/{Grid => Adaptivity}/ncgrid.jl | 14 +- src/Dofs/ConstraintHandler.jl | 43 -- src/Export/VTK.jl | 4 +- src/Ferrite.jl | 8 +- src/exports.jl | 2 - test/test_p4est.jl | 856 ++++++++++++++--------------- 9 files changed, 528 insertions(+), 512 deletions(-) create mode 100644 src/Adaptivity/AMR.jl create mode 100644 src/Adaptivity/constraints.jl rename src/{Grid => Adaptivity}/ncgrid.jl (81%) diff --git a/src/Adaptivity/AMR.jl b/src/Adaptivity/AMR.jl new file mode 100644 index 0000000000..83e706e5a0 --- /dev/null +++ b/src/Adaptivity/AMR.jl @@ -0,0 +1,18 @@ +module AMR + +using .. Ferrite +using SparseArrays + +include("BWG.jl") +include("ncgrid.jl") +include("constraints.jl") + +export ForestBWG, + refine!, + coarsen!, + balanceforest!, + creategrid, + ConformityConstraint, + NonConformingGrid + +end diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 6ff0eeecb8..400befa321 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -3,8 +3,8 @@ # - struct QuadrilateralBWG ... end # - struct HexahedronBWG ... end -abstract type AbstractAdaptiveGrid{dim} <: AbstractGrid{dim} end -abstract type AbstractAdaptiveCell{refshape <: AbstractRefShape} <: AbstractCell{refshape} end +abstract type AbstractAdaptiveGrid{dim} <: Ferrite.AbstractGrid{dim} end +abstract type AbstractAdaptiveCell{refshape <: Ferrite.AbstractRefShape} <: Ferrite.AbstractCell{refshape} end const ncorners_face3D = 4 const ncorners_face2D = 2 @@ -16,7 +16,7 @@ function set_maxlevel(dim::Integer,maxlevel::Integer) _maxlevel[dim-1] = maxlevel end -struct OctantBWG{dim, N, T} <: AbstractCell{RefHypercube{dim}} +struct OctantBWG{dim, N, T} <: Ferrite.AbstractCell{Ferrite.RefHypercube{dim}} #Refinement level l::T #x,y,z \in {0,...,2^b} where (0 ≤ l ≤ b)} @@ -305,7 +305,7 @@ function isrelevant(xyz::NTuple{dim,T},leafsuppₚ::Set{<:OctantBWG}) where {dim return true end -struct OctreeBWG{dim,N,T} <: AbstractAdaptiveCell{RefHypercube{dim}} +struct OctreeBWG{dim,N,T} <: AbstractAdaptiveCell{Ferrite.RefHypercube{dim}} leaves::Vector{OctantBWG{dim,N,T}} #maximum refinement level b::T @@ -438,26 +438,26 @@ struct ForestBWG{dim, C<:OctreeBWG, T<:Real} <: AbstractAdaptiveGrid{dim} # Sets cellsets::Dict{String,Set{Int}} nodesets::Dict{String,Set{Int}} - facesets::Dict{String,Set{FaceIndex}} - edgesets::Dict{String,Set{EdgeIndex}} - vertexsets::Dict{String,Set{VertexIndex}} + facesets::Dict{String,Set{Ferrite.FaceIndex}} + edgesets::Dict{String,Set{Ferrite.EdgeIndex}} + vertexsets::Dict{String,Set{Ferrite.VertexIndex}} #Topology topology::ExclusiveTopology end -function ForestBWG(grid::AbstractGrid{dim},b=_maxlevel[dim-1]) where dim +function ForestBWG(grid::Ferrite.AbstractGrid{dim},b=_maxlevel[dim-1]) where dim cells = getcells(grid) C = eltype(cells) @assert isconcretetype(C) @assert (C == Quadrilateral && dim == 2) || (C == Hexahedron && dim == 3) topology = ExclusiveTopology(cells) cells = OctreeBWG.(grid.cells,b) - nodes = getnodes(grid) - cellsets = getcellsets(grid) - nodesets = getnodesets(grid) - facesets = getfacesets(grid) - edgesets = getedgesets(grid) - vertexsets = getvertexsets(grid) + nodes = getnodes(grid) + cellsets = Ferrite.getcellsets(grid) + nodesets = Ferrite.getnodesets(grid) + facesets = Ferrite.getfacesets(grid) + edgesets = Ferrite.getedgesets(grid) + vertexsets = Ferrite.getvertexsets(grid) return ForestBWG(cells,nodes,cellsets,nodesets,facesets,edgesets,vertexsets,topology) end @@ -506,9 +506,9 @@ function coarsen_all!(forest::ForestBWG) end end -getneighborhood(forest::ForestBWG,idx) = getneighborhood(forest.topology,forest,idx) +Ferrite.getneighborhood(forest::ForestBWG,idx) = getneighborhood(forest.topology,forest,idx) -function getncells(grid::ForestBWG) +function Ferrite.getncells(grid::ForestBWG) numcells = 0 for tree in grid.cells numcells += length(tree) @@ -516,7 +516,7 @@ function getncells(grid::ForestBWG) return numcells end -function getcells(forest::ForestBWG{dim,C}) where {dim,C} +function Ferrite.getcells(forest::ForestBWG{dim,C}) where {dim,C} treetype = C ncells = getncells(forest) nnodes = 2^dim @@ -532,7 +532,7 @@ function getcells(forest::ForestBWG{dim,C}) where {dim,C} return cellvector end -function getcells(forest::ForestBWG{dim}, cellid::Int) where dim +function Ferrite.getcells(forest::ForestBWG{dim}, cellid::Int) where dim @warn "Slow dispatch, consider to call `getcells(forest)` once instead" maxlog=1 #TODO doc page for performance #TODO should nleaves be saved by forest? nleaves = length.(forest.cells) # cells=trees @@ -544,8 +544,8 @@ function getcells(forest::ForestBWG{dim}, cellid::Int) where dim return forest.cells[k].leaves[leafid] end -getcelltype(grid::ForestBWG) = eltype(grid.cells) -getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO +Ferrite.getcelltype(grid::ForestBWG) = eltype(grid.cells) +Ferrite.getcelltype(grid::ForestBWG, i::Int) = eltype(grid.cells) # assume for now same cell type TODO """ transform_pointBWG(forest, vertices) -> Vector{Vec{dim}} @@ -1350,9 +1350,9 @@ function face_neighbor(octant::OctantBWG{2,N,T}, f::T, b::T=_maxlevel[1]) where end face_neighbor(o::OctantBWG{dim,N,T1}, f::T2, b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) -reference_faces_bwg(::Type{RefHypercube{2}}) = ((1,3) , (2,4), (1,2), (3,4)) -reference_faces_bwg(::Type{RefHypercube{3}}) = ((1,3,5,7) , (2,4,6,8), (1,2,5,6), (3,4,7,8), (1,2,3,4), (5,6,7,8)) # p4est consistent ordering -reference_edges_bwg(::Type{RefHypercube{3}}) = ((𝒰[1,1],𝒰[1,2]), (𝒰[2,1],𝒰[2,2]), (𝒰[3,1],𝒰[3,2]), +reference_faces_bwg(::Type{Ferrite.RefHypercube{2}}) = ((1,3) , (2,4), (1,2), (3,4)) +reference_faces_bwg(::Type{Ferrite.RefHypercube{3}}) = ((1,3,5,7) , (2,4,6,8), (1,2,5,6), (3,4,7,8), (1,2,3,4), (5,6,7,8)) # p4est consistent ordering +reference_edges_bwg(::Type{Ferrite.RefHypercube{3}}) = ((𝒰[1,1],𝒰[1,2]), (𝒰[2,1],𝒰[2,2]), (𝒰[3,1],𝒰[3,2]), (𝒰[4,1],𝒰[4,2]), (𝒰[5,1],𝒰[5,2]), (𝒰[6,1],𝒰[6,2]), (𝒰[7,1],𝒰[7,2]), (𝒰[8,1],𝒰[8,2]), (𝒰[9,1],𝒰[9,2]), (𝒰[10,1],𝒰[10,2]), (𝒰[11,1],𝒰[11,2]), (𝒰[12,1],𝒰[12,2])) # TODO maybe remove, unnecessary, can directly use the table @@ -1371,9 +1371,9 @@ function compute_face_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{dim,<:Any, n_perminv = (dim == 2 ? node_map₂_inv : node_map₃_inv) f_ferrite = f_perm[f] - k′, f′_ferrite = getneighborhood(forest,FaceIndex(k,f_ferrite))[1] + k′, f′_ferrite = getneighborhood(forest,Ferrite.FaceIndex(k,f_ferrite))[1] f′ = f_perminv[f′_ferrite] - reffacenodes = reference_faces_bwg(RefHypercube{dim}) + reffacenodes = reference_faces_bwg(Ferrite.RefHypercube{dim}) nodes_f = [forest.cells[k].nodes[n_perm[ni]] for ni in reffacenodes[f]] nodes_f′ = [forest.cells[k′].nodes[n_perm[ni]] for ni in reffacenodes[f′]] if f > f′ @@ -1398,7 +1398,7 @@ function compute_edge_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{3,<:Any,T2 e_ferrite = e_perm[e] k′, e′_ferrite = forest.topology.edge_edge_neighbor[k,e_ferrite][1] e′ = e_perminv[e′_ferrite] - refedgenodes = reference_edges_bwg(RefHypercube{3}) + refedgenodes = reference_edges_bwg(Ferrite.RefHypercube{3}) nodes_e = ntuple(i->forest.cells[k].nodes[n_perm[refedgenodes[e][i]]],length(refedgenodes[e])) nodes_e′ = ntuple(i->forest.cells[k′].nodes[n_perm[refedgenodes[e′][i]]],length(refedgenodes[e′])) if nodes_e == nodes_e′ diff --git a/src/Adaptivity/constraints.jl b/src/Adaptivity/constraints.jl new file mode 100644 index 0000000000..5317c0cbb9 --- /dev/null +++ b/src/Adaptivity/constraints.jl @@ -0,0 +1,43 @@ +""" + This constraint can be passed to the constraint handler when working with non-conforming meshes to + add the affine constraints required to make the associated interpolation conforming. + + For a full example visit the AMR tutorial. +""" +struct ConformityConstraint + field_name::Symbol +end + +function Ferrite.add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:Grid}}, cc::ConformityConstraint) + @warn "Trying to add conformity constraint to $(cc.field_name) on a conforming grid. Skipping." +end + +function Ferrite.add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:NonConformingGrid}}, cc::ConformityConstraint) + @assert length(ch.dh.field_names) == 1 "Multiple fields not supported yet." + @assert cc.field_name ∈ ch.dh.field_names "Field $(cc.field_name) not found in provided dof handler. Available fields are $(ch.dh.field_names)." + # One set of linear contraints per hanging node + for sdh in ch.dh.subdofhandlers + field_idx = Ferrite._find_field(sdh, cc.field_name) + field_idx !== nothing && _add_conformity_constraint(ch, field_idx, sdh.field_interpolations[field_idx]) + end +end + +function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::ScalarInterpolation) + for (hdof,mdof) in ch.dh.grid.conformity_info + @debug @assert length(mdof) == 2 + lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof],[ch.dh.vertexdicts[field_index][m] => 0.5 for m in mdof], 0.0) + add!(ch,lc) + end +end + +function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::VectorizedInterpolation{vdim}) where vdim + for (hdof,mdof) in ch.dh.grid.conformity_info + @debug @assert length(mdof) == 2 + # One constraint per component + for vd in 1:vdim + lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof]+vd-1,[ch.dh.vertexdicts[field_index][m]+vd-1 => 0.5 for m in mdof], 0.0) # TODO change for other interpolation types than linear + add!(ch,lc) + end + end +end + diff --git a/src/Grid/ncgrid.jl b/src/Adaptivity/ncgrid.jl similarity index 81% rename from src/Grid/ncgrid.jl rename to src/Adaptivity/ncgrid.jl index 3265214378..812c127fcb 100644 --- a/src/Grid/ncgrid.jl +++ b/src/Adaptivity/ncgrid.jl @@ -19,15 +19,15 @@ This grid serves as an entry point for non-intrusive adaptive grid libraries. - `conformity_info::CIT`: a container for conformity information - `boundary_matrix::SparseMatrixCSC{Bool,Int}`: optional, only needed by `onboundary` to check if a cell is on the boundary, see, e.g. Helmholtz example """ -mutable struct NonConformingGrid{dim,C<:AbstractCell,T<:Real,CIT} <: AbstractGrid{dim} +mutable struct NonConformingGrid{dim,C<:Ferrite.AbstractCell,T<:Real,CIT} <: Ferrite.AbstractGrid{dim} cells::Vector{C} nodes::Vector{Node{dim,T}} # Sets cellsets::Dict{String,Set{Int}} nodesets::Dict{String,Set{Int}} - facesets::Dict{String,Set{FaceIndex}} - edgesets::Dict{String,Set{EdgeIndex}} - vertexsets::Dict{String,Set{VertexIndex}} + facesets::Dict{String,Set{Ferrite.FaceIndex}} + edgesets::Dict{String,Set{Ferrite.EdgeIndex}} + vertexsets::Dict{String,Set{Ferrite.VertexIndex}} conformity_info::CIT # TODO refine # Boundary matrix (faces per cell × cell) boundary_matrix::SparseMatrixCSC{Bool,Int} @@ -38,9 +38,9 @@ function NonConformingGrid( nodes::Vector{Node{dim,T}}; cellsets::Dict{String,Set{Int}}=Dict{String,Set{Int}}(), nodesets::Dict{String,Set{Int}}=Dict{String,Set{Int}}(), - facesets::Dict{String,Set{FaceIndex}}=Dict{String,Set{FaceIndex}}(), - edgesets::Dict{String,Set{EdgeIndex}}=Dict{String,Set{EdgeIndex}}(), - vertexsets::Dict{String,Set{VertexIndex}}=Dict{String,Set{VertexIndex}}(), + facesets::Dict{String,Set{Ferrite.FaceIndex}}=Dict{String,Set{Ferrite.FaceIndex}}(), + edgesets::Dict{String,Set{Ferrite.EdgeIndex}}=Dict{String,Set{Ferrite.EdgeIndex}}(), + vertexsets::Dict{String,Set{Ferrite.VertexIndex}}=Dict{String,Set{Ferrite.VertexIndex}}(), conformity_info, boundary_matrix::SparseMatrixCSC{Bool,Int}=spzeros(Bool, 0, 0) ) where {dim,C,T} diff --git a/src/Dofs/ConstraintHandler.jl b/src/Dofs/ConstraintHandler.jl index d86239b4be..b8ac56b713 100644 --- a/src/Dofs/ConstraintHandler.jl +++ b/src/Dofs/ConstraintHandler.jl @@ -101,49 +101,6 @@ function ConstraintHandler(::Type{T}, dh::AbstractDofHandler) where T <: Number ) end -""" - This constraint can be passed to the constraint handler when working with non-conforming meshes to - add the affine constraints required to make the associated interpolation conforming. - - For a full example visit the AMR tutorial. -""" -struct ConformityConstraint - field_name::Symbol -end - -function add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:Grid}}, cc::ConformityConstraint) - @warn "Trying to add conformity constraint to $(cc.field_name) on a conforming grid. Skipping." -end - -function add!(ch::ConstraintHandler{<:DofHandler{<:Any,<:NonConformingGrid}}, cc::ConformityConstraint) - @assert length(ch.dh.field_names) == 1 "Multiple fields not supported yet." - @assert cc.field_name ∈ ch.dh.field_names "Field $(cc.field_name) not found in provided dof handler. Available fields are $(ch.dh.field_names)." - # One set of linear contraints per hanging node - for sdh in ch.dh.subdofhandlers - field_idx = _find_field(sdh, cc.field_name) - field_idx !== nothing && _add_conformity_constraint(ch, field_idx, sdh.field_interpolations[field_idx]) - end -end - -function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::ScalarInterpolation) - for (hdof,mdof) in ch.dh.grid.conformity_info - @debug @assert length(mdof) == 2 - lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof],[ch.dh.vertexdicts[field_index][m] => 0.5 for m in mdof], 0.0) - add!(ch,lc) - end -end - -function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, interpolation::VectorizedInterpolation{vdim}) where vdim - for (hdof,mdof) in ch.dh.grid.conformity_info - @debug @assert length(mdof) == 2 - # One constraint per component - for vd in 1:vdim - lc = AffineConstraint(ch.dh.vertexdicts[field_index][hdof]+vd-1,[ch.dh.vertexdicts[field_index][m]+vd-1 => 0.5 for m in mdof], 0.0) # TODO change for other interpolation types than linear - add!(ch,lc) - end - end -end - """ RHSData diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index 2ab7f92c4f..d95a8e4cd6 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -57,13 +57,13 @@ Return a `DatasetFile` that data can be appended to, see The keyword arguments are forwarded to `WriteVTK.vtk_grid`, see [Data Formatting Options](https://juliavtk.github.io/WriteVTK.jl/stable/grids/syntax/#Data-formatting-options) """ -function WriteVTK.vtk_grid(filename::AbstractString, grid::Union{Grid{dim,<:Any,T}, NonConformingGrid{dim,<:Any,T}}; kwargs...) where {dim,T} +function WriteVTK.vtk_grid(filename::AbstractString, grid::AbstractGrid{dim}; kwargs...) where {dim} cls = MeshCell[] for cell in getcells(grid) celltype = Ferrite.cell_to_vtkcell(typeof(cell)) push!(cls, MeshCell(celltype, nodes_to_vtkorder(cell))) end - coords = reshape(reinterpret(T, getnodes(grid)), (dim, getnnodes(grid))) + coords = reshape(reinterpret(eltype(getnodes(grid,1).x), getnodes(grid)), (dim, getnnodes(grid))) return vtk_grid(filename, coords, cls; kwargs...) end function WriteVTK.vtk_grid(filename::AbstractString, dh::AbstractDofHandler; kwargs...) diff --git a/src/Ferrite.jl b/src/Ferrite.jl index 2f575ced56..eb45c7c275 100644 --- a/src/Ferrite.jl +++ b/src/Ferrite.jl @@ -108,15 +108,11 @@ include("FEValues/face_integrals.jl") # Grid include("Grid/grid.jl") -include("Grid/ncgrid.jl") include("Grid/topology.jl") include("Grid/utils.jl") include("Grid/grid_generators.jl") include("Grid/coloring.jl") -# Adaptiviy -include("Adaptivity/BWG.jl") - # Dofs include("Dofs/DofHandler.jl") include("Dofs/ConstraintHandler.jl") @@ -142,4 +138,8 @@ include("PointEvalHandler.jl") include("deprecations.jl") include("docs.jl") +# Adaptiviy +include("Adaptivity/AMR.jl") +using .AMR + end # module diff --git a/src/exports.jl b/src/exports.jl index faec722498..84511a6fa9 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -106,8 +106,6 @@ export generate_grid, # AdaptiveGrid ForestBWG, - OctreeBWG, - OctantBWG, # Grid coloring create_coloring, diff --git a/test/test_p4est.jl b/test/test_p4est.jl index f314d2848a..4f204abbff 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -1,157 +1,157 @@ @testset "OctantBWG Lookup Tables" begin - @test Ferrite._face(1) == [3,5] - @test Ferrite._face(5) == [1,5] - @test Ferrite._face(12) == [2,4] - @test Ferrite._face(1,1) == 3 && Ferrite._face(1,2) == 5 - @test Ferrite._face(5,1) == 1 && Ferrite._face(5,2) == 5 - @test Ferrite._face(12,1) == 2 && Ferrite._face(12,2) == 4 - @test Ferrite._face(3,1) == 3 && Ferrite._face(3,2) == 6 - - @test Ferrite._face_edge_corners(1,1) == (0,0) - @test Ferrite._face_edge_corners(3,3) == (3,4) - @test Ferrite._face_edge_corners(8,6) == (2,4) - @test Ferrite._face_edge_corners(4,5) == (0,0) - @test Ferrite._face_edge_corners(5,4) == (0,0) - @test Ferrite._face_edge_corners(7,1) == (3,4) - @test Ferrite._face_edge_corners(11,1) == (2,4) - @test Ferrite._face_edge_corners(9,1) == (1,3) - @test Ferrite._face_edge_corners(10,2) == (1,3) - @test Ferrite._face_edge_corners(12,2) == (2,4) - - @test Ferrite.𝒱₃[1,:] == Ferrite.𝒰[1:4,1] == Ferrite._face_corners(3,1) - @test Ferrite.𝒱₃[2,:] == Ferrite.𝒰[1:4,2] == Ferrite._face_corners(3,2) - @test Ferrite.𝒱₃[3,:] == Ferrite.𝒰[5:8,1] == Ferrite._face_corners(3,3) - @test Ferrite.𝒱₃[4,:] == Ferrite.𝒰[5:8,2] == Ferrite._face_corners(3,4) - @test Ferrite.𝒱₃[5,:] == Ferrite.𝒰[9:12,1] == Ferrite._face_corners(3,5) - @test Ferrite.𝒱₃[6,:] == Ferrite.𝒰[9:12,2] == Ferrite._face_corners(3,6) - - @test Ferrite._edge_corners(1) == [1,2] - @test Ferrite._edge_corners(4) == [7,8] - @test Ferrite._edge_corners(12,2) == 8 + @test Ferrite.AMR._face(1) == [3,5] + @test Ferrite.AMR._face(5) == [1,5] + @test Ferrite.AMR._face(12) == [2,4] + @test Ferrite.AMR._face(1,1) == 3 && Ferrite.AMR._face(1,2) == 5 + @test Ferrite.AMR._face(5,1) == 1 && Ferrite.AMR._face(5,2) == 5 + @test Ferrite.AMR._face(12,1) == 2 && Ferrite.AMR._face(12,2) == 4 + @test Ferrite.AMR._face(3,1) == 3 && Ferrite.AMR._face(3,2) == 6 + + @test Ferrite.AMR._face_edge_corners(1,1) == (0,0) + @test Ferrite.AMR._face_edge_corners(3,3) == (3,4) + @test Ferrite.AMR._face_edge_corners(8,6) == (2,4) + @test Ferrite.AMR._face_edge_corners(4,5) == (0,0) + @test Ferrite.AMR._face_edge_corners(5,4) == (0,0) + @test Ferrite.AMR._face_edge_corners(7,1) == (3,4) + @test Ferrite.AMR._face_edge_corners(11,1) == (2,4) + @test Ferrite.AMR._face_edge_corners(9,1) == (1,3) + @test Ferrite.AMR._face_edge_corners(10,2) == (1,3) + @test Ferrite.AMR._face_edge_corners(12,2) == (2,4) + + @test Ferrite.AMR.𝒱₃[1,:] == Ferrite.AMR.𝒰[1:4,1] == Ferrite.AMR._face_corners(3,1) + @test Ferrite.AMR.𝒱₃[2,:] == Ferrite.AMR.𝒰[1:4,2] == Ferrite.AMR._face_corners(3,2) + @test Ferrite.AMR.𝒱₃[3,:] == Ferrite.AMR.𝒰[5:8,1] == Ferrite.AMR._face_corners(3,3) + @test Ferrite.AMR.𝒱₃[4,:] == Ferrite.AMR.𝒰[5:8,2] == Ferrite.AMR._face_corners(3,4) + @test Ferrite.AMR.𝒱₃[5,:] == Ferrite.AMR.𝒰[9:12,1] == Ferrite.AMR._face_corners(3,5) + @test Ferrite.AMR.𝒱₃[6,:] == Ferrite.AMR.𝒰[9:12,2] == Ferrite.AMR._face_corners(3,6) + + @test Ferrite.AMR._edge_corners(1) == [1,2] + @test Ferrite.AMR._edge_corners(4) == [7,8] + @test Ferrite.AMR._edge_corners(12,2) == 8 #Test Figure 3a) of Burstedde, Wilcox, Ghattas [2011] test_ξs = (1,2,3,4) - @test Ferrite._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs + @test Ferrite.AMR._neighbor_corner.((1,),(2,),(1,),test_ξs) == test_ξs #Test Figure 3b) - @test Ferrite._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.𝒫[5,:]...,) + @test Ferrite.AMR._neighbor_corner.((3,),(5,),(3,),test_ξs) == (Ferrite.AMR.𝒫[5,:]...,) end @testset "Index Permutation" begin - for i in 1:length(Ferrite.edge_perm) - @test i == Ferrite.edge_perm_inv[Ferrite.edge_perm[i]] + for i in 1:length(Ferrite.AMR.edge_perm) + @test i == Ferrite.AMR.edge_perm_inv[Ferrite.AMR.edge_perm[i]] end - for i in 1:length(Ferrite.𝒱₂_perm) - @test i == Ferrite.𝒱₂_perm_inv[Ferrite.𝒱₂_perm[i]] + for i in 1:length(Ferrite.AMR.𝒱₂_perm) + @test i == Ferrite.AMR.𝒱₂_perm_inv[Ferrite.AMR.𝒱₂_perm[i]] end - for i in 1:length(Ferrite.𝒱₃_perm) - @test i == Ferrite.𝒱₃_perm_inv[Ferrite.𝒱₃_perm[i]] + for i in 1:length(Ferrite.AMR.𝒱₃_perm) + @test i == Ferrite.AMR.𝒱₃_perm_inv[Ferrite.AMR.𝒱₃_perm[i]] end - for i in 1:length(Ferrite.node_map₂) - @test i == Ferrite.node_map₂_inv[Ferrite.node_map₂[i]] + for i in 1:length(Ferrite.AMR.node_map₂) + @test i == Ferrite.AMR.node_map₂_inv[Ferrite.AMR.node_map₂[i]] end - for i in 1:length(Ferrite.node_map₃) - @test i == Ferrite.node_map₃_inv[Ferrite.node_map₃[i]] + for i in 1:length(Ferrite.AMR.node_map₃) + @test i == Ferrite.AMR.node_map₃_inv[Ferrite.AMR.node_map₃[i]] end end @testset "OctantBWG Encoding" begin # # Tests from Figure 3a) and 3b) of Burstedde et al - o = Ferrite.OctantBWG(3,2,21,3) + o = Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,21,3) b = 3 - @test Ferrite.child_id(o,b) == 5 - @test Ferrite.child_id(Ferrite.parent(o,b),b) == 3 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) - @test Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) == Ferrite.root(3) - o = Ferrite.OctantBWG(3,2,4,3) - @test Ferrite.child_id(o,b) == 4 - @test Ferrite.child_id(Ferrite.parent(o,b),b) == 1 - @test Ferrite.parent(Ferrite.parent(o,b),b) == Ferrite.OctantBWG(3,0,1,b) - @test Ferrite.parent(Ferrite.parent(Ferrite.parent(o,b),b),b) == Ferrite.root(3) - - @test Ferrite.child_id(Ferrite.OctantBWG(2,1,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.OctantBWG(2,1,2,3),3) == 2 - @test Ferrite.child_id(Ferrite.OctantBWG(2,1,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.OctantBWG(2,1,4,3),3) == 4 - @test Ferrite.child_id(Ferrite.OctantBWG(2,2,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,1,3),3) == 1 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,2,3),3) == 2 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,3,3),3) == 3 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,4,3),3) == 4 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,16,3),3) == 8 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,24,3),3) == 8 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,64,3),3) == 8 - @test Ferrite.child_id(Ferrite.OctantBWG(3,2,9,3),3) == 1 + @test Ferrite.AMR.child_id(o,b) == 5 + @test Ferrite.AMR.child_id(Ferrite.AMR.parent(o,b),b) == 3 + @test Ferrite.AMR.parent(Ferrite.AMR.parent(o,b),b) == Ferrite.AMR.Ferrite.AMR.OctantBWG(3,0,1,b) + @test Ferrite.AMR.parent(Ferrite.AMR.parent(Ferrite.AMR.parent(o,b),b),b) == Ferrite.AMR.root(3) + o = Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,4,3) + @test Ferrite.AMR.child_id(o,b) == 4 + @test Ferrite.AMR.child_id(Ferrite.AMR.parent(o,b),b) == 1 + @test Ferrite.AMR.parent(Ferrite.AMR.parent(o,b),b) == Ferrite.AMR.Ferrite.AMR.OctantBWG(3,0,1,b) + @test Ferrite.AMR.parent(Ferrite.AMR.parent(Ferrite.AMR.parent(o,b),b),b) == Ferrite.AMR.root(3) + + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,1,1,3),3) == 1 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,1,2,3),3) == 2 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,1,3,3),3) == 3 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,1,4,3),3) == 4 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,2,1,3),3) == 1 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,1,3),3) == 1 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,2,3),3) == 2 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,3,3),3) == 3 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,4,3),3) == 4 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,16,3),3) == 8 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,24,3),3) == 8 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,64,3),3) == 8 + @test Ferrite.AMR.child_id(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,2,9,3),3) == 1 #maxlevel = 10 takes too long maxlevel = 6 levels = collect(1:maxlevel) morton_ids = [1:2^(2*l) for l in levels] for (level,morton_range) in zip(levels,morton_ids) for morton_id in morton_range - @test Int(Ferrite.morton(OctantBWG(2,level,morton_id,maxlevel),level,maxlevel)) == morton_id + @test Int(Ferrite.AMR.morton(Ferrite.AMR.OctantBWG(2,level,morton_id,maxlevel),level,maxlevel)) == morton_id end end morton_ids = [1:2^(3*l) for l in levels] for (level,morton_range) in zip(levels,morton_ids) for morton_id in morton_range - @test Int(Ferrite.morton(OctantBWG(3,level,morton_id,maxlevel),level,maxlevel)) == morton_id + @test Int(Ferrite.AMR.morton(Ferrite.AMR.OctantBWG(3,level,morton_id,maxlevel),level,maxlevel)) == morton_id end end end @testset "OctantBWG Operations" begin - o = Ferrite.OctantBWG(1,(2,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(0,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(4,0,0)) - @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(2,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(2,2,0)) - @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(2,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(2,0,2)) - @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(2,0,0)), Ferrite.OctantBWG(2,(3,1,1))) - @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(2,0,0)), Ferrite.OctantBWG(3,(5,3,3))) - - o = Ferrite.OctantBWG(1,(0,0,0)) - @test Ferrite.face_neighbor(o,1,2) == Ferrite.OctantBWG(1,(-2,0,0)) - @test Ferrite.face_neighbor(o,2,2) == Ferrite.OctantBWG(1,(2,0,0)) - @test Ferrite.face_neighbor(o,3,2) == Ferrite.OctantBWG(1,(0,-2,0)) - @test Ferrite.face_neighbor(o,4,2) == Ferrite.OctantBWG(1,(0,2,0)) - @test Ferrite.face_neighbor(o,5,2) == Ferrite.OctantBWG(1,(0,0,-2)) - @test Ferrite.face_neighbor(o,6,2) == Ferrite.OctantBWG(1,(0,0,2)) - o = Ferrite.OctantBWG(0,(0,0,0)) - @test Ferrite.descendants(o,2) == (Ferrite.OctantBWG(2,(0,0,0)), Ferrite.OctantBWG(2,(3,3,3))) - @test Ferrite.descendants(o,3) == (Ferrite.OctantBWG(3,(0,0,0)), Ferrite.OctantBWG(3,(7,7,7))) - - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),1,3) == Ferrite.OctantBWG(2,(2,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),4,3) == Ferrite.OctantBWG(2,(2,2,2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),6,3) == Ferrite.OctantBWG(2,(4,0,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),9,3) == Ferrite.OctantBWG(2,(0,-2,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(2,0,0)),12,3) == Ferrite.OctantBWG(2,(4,2,0)) - - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),1,4) == Ferrite.OctantBWG(3,(0,-2,-2)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(3,(0,0,0)),12,4) == Ferrite.OctantBWG(3,(2,2,0)) - - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),1,4) == Ferrite.OctantBWG(2,(0,-4,-4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),2,4) == Ferrite.OctantBWG(2,(0,4,-4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),3,4) == Ferrite.OctantBWG(2,(0,-4,4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),4,4) == Ferrite.OctantBWG(2,(0,4,4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),5,4) == Ferrite.OctantBWG(2,(-4,0,-4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),6,4) == Ferrite.OctantBWG(2,(4,0,-4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),7,4) == Ferrite.OctantBWG(2,(-4,0,4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),8,4) == Ferrite.OctantBWG(2,(4,0,4)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),9,4) == Ferrite.OctantBWG(2,(-4,-4,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),10,4) == Ferrite.OctantBWG(2,(4,-4,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),11,4) == Ferrite.OctantBWG(2,(-4,4,0)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(2,(0,0,0)),12,4) == Ferrite.OctantBWG(2,(4,4,0)) - - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),1,4) == Ferrite.OctantBWG(1,(0,-8,-8)) - @test Ferrite.edge_neighbor(Ferrite.OctantBWG(1,(0,0,0)),12,4) == Ferrite.OctantBWG(1,(8,8,0)) - - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),1,3) == Ferrite.OctantBWG(2,(0,-2,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),4,3) == Ferrite.OctantBWG(2,(4,2,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0,0)),8,3) == Ferrite.OctantBWG(2,(4,2,2)) - - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),1,3) == Ferrite.OctantBWG(2,(0,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),2,3) == Ferrite.OctantBWG(2,(4,-2)) - @test Ferrite.corner_neighbor(Ferrite.OctantBWG(2,(2,0)),4,3) == Ferrite.OctantBWG(2,(4,2)) + o = Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,0)) + @test Ferrite.AMR.face_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)) + @test Ferrite.AMR.face_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(4,0,0)) + @test Ferrite.AMR.face_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,-2,0)) + @test Ferrite.AMR.face_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,2,0)) + @test Ferrite.AMR.face_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,-2)) + @test Ferrite.AMR.face_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,2)) + @test Ferrite.AMR.descendants(o,2) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(3,1,1))) + @test Ferrite.AMR.descendants(o,3) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(2,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(5,3,3))) + + o = Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)) + @test Ferrite.AMR.face_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(-2,0,0)) + @test Ferrite.AMR.face_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,0)) + @test Ferrite.AMR.face_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,-2,0)) + @test Ferrite.AMR.face_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,2,0)) + @test Ferrite.AMR.face_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,-2)) + @test Ferrite.AMR.face_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,2)) + o = Ferrite.AMR.Ferrite.AMR.OctantBWG(0,(0,0,0)) + @test Ferrite.AMR.descendants(o,2) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(3,3,3))) + @test Ferrite.AMR.descendants(o,3) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(0,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(7,7,7))) + + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),1,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,-2,-2)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),4,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,2,2)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),6,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,0,-2)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),9,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,-2,0)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),12,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,2,0)) + + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(0,0,0)),1,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(0,-2,-2)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(0,0,0)),12,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(2,2,0)) + + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),1,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,-4,-4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),2,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,4,-4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),3,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,-4,4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),4,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,4,4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),5,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(-4,0,-4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),6,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,0,-4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),7,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(-4,0,4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),8,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,0,4)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),9,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(-4,-4,0)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),10,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,-4,0)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),11,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(-4,4,0)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)),12,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,4,0)) + + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)),1,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,-8,-8)) + @test Ferrite.AMR.edge_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)),12,4) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(8,8,0)) + + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),1,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,-2,-2)) + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),4,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,2,-2)) + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)),8,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,2,2)) + + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0)),1,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,-2)) + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0)),2,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,-2)) + @test Ferrite.AMR.corner_neighbor(Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0)),4,3) == Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(4,2)) end @testset "OctreeBWG Operations" begin @@ -184,34 +184,34 @@ end # x-----------x-----------x adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells - @test cell isa OctreeBWG - @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) + @test cell isa Ferrite.AMR.OctreeBWG + @test cell.leaves[1] == Ferrite.AMR.OctantBWG(2,0,1,cell.b) end - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,1), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(-8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(0,-8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,3), adaptive_grid.cells[2].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,1), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(-8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,3), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) o = adaptive_grid.cells[1].leaves[1] - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), o) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), o) == OctantBWG(0,(0,-8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,1), o) == OctantBWG(0,(-8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,3), o) == OctantBWG(0,(0,-8)) - - grid_new = Ferrite.creategrid(adaptive_grid) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + + grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 9 @test length(grid_new.conformity_info) == 0 grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) grid.cells[4] = Quadrilateral((grid.cells[4].nodes[2], grid.cells[4].nodes[3], grid.cells[4].nodes[4], grid.cells[4].nodes[1])) - # root mesh in Ferrite notation in p4est notation + # root mesh in Ferrite.AMR notation in p4est notation # x-----------x-----------x x-----------x-----------x # |4 3 3|2 1 1| |3 4 4|2 3 1| # | | | | | | @@ -231,32 +231,32 @@ end # x-----------x-----------x x-----------x-----------x adaptive_grid = ForestBWG(grid,3) for cell in adaptive_grid.cells - @test cell isa OctreeBWG - @test cell.leaves[1] == OctantBWG(2,0,1,cell.b) + @test cell isa Ferrite.AMR.OctreeBWG + @test cell.leaves[1] == Ferrite.AMR.OctantBWG(2,0,1,cell.b) end - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,2), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == OctantBWG(0,(0,-8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(4,4), adaptive_grid.cells[2].leaves[1]) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == OctantBWG(0,(0,8)) - - #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,8)) - #@test Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,-8)) - #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.transform_corner(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1],false) == OctantBWG(0,(8,8)) - #@test Ferrite.transform_corner(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.transform_corner(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == OctantBWG(0,(8,-8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,2), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,4), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + + #@test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,8)) + #@test Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,-8)) + #@test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.AMR.OctantBWG(0,(8,8)) + #@test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,-8)) o = adaptive_grid.cells[1].leaves[1] - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), o) == OctantBWG(0,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), o) == OctantBWG(0,(0,-8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,2), o) == OctantBWG(0,(8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(4,4), o) == OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) #simple first and second level refinement @@ -278,26 +278,26 @@ end # x--x--x | | # | | | | | # x--x--x-----x-----------x - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 4 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,1,m,adaptive_grid.cells[1].b) + @test octant == Ferrite.AMR.OctantBWG(2,1,m,adaptive_grid.cells[1].b) end - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == OctantBWG(1,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == OctantBWG(1,(4,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(1,(0,-4)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(1,(4,-4)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(1,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(1,(0,-4)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,-4)) - grid_new = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 @test length(grid_new.conformity_info) == 4 # octree holds now 3 first level and 4 second level @test length(adaptive_grid.cells[1].leaves) == 7 for (m,octant) in zip(1:4,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(2,2,m,adaptive_grid.cells[1].b) + @test octant == Ferrite.AMR.OctantBWG(2,2,m,adaptive_grid.cells[1].b) end @@ -317,19 +317,19 @@ end # | | | | # x-----x-----x-----------x adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[2]) == OctantBWG(1,(0,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == OctantBWG(2,(4,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == OctantBWG(2,(6,8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[3]) == OctantBWG(1,(0,-4)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == OctantBWG(2,(4,-2)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == OctantBWG(2,(6,-2)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[2]) == Ferrite.AMR.OctantBWG(1,(0,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(2,(4,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[3]) == Ferrite.AMR.OctantBWG(1,(0,-4)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,-2)) - grid_new = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 @test length(grid_new.conformity_info) == 4 @@ -337,10 +337,10 @@ end grid = Ferrite.generate_simple_disc_grid(Quadrilateral, 6) grid.cells[2] = Quadrilateral((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1])) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[5],adaptive_grid.cells[5].leaves[1]) - grid_new = Ferrite.creategrid(adaptive_grid) + grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 23 @test length(grid_new.conformity_info) == 4 @@ -349,21 +349,21 @@ end ################################################################## adaptive_grid = ForestBWG(grid,8) for l in 1:8 - Ferrite.refine_all!(adaptive_grid,l) + Ferrite.AMR.refine_all!(adaptive_grid,l) for tree in adaptive_grid.cells - @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) + @test all(Ferrite.AMR.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end end #check montonicity of ancestor_id for tree in adaptive_grid.cells - ids = Ferrite.ancestor_id.(tree.leaves,(1,),(tree.b,)) + ids = Ferrite.AMR.ancestor_id.(tree.leaves,(1,),(tree.b,)) @test issorted(ids) end #now go back from finest to coarsest for l in 7:-1:0 - Ferrite.coarsen_all!(adaptive_grid) + Ferrite.AMR.coarsen_all!(adaptive_grid) for tree in adaptive_grid.cells - @test all(Ferrite.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) + @test all(Ferrite.AMR.morton.(tree.leaves,l,8) == collect(1:2^(2*l))) end end ######################### @@ -377,75 +377,75 @@ end o = adaptive_grid.cells[1].leaves[1] # faces - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(8,0,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,2), o) == OctantBWG(0,(-8,0,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,4), o) == OctantBWG(0,(0,-8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,6), o) == OctantBWG(0,(0,0,8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,6), o) == OctantBWG(0,(0,0,-8)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,1), o) == OctantBWG(0,(-8,0,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,1), o) == OctantBWG(0,(8,0,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,3), o) == OctantBWG(0,(0,-8,0)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,3), o) == OctantBWG(0,(0,8,0)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(8,5), o) == OctantBWG(0,(0,0,-8)) - @test Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,5), o) == OctantBWG(0,(0,0,8)) - - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,1), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,1), o) - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,3), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,3), o) - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(1,5), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(1,5), o) - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,2), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,2), o) - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,4), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,4), o) - @test_throws BoundsError Ferrite.transform_face(adaptive_grid, FaceIndex(8,6), o) - @test_throws BoundsError Ferrite.transform_face_remote(adaptive_grid, FaceIndex(8,6), o) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) + @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) + + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,1), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,1), o) + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,3), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,3), o) + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,5), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,5), o) + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,2), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,2), o) + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,4), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,4), o) + @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,6), o) + @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,6), o) #corners - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,1), o, false) == OctantBWG(0,(-8,-8,-8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,2), o, false) == OctantBWG(0,(8,-8,-8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,3), o, false) == OctantBWG(0,(-8,8,-8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,4), o, false) == OctantBWG(0,(8,8,-8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,5), o, false) == OctantBWG(0,(-8,-8,8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,6), o, false) == OctantBWG(0,(8,-8,8)) - @test_throws BoundsError Ferrite.transform_corner(adaptive_grid, VertexIndex(1,7), o, false) == OctantBWG(0,(-8,8,8)) - @test Ferrite.transform_corner(adaptive_grid, VertexIndex(1,8), o, false) == OctantBWG(0,(8,8,8)) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,1), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,2), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,3), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,4), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,5), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,6), o, false) - @test_throws BoundsError Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,7), o, false) - Ferrite.transform_corner_remote(adaptive_grid, VertexIndex(1,8), o, false) == OctantBWG(0,(-8,-8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,1), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,2), o, false) == Ferrite.AMR.OctantBWG(0,(8,-8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,3), o, false) == Ferrite.AMR.OctantBWG(0,(-8,8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,4), o, false) == Ferrite.AMR.OctantBWG(0,(8,8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,5), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,6), o, false) == Ferrite.AMR.OctantBWG(0,(8,-8,8)) + @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,7), o, false) == Ferrite.AMR.OctantBWG(0,(-8,8,8)) + @test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,8), o, false) == Ferrite.AMR.OctantBWG(0,(8,8,8)) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,1), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,2), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,3), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,4), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,5), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,6), o, false) + @test_throws BoundsError Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,7), o, false) + Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,8), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,-8)) #edges - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,1), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,2), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,3), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,1), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,2), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,3), o, false) - @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,4), o, false) == OctantBWG(0,(0,8,8)) - @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,4), o, false) == OctantBWG(0,(0,-8,-8)) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,5), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,6), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,7), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,5), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,6), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,7), o, false) - @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,8), o, false) == OctantBWG(0,(8,0,8)) - @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,8), o, false) == OctantBWG(0,(-8,0,-8)) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,9), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,10), o, false) - @test_throws BoundsError Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,11), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,9), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,10), o, false) - @test_throws BoundsError Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,11), o, false) - @test Ferrite.transform_edge(adaptive_grid, EdgeIndex(1,12), o, false) == OctantBWG(0,(8,8,0)) - @test Ferrite.transform_edge_remote(adaptive_grid, EdgeIndex(1,12), o, false) == OctantBWG(0,(-8,-8,0)) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,1), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,2), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,3), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,1), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,2), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,3), o, false) + @test Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,4), o, false) == Ferrite.AMR.OctantBWG(0,(0,8,8)) + @test Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,4), o, false) == Ferrite.AMR.OctantBWG(0,(0,-8,-8)) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,5), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,6), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,7), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,5), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,6), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,7), o, false) + @test Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,8), o, false) == Ferrite.AMR.OctantBWG(0,(8,0,8)) + @test Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,8), o, false) == Ferrite.AMR.OctantBWG(0,(-8,0,-8)) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,9), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,10), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,11), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,9), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,10), o, false) + @test_throws BoundsError Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,11), o, false) + @test Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,12), o, false) == Ferrite.AMR.OctantBWG(0,(8,8,0)) + @test Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,12), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,0)) # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) @@ -471,22 +471,22 @@ end grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) adaptive_grid = ForestBWG(grid,3) - @test Ferrite.transform_corner(adaptive_grid,7,3,OctantBWG(0,(0,0,0)),false) == OctantBWG(0,(-8,8,-8)) + @test Ferrite.AMR.transform_corner(adaptive_grid,7,3,Ferrite.AMR.OctantBWG(0,(0,0,0)),false) == Ferrite.AMR.OctantBWG(0,(-8,8,-8)) #refinement - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 8 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,1,m,adaptive_grid.cells[1].b) + @test octant == Ferrite.AMR.OctantBWG(3,1,m,adaptive_grid.cells[1].b) end - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) @test length(adaptive_grid.cells[1].leaves) == 15 for (m,octant) in zip(1:8,adaptive_grid.cells[1].leaves) - @test octant == OctantBWG(3,2,m,adaptive_grid.cells[1].b) + @test octant == Ferrite.AMR.OctantBWG(3,2,m,adaptive_grid.cells[1].b) end adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) @test length(adaptive_grid.cells[1].leaves) == 15 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) @test all(getproperty.(adaptive_grid.cells[1].leaves[4:11],:l) .== 2) @@ -494,16 +494,16 @@ end adaptive_grid = ForestBWG(grid,5) #go from coarsest to finest uniformly for l in 1:5 - Ferrite.refine_all!(adaptive_grid,l) + Ferrite.AMR.refine_all!(adaptive_grid,l) for tree in adaptive_grid.cells - @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) + @test all(Ferrite.AMR.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end end #now go back from finest to coarsest for l in 4:-1:0 - Ferrite.coarsen_all!(adaptive_grid) + Ferrite.AMR.coarsen_all!(adaptive_grid) for tree in adaptive_grid.cells - @test all(Ferrite.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) + @test all(Ferrite.AMR.morton.(tree.leaves,l,5) == collect(1:2^(3*l))) end end @@ -511,19 +511,19 @@ end grid = generate_grid(Hexahedron,(2,1,1)) # (a) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[3]) - @test adaptive_grid.cells[2].leaves[3+4] == OctantBWG(2,(0,4,2)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[2].leaves[3+4]) == OctantBWG(2,(8,4,2)) + Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[3]) + @test adaptive_grid.cells[2].leaves[3+4] == Ferrite.AMR.OctantBWG(2,(0,4,2)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[2].leaves[3+4]) == Ferrite.AMR.OctantBWG(2,(8,4,2)) # (b) Rotate elements topologically grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[6], grid.cells[1].nodes[7], grid.cells[1].nodes[8], grid.cells[1].nodes[5])) grid.cells[2] = Hexahedron((grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[8], grid.cells[2].nodes[5], grid.cells[2].nodes[6], grid.cells[2].nodes[7])) # grid.cells[2] = Hexahedron((grid.cells[2].nodes[1], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[8], grid.cells[2].nodes[6], grid.cells[2].nodes[2], grid.cells[2].nodes[7], grid.cells[2].nodes[5])) How to rotate along diagonal? :) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) - @test adaptive_grid.cells[2].leaves[6] == OctantBWG(2,(2,0,2)) - @test Ferrite.transform_face(adaptive_grid, FaceIndex(1,3), adaptive_grid.cells[2].leaves[6]) == OctantBWG(2,(4,-2,2)) + Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + @test adaptive_grid.cells[2].leaves[6] == Ferrite.AMR.OctantBWG(2,(2,0,2)) + @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,3), adaptive_grid.cells[2].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2,2)) end @testset "ForestBWG AbstractGrid Interfacing" begin @@ -531,7 +531,7 @@ end grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,maxlevel) for l in 1:maxlevel - Ferrite.refine_all!(adaptive_grid,l) + Ferrite.AMR.refine_all!(adaptive_grid,l) @test getncells(adaptive_grid) == 2^(2*l) * 4 == length(getcells(adaptive_grid)) end end @@ -541,95 +541,95 @@ end #simple one quad with one additional non-allowed non-conformity level grid = generate_grid(Quadrilateral,(1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) - balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + balanced = Ferrite.AMR.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 16 #more complex non-conformity level 3 and 4 that needs to be balanced adaptive_grid = ForestBWG(grid,5) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[15]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[16]) - balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[15]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[16]) + balanced = Ferrite.AMR.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 64 grid = generate_grid(Quadrilateral,(2,1)) adaptive_grid = ForestBWG(grid,2) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 11 + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 11 grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,2) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 19 + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 19 # 2D example with balancing over a corner connection that is not within the topology tables grid = generate_grid(Quadrilateral,(2,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[5]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 23 + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[5]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 23 #corner balance case but rotated grid = generate_grid(Quadrilateral,(2,1)) grid.cells[1] = Quadrilateral((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1])) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 23 + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 23 # 3D case intra treee simple test, non conformity level 2 grid = generate_grid(Hexahedron,(1,1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) - balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[6]) + balanced = Ferrite.AMR.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 43 #3D case intra tree non conformity level 3 at two different places adaptive_grid = ForestBWG(grid,4) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[28]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[29]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[37]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[39]) - balanced = Ferrite.balancetree(adaptive_grid.cells[1]) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[12]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[28]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[29]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[37]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[39]) + balanced = Ferrite.AMR.balancetree(adaptive_grid.cells[1]) @test length(balanced.leaves) == 127 #3D case inter tree non conformity level 3 at two different places grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,4) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - #Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - #Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - transfered_grid_ref = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + #Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + #Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + transfered_grid_ref = Ferrite.AMR.creategrid(adaptive_grid) # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) @@ -655,88 +655,88 @@ end grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) - #Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[4]) + #Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[7]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == length(transfered_grid_ref.cells) @test length(transfered_grid.cells) == 92 # edge balancing for new introduced connection that is not within topology table grid = generate_grid(Hexahedron, (2,1,1)); adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid, [1,2]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid, [4]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid, [5]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 51 + Ferrite.AMR.refine!(adaptive_grid, [1,2]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid, [4]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid, [5]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 51 #another edge balancing case grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid,1) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid,[2,4,6,8]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid,34) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 134 + Ferrite.AMR.refine!(adaptive_grid,1) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid,[2,4,6,8]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid,34) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 134 #yet another edge balancing case grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid,1) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid,[2,4,6,8]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid,30) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 120 + Ferrite.AMR.refine!(adaptive_grid,1) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid,[2,4,6,8]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid,30) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 120 grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 15 + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 15 #yet another edge balancing case grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 43 + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 43 #yet another edge balancing case grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[10]) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[3]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 71 + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[10]) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[3]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 71 #yet another edge balancing case grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) - Ferrite.balanceforest!(adaptive_grid) - Ferrite.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[7]) - Ferrite.balanceforest!(adaptive_grid) - @test Ferrite.getncells(adaptive_grid) == 120 + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[1]) + Ferrite.AMR.balanceforest!(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[4],adaptive_grid.cells[4].leaves[7]) + Ferrite.AMR.balanceforest!(adaptive_grid) + @test Ferrite.AMR.getncells(adaptive_grid) == 120 end @testset "Materializing Grid" begin @@ -747,10 +747,10 @@ end # 2D case with a single tree grid = generate_grid(Quadrilateral,(1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 10 @test length(transfered_grid.nodes) == 19 @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -758,10 +758,10 @@ end #2D case with four trees and somewhat refinement pattern grid = generate_grid(Quadrilateral,(2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 22 @test length(transfered_grid.nodes) == 35 @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -769,19 +769,19 @@ end #more random refinement grid = generate_grid(Quadrilateral,(3,3)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) - Ferrite.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[3]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[3]) - Ferrite.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[5]) - Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) - Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) - Ferrite.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[2]) + Ferrite.AMR.refine!(adaptive_grid.cells[3],adaptive_grid.cells[3].leaves[3]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[3]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[5]) + Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 45 @test length(transfered_grid.nodes) == 76 @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -793,10 +793,10 @@ end # 3D case with a single tree grid = generate_grid(Hexahedron,(1,1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 8+7+7 @test length(transfered_grid.nodes) == 65 @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -804,31 +804,31 @@ end # Test only Interoctree by face connection grid = generate_grid(Hexahedron,(2,1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 16 @test length(transfered_grid.nodes) == 45 @test unique(transfered_grid.nodes) == transfered_grid.nodes #rotate the case around grid = generate_grid(Hexahedron,(1,2,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 16 @test length(transfered_grid.nodes) == 45 @test unique(transfered_grid.nodes) == transfered_grid.nodes grid = generate_grid(Hexahedron,(1,1,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 16 @test length(transfered_grid.nodes) == 45 @test unique(transfered_grid.nodes) == transfered_grid.nodes grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 8^2 @test length(transfered_grid.nodes) == 125 # 5 per edge @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -857,8 +857,8 @@ end # |1 3 2|4 1 1| # x-----------x-----------x adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - transfered_grid = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine_all!(adaptive_grid,1) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 8^2 @test length(transfered_grid.nodes) == 125 # 5 per edge @test unique(transfered_grid.nodes) == transfered_grid.nodes @@ -869,8 +869,8 @@ end #Easy Intraoctree grid = generate_grid(Hexahedron,(1,1,1)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine_all!(adaptive_grid,1) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) # x-----------x-----------x # | | | # | | | @@ -888,13 +888,13 @@ end # | | | | # | | | | # x-----x-----x-----------x - transfered_grid = Ferrite.creategrid(adaptive_grid) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.conformity_info) == 12 # Easy Interoctree grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) # x-----------x-----------x # | | | # | | | @@ -912,7 +912,7 @@ end # | | | | # | | | | # x-----x-----x-----------x - transfered_grid = Ferrite.creategrid(adaptive_grid) + transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.conformity_info) == 12 #rotate the case from above in the first cell around @@ -921,8 +921,8 @@ end grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[4+2], grid.cells[1].nodes[4+3], grid.cells[1].nodes[4+4], grid.cells[1].nodes[4+1])) grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[4+2], grid.cells[1].nodes[4+3], grid.cells[1].nodes[4+4], grid.cells[1].nodes[4+1])) adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - transfered_grid_rotated = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid_rotated = Ferrite.AMR.creategrid(adaptive_grid) @test transfered_grid_rotated.conformity_info[5] == [28,25] @test transfered_grid_rotated.conformity_info[20] == [10,15] @test transfered_grid_rotated.conformity_info[30] == [15,18] @@ -960,8 +960,8 @@ end # |1 3 2|4 1 1| # x-----------x-----------x adaptive_grid = ForestBWG(grid,3) - Ferrite.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) - transfered_grid_rotated = Ferrite.creategrid(adaptive_grid) + Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) + transfered_grid_rotated = Ferrite.AMR.creategrid(adaptive_grid) @test transfered_grid_rotated.conformity_info[11] == [1,7] @test transfered_grid_rotated.conformity_info[10] == [3,7] end From 0eec30c7d130e15f60a91b1c24805bcedb316164 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 11:17:47 +0200 Subject: [PATCH 111/143] adjust devdocs to submodule --- docs/src/devdocs/AMR.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index a5a02e490b..810b4e420b 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -168,13 +168,13 @@ OctantBWG{2,4,4} There are multiple useful functions to compute information about an octant e.g. parent, childs, etc. ```@docs -Ferrite.isancestor -Ferrite.morton -Ferrite.children -Ferrite.vertices(octant::OctantBWG, b::Integer) -Ferrite.edges(octant::OctantBWG{3}, b::Integer) -Ferrite.faces(octant::OctantBWG, b::Integer) -Ferrite.transform_pointBWG +Ferrite.AMR.isancestor +Ferrite.AMR.morton +Ferrite.AMR.children +Ferrite.AMR.vertices +Ferrite.AMR.edges +Ferrite.AMR.faces +Ferrite.AMR.transform_pointBWG ``` ### Intraoctree operation @@ -184,10 +184,10 @@ These operations are useful to collect unique entities within a single octree or [BWG2011](@citet) Algorithm 5, 6, and 7 describe the following intraoctree operations: ```@docs -Ferrite.corner_neighbor -Ferrite.edge_neighbor -Ferrite.face_neighbor -Ferrite.possibleneighbors +Ferrite.AMR.corner_neighbor +Ferrite.AMR.edge_neighbor +Ferrite.AMR.face_neighbor +Ferrite.AMR.possibleneighbors ``` ### Interoctree operation @@ -197,18 +197,18 @@ Thereby, one needs to account for topological connections between the octrees as [BWG2011](@citet) Algorithm 8, 10, and 12 explain the algorithms that are implemented in the following functions: ```@docs -Ferrite.transform_corner -Ferrite.transform_edge -Ferrite.transform_face +Ferrite.AMR.transform_corner +Ferrite.AMR.transform_edge +Ferrite.AMR.transform_face ``` Note that we flipped the input and to expected output logic a bit to the proposed algorithms of the paper. However, the original proposed versions are implemented as well in: ```@docs -Ferrite.transform_corner_remote -Ferrite.transform_edge_remote -Ferrite.transform_face_remote +Ferrite.AMR.transform_corner_remote +Ferrite.AMR.transform_edge_remote +Ferrite.AMR.transform_face_remote ``` despite being never used in the code base so far. From 20d0130a131e94af74f5d4b6dbf9145db528b920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 12:21:28 +0200 Subject: [PATCH 112/143] get_coordinate_eltype in vtk_grid --- src/Export/VTK.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index d95a8e4cd6..03bb9539cd 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -63,7 +63,7 @@ function WriteVTK.vtk_grid(filename::AbstractString, grid::AbstractGrid{dim}; kw celltype = Ferrite.cell_to_vtkcell(typeof(cell)) push!(cls, MeshCell(celltype, nodes_to_vtkorder(cell))) end - coords = reshape(reinterpret(eltype(getnodes(grid,1).x), getnodes(grid)), (dim, getnnodes(grid))) + coords = reshape(reinterpret(get_coordinate_eltype(grid), getnodes(grid)), (dim, getnnodes(grid))) return vtk_grid(filename, coords, cls; kwargs...) end function WriteVTK.vtk_grid(filename::AbstractString, dh::AbstractDofHandler; kwargs...) From 9ddca47a207570a554fba808791ef43df17de804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 12:27:55 +0200 Subject: [PATCH 113/143] use for now different mesh for docs build --- docs/src/literate-tutorials/adaptivity.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 9b6f13163a..f5ef6a83fd 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,5 +1,6 @@ using Ferrite, FerriteGmsh, SparseArrays -grid = togrid("l.msh"); +#grid = togrid("l.msh"); +grid = generate_grid(Quadrilateral,(2,2)) grid = ForestBWG(grid,25) Ferrite.refine_all!(grid,1) Ferrite.refine_all!(grid,2) From b4f7c4f722e15d0d7ddd8fa932ec1a15990253da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 12:35:43 +0200 Subject: [PATCH 114/143] export refine_all into Ferrite namespace --- src/Adaptivity/AMR.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Adaptivity/AMR.jl b/src/Adaptivity/AMR.jl index 83e706e5a0..66012bf066 100644 --- a/src/Adaptivity/AMR.jl +++ b/src/Adaptivity/AMR.jl @@ -9,6 +9,7 @@ include("constraints.jl") export ForestBWG, refine!, + refine_all!, coarsen!, balanceforest!, creategrid, From d11504fbda79cc0c32a854a9b976754f700a4c9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Fri, 10 May 2024 13:05:31 +0200 Subject: [PATCH 115/143] fix doc build --- docs/src/literate-tutorials/adaptivity.jl | 23 +++++++++---------- .../src/literate-tutorials/heat_adaptivity.jl | 18 +++++++-------- src/Grid/grid.jl | 1 + 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index f5ef6a83fd..ea70872259 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -33,8 +33,8 @@ function assemble_cell!(ke, fe, cellvalues, material, ue) dΩ = getdetJdV(cellvalues, q_point) for i in 1:n_basefuncs - ∇Nᵢ = shape_gradient(cellvalues, q_point, i)# shape_symmetric_gradient(cellvalues, q_point, i) - fe[i] += σ ⊡ ∇Nᵢ * dΩ # add internal force to residual + ∇Nᵢ = shape_gradient(cellvalues, q_point, i) + fe[i] += σ ⊡ ∇Nᵢ * dΩ for j in 1:n_basefuncs ∇ˢʸᵐNⱼ = shape_symmetric_gradient(cellvalues, q_point, j) ke[i, j] += (∂σ∂ε ⊡ ∇ˢʸᵐNⱼ) ⊡ ∇Nᵢ * dΩ @@ -52,7 +52,7 @@ function assemble_global!(K, f, a, dh, cellvalues, material) assembler = start_assemble(K, f) ## Loop over all cells for cell in CellIterator(dh) - reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + reinit!(cellvalues, cell) @views ue = a[celldofs(cell)] ## Compute element contribution assemble_cell!(ke, fe, cellvalues, material, ue) @@ -64,9 +64,9 @@ end function solve(grid) dim = 2 - order = 1 # linear interpolation - ip = Lagrange{RefQuadrilateral, order}()^dim # vector valued interpolation - qr = QuadratureRule{RefQuadrilateral}(2) # 1 quadrature point + order = 1 + ip = Lagrange{RefQuadrilateral, order}()^dim + qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); dh = DofHandler(grid) @@ -91,19 +91,19 @@ end function compute_fluxes(u,dh) ip = Lagrange{RefQuadrilateral, 1}()^2 - # Superconvergent points + ## Superconvergent points qr = QuadratureRule{RefQuadrilateral}(1) cellvalues_sc = CellValues(qr, ip); - # "Normal" quadrature points for the fluxes + ## "Normal" quadrature points for the fluxes qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); - # Buffers + ## Buffers σ_gp_sc = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() σ_gp_sc_loc = Vector{SymmetricTensor{2,2,Float64,3}}() σ_gp = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() σ_gp_loc = Vector{SymmetricTensor{2,2,Float64,3}}() for (cellid,cell) in enumerate(CellIterator(dh)) - reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + reinit!(cellvalues, cell) reinit!(cellvalues_sc, cell) @views ue = u[celldofs(cell)] for q_point in 1:getnquadpoints(cellvalues) @@ -118,7 +118,7 @@ function compute_fluxes(u,dh) end push!(σ_gp,copy(σ_gp_loc)) push!(σ_gp_sc,copy(σ_gp_sc_loc)) - # Reset buffer for local points + ## Reset buffer for local points empty!(σ_gp_loc) empty!(σ_gp_sc_loc) end @@ -146,7 +146,6 @@ function solve_adaptive(initial_grid) error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) - #error += norm((σ_dof_at_sc - σ_gp_sc[cellid][1])/σ_gp_sc[cellid][1]) error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) * getdetJdV(cellvalues_tensorial,q_point) end if error > 0.01 diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index 9a55ad50b7..4163be3b3a 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -23,8 +23,8 @@ function assemble_cell!(ke, fe, cellvalues, ue, coords) dΩ = getdetJdV(cellvalues, q_point) for i in 1:n_basefuncs Nᵢ = shape_value(cellvalues, q_point, i) - ∇Nᵢ = shape_gradient(cellvalues, q_point, i)# shape_symmetric_gradient(cellvalues, q_point, i) - fe[i] += analytical_rhs(x) * Nᵢ * dΩ # add internal force to residual + ∇Nᵢ = shape_gradient(cellvalues, q_point, i) + fe[i] += analytical_rhs(x) * Nᵢ * dΩ for j in 1:n_basefuncs ∇Nⱼ = shape_gradient(cellvalues, q_point, j) ke[i, j] += ∇Nⱼ ⋅ ∇Nᵢ * dΩ @@ -42,7 +42,7 @@ function assemble_global!(K, f, a, dh, cellvalues) assembler = start_assemble(K, f) ## Loop over all cells for cell in CellIterator(dh) - reinit!(cellvalues, cell) # update spatial derivatives based on element coordinates + reinit!(cellvalues, cell) @views ue = a[celldofs(cell)] ## Compute element contribution coords = getcoordinates(cell) @@ -55,9 +55,9 @@ end function solve(grid) dim = 2 - order = 1 # linear interpolation - ip = Lagrange{RefQuadrilateral, order}() # vector valued interpolation - qr = QuadratureRule{RefQuadrilateral}(2) # 1 quadrature point + order = 1 + ip = Lagrange{RefQuadrilateral, order}() + qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); dh = DofHandler(grid) @@ -84,13 +84,13 @@ end function compute_fluxes(u,dh) ip = Lagrange{RefQuadrilateral, 1}() - # Normal quadrature points + ## Normal quadrature points qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); - # Superconvergent point + ## Superconvergent point qr_sc = QuadratureRule{RefQuadrilateral}(1) cellvalues_sc = CellValues(qr_sc, ip); - #Buffers + ## Buffers σ_gp = Vector{Vector{Vec{2,Float64}}}() σ_gp_loc = Vector{Vec{2,Float64}}() σ_gp_sc = Vector{Vector{Vec{2,Float64}}}() diff --git a/src/Grid/grid.jl b/src/Grid/grid.jl index 9e96e6567f..916d4b6ce0 100644 --- a/src/Grid/grid.jl +++ b/src/Grid/grid.jl @@ -345,6 +345,7 @@ end Get the datatype for a single point in the grid. """ +get_coordinate_type(grid::AbstractGrid{dim}) where {dim} = get_coordinate_type(first(getnodes(grid))) get_coordinate_type(::Grid{dim,C,T}) where {dim,C,T} = Vec{dim,T} # Node is baked into the mesh type. """ From ffcdf96fe31611848587fe2375b2e59835390228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 13 May 2024 09:36:05 +0200 Subject: [PATCH 116/143] Apply suggestions from code review Co-authored-by: Knut Andreas Meyer --- docs/src/devdocs/AMR.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index 810b4e420b..d15f644461 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -1,8 +1,8 @@ -# AMR +# Adaptive Mesh Refinement (AMR) ## P4est -All of it is based on these papers: +Ferrite's P4est implementation is based on these papers: - [BWG2011](@citet) - [IBWG2015](@citet) @@ -13,7 +13,7 @@ Look into the issues of Ferrite.jl and search for the AMR tag. ### Important Concepts -One of the most important concepts, where everything is based on, are space filling curves (SFC). +One of the most important concepts, which everything is based on, are space filling curves (SFC). In particular, [Z-order (also named Morton order, Morton space-filling curves)](https://en.wikipedia.org/wiki/Z-order_curve) are used in p4est. The basic idea is that each Octant (in 3D) or quadrant (in 2D) can be encoded by 2 quantities @@ -77,7 +77,8 @@ julia> Ferrite._compute_size(3,1) Construct all level 1 octants based on mortonid: ```julia # note the arguments are dim,level,mortonid,maximumlevel -julia> oct = OctantBWG(2,1,1,3) +julia> dim = 2; level = 1; maximumlevel=3 +julia> oct = OctantBWG(dim, level, 1, maximumlevel) OctantBWG{2,4,4} l = 1 xy = 0,0 From 9af1812dae33a1c8bf651ba4b4ce91661d5a8485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 13 May 2024 09:51:15 +0200 Subject: [PATCH 117/143] improve devdocs --- docs/src/devdocs/AMR.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index d15f644461..26ff670d16 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -34,6 +34,11 @@ struct OctantBWG{dim, N, T} <: AbstractCell{RefHypercube{dim}} end ``` whenever coordinates are considered we follow the z order logic, meaning x before y before z. +Note that the acronym BWG stands for the initials of the surname of the authors of the p4est paper. +The coordinates of an octant are described in the *octree coordinate system* which goes from $[0,2^b]^{dim}$. +The parameter $b$ describes the maximum level of refinement and is set a priori. +Another important aspect of the octree coordinate system is, that it is a discrete integer coordinate system. +The size of an octant at the lowest possible level `b` is always 1, sometimes these octants are called atoms. The octree is implemented as: ```julia @@ -48,7 +53,6 @@ end So, only the leaves of the tree are stored and not any intermediate refinement level. The field `b` is the maximum refinement level and is crucial. This parameter determines the size of the octree coordinate system. The octree coordinate system is the coordinate system in which the coordinates `xyz` of any `octant::OctantBWG` are described. -This coordinate system goes from [0,2^b]^{dim}. The size of an octant is always 1 at the lowest possible level `b`. ### Examples @@ -58,42 +62,43 @@ So, our root is on level 0 of size 8 and has the lower left coordinates `(0,0)` ```julia # different constructors available, first one OctantBWG(dim,level,mortonid,maximumlevel) # other possibility by giving directly level and a tuple of coordinates OctantBWG(level,(x,y)) -julia> oct = OctantBWG(2,0,1,3) +julia> dim = 2; level = 0; maximumlevel=3 +julia> oct = OctantBWG(dim,level,1,maximumlevel) OctantBWG{2,4,4} l = 0 xy = 0,0 ``` The size of octants at a specific level can be computed by a simple operation ```julia -julia> Ferrite._compute_size(3,0) +julia> Ferrite.AMR._compute_size(#=b=#3,#=l=#0) 8 ``` Now, to fully understand the octree coordinate system we go a level down, i.e. we cut the space in $x$ and $y$ in half. This means, that the octants are now of size 4. ```julia -julia> Ferrite._compute_size(3,1) +julia> Ferrite.AMR._compute_size(3,1) 4 ``` Construct all level 1 octants based on mortonid: ```julia # note the arguments are dim,level,mortonid,maximumlevel julia> dim = 2; level = 1; maximumlevel=3 -julia> oct = OctantBWG(dim, level, 1, maximumlevel) +julia> oct = Ferrite.AMR.OctantBWG(dim, level, 1, maximumlevel) OctantBWG{2,4,4} l = 1 xy = 0,0 -julia> o = OctantBWG(2,1,2,3) +julia> oct = Ferrite.AMR.OctantBWG(dim, level, 2, maximumlevel) OctantBWG{2,4,4} l = 1 xy = 4,0 -julia> o = OctantBWG(2,1,3,3) +julia> oct = Ferrite.AMR.OctantBWG(dim, level, 3, maximumlevel) OctantBWG{2,4,4} l = 1 xy = 0,4 -julia> o = OctantBWG(2,1,4,3) +julia> oct = Ferrite.AMR.OctantBWG(dim, level, 4, maximumlevel) OctantBWG{2,4,4} l = 1 xy = 4,4 @@ -143,7 +148,7 @@ What we see above is just the `leafindex`, i.e. the index where you find this le Let's try to construct the lower right based on the morton index on level 1 ```julia -julia> o = OctantBWG(2,1,8,3) +julia> o = Ferrite.OctantBWG(2,1,8,3) ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) Stacktrace: [1] OctantBWG(dim::Int64, l::Int32, m::Int32, b::Int32) @@ -158,7 +163,7 @@ The assertion expresses that it is not possible to construct a morton index 8 oc The morton index of the lower right cell is 2 on level 1. ```julia -julia> o = OctantBWG(2,1,2,3) +julia> o = Ferrite.AMR.OctantBWG(2,1,2,3) OctantBWG{2,4,4} l = 1 xy = 4,0 From 79ac08d4442b83b42a76f35de87157400466a9f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 13 May 2024 09:54:52 +0200 Subject: [PATCH 118/143] add relation of size computation and remove once the function call --- docs/src/devdocs/AMR.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index 26ff670d16..2b70305982 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -73,12 +73,9 @@ The size of octants at a specific level can be computed by a simple operation julia> Ferrite.AMR._compute_size(#=b=#3,#=l=#0) 8 ``` +This computation is based on the relation $\text{size}=2^{b-l}$. Now, to fully understand the octree coordinate system we go a level down, i.e. we cut the space in $x$ and $y$ in half. -This means, that the octants are now of size 4. -```julia -julia> Ferrite.AMR._compute_size(3,1) -4 -``` +This means, that the octants are now of size $2^{3-1}=4$. Construct all level 1 octants based on mortonid: ```julia # note the arguments are dim,level,mortonid,maximumlevel @@ -149,7 +146,7 @@ Let's try to construct the lower right based on the morton index on level 1 ```julia julia> o = Ferrite.OctantBWG(2,1,8,3) -ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) +ERROR: AssertionError: m ≤ (one(T) + one(T)) ^ (dim * l) # 8 > 4 Stacktrace: [1] OctantBWG(dim::Int64, l::Int32, m::Int32, b::Int32) @ Ferrite ~/repos/Ferrite.jl/src/Adaptivity/AdaptiveCells.jl:23 From 47c966ef27bb7c87c7bcce2a8fd79a065de07040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 13 May 2024 10:21:49 +0200 Subject: [PATCH 119/143] Update docs/src/devdocs/AMR.md Co-authored-by: Kristoffer Carlsson --- docs/src/devdocs/AMR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index 2b70305982..4748a4f70f 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -79,7 +79,7 @@ This means, that the octants are now of size $2^{3-1}=4$. Construct all level 1 octants based on mortonid: ```julia # note the arguments are dim,level,mortonid,maximumlevel -julia> dim = 2; level = 1; maximumlevel=3 +julia> dim = 2; level = 1; maximumlevel = 3 julia> oct = Ferrite.AMR.OctantBWG(dim, level, 1, maximumlevel) OctantBWG{2,4,4} l = 1 From e7a48563cf4ea79f85c9494d8f768c4607119acb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 15 May 2024 15:03:16 +0200 Subject: [PATCH 120/143] multiple corner connections test in 2D by disc discretization --- test/test_p4est.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 4f204abbff..bd51c10a36 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -964,4 +964,14 @@ end transfered_grid_rotated = Ferrite.AMR.creategrid(adaptive_grid) @test transfered_grid_rotated.conformity_info[11] == [1,7] @test transfered_grid_rotated.conformity_info[10] == [3,7] + + # multiple corner connections in 2D by disc discretization + grid = Ferrite.generate_simple_disc_grid(Quadrilateral,10) + adaptive_grid = ForestBWG(grid,3) + @test getncells(adaptive_grid) == 10 + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[3]) + @test getncells(adaptive_grid) == 16 + Ferrite.balanceforest!(adaptive_grid) + @test getncells(adaptive_grid) == 9*4 + 3 + 4 end From 731dfbb0f0733d943de90a55c0a7c31b82704d2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 15 May 2024 15:48:16 +0200 Subject: [PATCH 121/143] include multiple edge connection case with tests --- src/Adaptivity/BWG.jl | 10 +++++----- src/Grid/grid_generators.jl | 27 ++++++++++++++++++++++++++- test/test_p4est.jl | 11 +++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 400befa321..ece72ccced 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -1055,11 +1055,11 @@ function balanceforest!(forest::ForestBWG{dim}) where dim continue end isempty(ec) && continue - @assert length(ec) == 1 - !contains_edge(rootedges[s_i],pivot_edge) && continue - ec = ec[1] - k′, e′ = ec[1], edge_perm_inv[ec[2]] - balance_edge(forest,k′,e′,o,s) + for edge_connection in ec + !contains_edge(rootedges[s_i],pivot_edge) && continue + k′, e′ = edge_connection[1], edge_perm_inv[edge_connection[2]] + balance_edge(forest,k′,e′,o,s) + end end end end diff --git a/src/Grid/grid_generators.jl b/src/Grid/grid_generators.jl index be10fe4de8..b55f2ff56a 100644 --- a/src/Grid/grid_generators.jl +++ b/src/Grid/grid_generators.jl @@ -566,7 +566,7 @@ function generate_grid(::Type{Tetrahedron}, cells_per_dim::NTuple{3,Int}, left:: return Grid(cells, nodes, facesets=facesets, boundary_matrix=boundary_matrix) end -function generate_simple_disc_grid(::Type{Quadrilateral}, n; radius= 1.0) +function generate_simple_disc_grid(::Type{Quadrilateral}, n; radius = 1.0) nnodes = 2n + 1 θ = deg2rad(360/2n) @@ -582,3 +582,28 @@ function generate_simple_disc_grid(::Type{Quadrilateral}, n; radius= 1.0) return Grid(elements, Node.(nodes); facesets=facesets) end + +function generate_simple_disc_grid(::Type{Hexahedron}, n; radius = 1.0, layers = 1, height = 1.0) + nnodes = 2n + 1 + θ = deg2rad(360/2n) + + nodepos_bottom = Vec((0.0,radius,0.0)) + nodes = [rotate(nodepos_bottom, Vec{3}((0,0,1)), θ*i) for i ∈ 0:(2n-1)] + push!(nodes, Vec((0.0,0.0,0.0))) + + # TODO generalize for n layers by looping over layers + nodepos_layer = Vec((0.0,radius,height)) + nodes_layer = [rotate(nodepos_layer, Vec{3}((0,0,1)), θ*i) for i ∈ 0:(2n-1)] + push!(nodes_layer, Vec((0.0,0.0,1.0))) + nodes = vcat(nodes,nodes_layer) + + elements = [Hexahedron((2i-1==0 ? nnodes-1 : 2i-1,2i,2i+1 == nnodes ? 1 : 2i+1,nnodes,2i-1==0 ? nnodes-1 : 2i-1+(2*n+1),2i+(2*n+1),2i+1 == nnodes ? (2*n+2) : 2i+1+(2*n+1),nnodes*(layers+1))) for i ∈ 1:n*layers] + + facesets = Dict( + "side" => Set([FaceIndex(i,1) for i ∈ 1:n]) ∪ Set([FaceIndex(i,2) for i ∈ 1:n]), + "top" => Set([FaceIndex(i,5) for i ∈ 1:n]), + "bottom" => Set([FaceIndex(i,6) for i ∈ 1:n]), + ) + + return Grid(elements, Node.(nodes))#; facesets=facesets) +end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index bd51c10a36..e905b6ad6a 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -974,4 +974,15 @@ end @test getncells(adaptive_grid) == 16 Ferrite.balanceforest!(adaptive_grid) @test getncells(adaptive_grid) == 9*4 + 3 + 4 + + # multiple corner connections in 3D by cylinder discretization + grid = Ferrite.generate_simple_disc_grid(Hexahedron,10) + adaptive_grid = ForestBWG(grid,3) + @test getncells(adaptive_grid) == 10 + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + @test getncells(adaptive_grid) == 17 + Ferrite.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[3]) + @test getncells(adaptive_grid) == 24 + Ferrite.balanceforest!(adaptive_grid) + @test getncells(adaptive_grid) == 9*8 + 7 + 8 end From 361f8e0f4ac196008f6f106e9b14b8f7a1178fb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Tue, 21 May 2024 13:50:45 +0200 Subject: [PATCH 122/143] add some topics docs on AMR --- docs/make.jl | 3 +- docs/src/topics/amr.md | 141 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 docs/src/topics/amr.md diff --git a/docs/make.jl b/docs/make.jl index f3a68d1689..d6375d3dd5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -68,7 +68,8 @@ bibtex_plugin = CitationBibliography( "topics/boundary_conditions.md", "topics/constraints.md", "topics/grid.md", - "topics/export.md" + "topics/export.md", + "topics/amr.md" ], "Reference" => [ "Reference overview" => "reference/index.md", diff --git a/docs/src/topics/amr.md b/docs/src/topics/amr.md new file mode 100644 index 0000000000..b2353c0927 --- /dev/null +++ b/docs/src/topics/amr.md @@ -0,0 +1,141 @@ +# Adaptive Mesh Refinement + +Adaptive mesh refinement (AMR) is a computational technique used in finite element analysis to enhance the accuracy and efficiency of simulations. +It involves dynamically adjusting the mesh resolution based on some criteria. +By refining the mesh in regions where the solution exhibits features of interest, AMR ensures that computational resources are concentrated where they are most needed, leading to more accurate results without a proportional increase in computational cost. +This refinement can be achieved in different fashions by either adjusting the mesh size (h-adaptivity), the polynomial order of the Ansatz functions (p-adaptivity) or the nodal positions (r-adaptivity). + +In Ferrite.jl, adaptivity is achieved through a p4est type of implementation which covers h-adaptivity. +This approach is designed to handle unstructured hexahedral (in 3D) and quadrilateral (in 2D) meshes. +A further restriction of the p4est type of implementation is isotropic refinement, meaning that elements are subdivided into smaller elements of the same shape. + +In AMR different phenomena and vocabulary emerge which we group into the following aspects + +- hanging nodes +- balancing +- error estimation + +## Hanging Nodes +### What Are Hanging Nodes? + +Hanging nodes occur during the process of mesh refinement. +When a mesh is refined, some elements may be subdivided while their neighboring elements are not. +This results in nodes that lie on the edges or faces of coarser elements but do not coincide with the existing nodes of those elements. +These intermediate nodes are referred to as hanging nodes. + +Consider the following situation: +``` +x-----------x-----------x x-----------x-----------x +|7 |8 |9 |7 |8 |9 +| | | | | | +| | | | | | +| | | | | | +| | | | | | +| | | | | | +| | | refinement | | | +x-----------x-----------x --------> x-----x-----x-----------x +|4 |5 |6 |4 |14 |5 |6 +| | | | | | | +| | | | | | | +| | | x-----x-----x | +| | | |11 |12 |13 | +| | | | | | | +| | | | | | | +x-----------x-----------x x-----x-----x-----------x + 1 2 3 1 10 2 3 +``` +The new introduced nodes 10, 11 and 12 are shared and therefore are not hanging nodes. +However, the nodes 14 and 13 are not shared with the neighboring coarser element and therefore hanging. + +### Implications of Hanging Nodes + +The presence of hanging nodes poses the challenge of non-conformity: +A mesh with hanging nodes is non-conforming because the finite element mesh no longer adheres to the requirement that elements meet only at their nodes or along entire edges or faces. +This lack of conformity can lead to difficulties in maintaining the continuity of the solution across the mesh. +However we can recover the continuity of the solution by constraining the hanging nodes. + + +### How to Treat Hanging Nodes + +To address the issues introduced by hanging nodes, specific strategies and constraints are employed. +The degrees of freedom (DoFs) associated with hanging nodes are constrained based on the surrounding coarser mesh elements. +For example, in a linear finite element method, the value at a hanging node can be constrained to be the average of the values at the adjacent vertices of the coarser element. +As for the example above node 13 could be constrained to $\boldsymbol{u}[13]=0.5\boldsymbol{u}[5]+0.5\boldsymbol{u}[2]$. +As soon as higher polynomial degrees are involved, things become more involved. +In Ferrite, a conformity constraint can be constructed with the ConstraintHandler when using a DofHandler which has been constructed with a grid passed from `Ferrite.creategrid(adaptive_grid::ForestBWG)`. +This conformity constraint ensures that each hanging node is constrained appropriately. + +```julia +ch = ConstraintHandler(dh) +add!(ch, Ferrite.ConformityConstraint(:u)) +``` + +## Balancing + +Hanging nodes can depend in a nested fashion on other hanging nodes. +This case complicates the `ConformityConstraint` massively and is therefore often prohibited by the act of balancing. +Mesh balancing refers to the process of ensuring a smooth and gradual transition in element sizes across the computational mesh. +This is especially important in adaptive mesh refinement (AMR), where different regions of the mesh may undergo varying levels of refinement based on the solution's characteristics. +The goal of mesh balancing is to prevent isolated regions of excessive refinement, which can lead to inefficiencies in terms of constructing conformity constraints and numerical instability. +A famous balancing approach is the 2:1 balance, where it is ensured that a hanging node only depends on non-hanging nodes. +Therefore, exactly one level of non-conformity is allowed. +An example of this process is visualized below. + +``` +x-----------x-----------x x-----------x-----------x +| | | | | | | +| | | | | | | +| | | | | | | +| | | |-----x-----| | +| | | | | | | +| | | | | | | +| | | balancing | | | | +x-----x--x--x-----------x --------> x-----x--x--x-----------x +| | | | | | | | | | | +| x--x--x | | x--x--x | | +| | | | | | | | | | | +x-----x--x--x | x-----x--x--x-----x-----| +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +x-----x-----x-----------x x-----x-----x-----------x +``` + +Note that in the example above, the top right element hasn't been refined. +However, in some cases it is advantageous to do so in order to have a smoother transition in element size. +Therefore, by default, the adaptive mesh is also refined over this vertex, leading to the following result: + +``` +x-----------x-----------x x-----------x-----------x +| | | | | | | | +| | | | | | | | +| | | | | | | | +| | | |-----x-----|-----x-----| +| | | | | | | | +| | | | | | | | +| | | balancing | | | | | +x-----x--x--x-----------x --------> x-----x--x--x-----------x +| | | | | | | | | | | +| x--x--x | | x--x--x | | +| | | | | | | | | | | +x-----x--x--x | x-----x--x--x-----x-----| +| | | | | | | | | +| | | | | | | | | +| | | | | | | | | +x-----x-----x-----------x x-----x-----x-----------x +``` + +In Ferrite's p4est implementation, one can call `balanceforest!` to balance the adaptive grid. + +```julia +Ferrite.balanceforest!(adaptive_grid) +``` + +## Error Estimation +Error estimation is a critical component of adaptive mesh refinement (AMR) in finite element analysis. +The primary objective of error estimation is to identify regions of the computational domain where the numerical solution is less accurate and requires further refinement. +Accurate error estimation guides the adaptive refinement process, ensuring that computational resources are concentrated in areas where they will have the most significant impact on improving solution accuracy. + +In practice, error estimators evaluate the local error in the finite element solution by comparing it to an approximation of the true solution. +Common techniques include residual-based error estimation, where the residuals of the finite element equations are used to estimate the local error, and recovery-based methods, which involve constructing a higher-order approximation of the solution and assessing the difference between this approximation and the finite element solution. +By identifying elements with high estimated errors, these methods provide a targeted approach to mesh refinement, enhancing the overall efficiency and accuracy of the simulation. From d938b70bfa7a8dfb8819d3a02b6243b0f6d2d91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 10:10:49 +0200 Subject: [PATCH 123/143] update examples and vtk export for abstractgrid --- docs/src/literate-tutorials/adaptivity.jl | 18 ++++++------- .../src/literate-tutorials/heat_adaptivity.jl | 27 +++++++++---------- src/Export/VTK.jl | 6 ++--- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index ea70872259..a01f54f02b 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -75,8 +75,8 @@ function solve(grid) ch = ConstraintHandler(dh) add!(ch, Ferrite.ConformityConstraint(:u)) - add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) - add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.01, 2)) + add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) + add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 0.01, 2)) close!(ch); K = create_sparsity_pattern(dh,ch) @@ -153,19 +153,19 @@ function solve_adaptive(initial_grid) end push!(error_arr,error) end - vtk_grid("linear_elasticity-$i", dh) do vtk - vtk_point_data(vtk, dh, u) - vtk_point_data(vtk, projector, σ_dof, "stress") - vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") - vtk_cell_data(vtk, error_arr, "error") + VTKFile("linear_elasticity-$i", dh) do vtk + write_solution(vtk, dh, u) + write_projection(vtk, projector, σ_dof, "stress") + write_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") + write_cell_data(vtk, error_arr, "error") end Ferrite.refine!(grid,cells_to_refine) - vtk_grid("unbalanced.vtu", dh) do vtk + VTKFile("unbalanced.vtu", dh) do vtk end Ferrite.balanceforest!(grid) - vtk_grid("balanced.vtu", dh) do vtk + VTKFile("balanced.vtu", dh) do vtk end i += 1 diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index 4163be3b3a..6abf9a77ba 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -66,10 +66,10 @@ function solve(grid) ch = ConstraintHandler(dh) add!(ch, ConformityConstraint(:u)) - add!(ch, Dirichlet(:u, getfaceset(grid, "top"), (x, t) -> 0.0)) - add!(ch, Dirichlet(:u, getfaceset(grid, "right"), (x, t) -> 0.0)) - add!(ch, Dirichlet(:u, getfaceset(grid, "left"), (x, t) -> 0.0)) - add!(ch, Dirichlet(:u, getfaceset(grid, "bottom"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0)) + add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 0.0)) close!(ch); K = create_sparsity_pattern(dh,ch) @@ -124,12 +124,10 @@ function solve_adaptive(initial_grid) finished = false i = 1 grid = deepcopy(initial_grid) - pvd = paraview_collection("heat_amr.pvd"); + pvd = VTKFileCollection("heat_amr.pvd",grid); while !finished && i<=10 @show i transfered_grid = Ferrite.creategrid(grid) - vtk_grid("heat_amr-grid_$i", transfered_grid) do vtk - end u,dh,ch,cv = solve(transfered_grid) σ_gp, σ_gp_sc = compute_fluxes(u,dh) projector = L2Projector(Lagrange{RefQuadrilateral, 1}(), transfered_grid) @@ -151,13 +149,12 @@ function solve_adaptive(initial_grid) push!(error_arr,error) end - vtk_grid("heat_amr-iteration_$i", dh) do vtk - vtk_point_data(vtk, dh, u) - vtk_point_data(vtk, projector, σ_dof, "flux") - vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "flux sc x") - vtk_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),2), "flux sc y") - vtk_cell_data(vtk, error_arr, "error") - pvd[i] = vtk + addstep!(pvd, i, dh) do vtk + write_solution(vtk, dh, u) + write_projection(vtk, projector, σ_dof, "flux") + write_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "flux sc x") + write_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),2), "flux sc y") + write_cell_data(vtk, error_arr, "error") end Ferrite.refine!(grid, cells_to_refine) @@ -168,7 +165,7 @@ function solve_adaptive(initial_grid) finished = true end end - vtk_save(pvd); + close(pvd); end solve_adaptive(grid) diff --git a/src/Export/VTK.jl b/src/Export/VTK.jl index bdca14c191..fa39576f4a 100644 --- a/src/Export/VTK.jl +++ b/src/Export/VTK.jl @@ -179,17 +179,17 @@ nodes_to_vtkorder(cell::QuadraticHexahedron) = [ cell.nodes[27], # interior ] -function create_vtk_griddata(grid::Grid{dim,C,T}) where {dim,C,T} +function create_vtk_griddata(grid::AbstractGrid{dim}) where dim cls = WriteVTK.MeshCell[] for cell in getcells(grid) celltype = Ferrite.cell_to_vtkcell(typeof(cell)) push!(cls, WriteVTK.MeshCell(celltype, nodes_to_vtkorder(cell))) end - coords = reshape(reinterpret(T, getnodes(grid)), (dim, getnnodes(grid))) + coords = reshape(reinterpret(get_coordinate_eltype(grid), getnodes(grid)), (dim, getnnodes(grid))) return coords, cls end -function create_vtk_grid(filename::AbstractString, grid::Grid{dim,C,T}; kwargs...) where {dim,C,T} +function create_vtk_grid(filename::AbstractString, grid::AbstractGrid{dim}; kwargs...) where dim coords, cls = create_vtk_griddata(grid) return WriteVTK.vtk_grid(filename, coords, cls; kwargs...) end From d7a399e91442b92296b9d2df05900ace040493f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 10:16:06 +0200 Subject: [PATCH 124/143] update ns_vs_diffeq example? --- docs/src/literate-tutorials/ns_vs_diffeq.jl | 44 ++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index 352af3ffcc..f397d03812 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -135,22 +135,20 @@ grid = generate_grid(Quadrilateral, (x_cells, y_cells), Vec{2}((0.0, 0.0)), Vec{ # Next we carve a hole $B_{0.05}(0.2,0.2)$ in the mesh by deleting the cells and update the boundary face sets. # This code will be replaced once a proper mesh interface is available. -hole_center = Vec((0.2, 0.2)) -hole_radius = 0.05 -cell_indices = filter(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) > hole_radius, 1:length(grid.cells)) -hole_cell_indices = filter(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) <= hole_radius, 1:length(grid.cells)); -hole_facet_ring = Set{FacetIndex}() +cell_indices = filter(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))>0.05, 1:length(grid.cells)) +hole_cell_indices = filter(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))<=0.05, 1:length(grid.cells)); +hole_face_ring = Set{FaceIndex}() for hci ∈ hole_cell_indices - push!(hole_facet_ring, FacetIndex((hci+1, 4))) - push!(hole_facet_ring, FacetIndex((hci-1, 2))) - push!(hole_facet_ring, FacetIndex((hci-x_cells, 3))) - push!(hole_facet_ring, FacetIndex((hci+x_cells, 1))) + push!(hole_face_ring, FaceIndex((hci+1, 4))) + push!(hole_face_ring, FaceIndex((hci-1, 2))) + push!(hole_face_ring, FaceIndex((hci-x_cells, 3))) + push!(hole_face_ring, FaceIndex((hci+x_cells, 1))) end -addfacetset!(grid, "hole", Set(filter(x->x.idx[1] ∉ hole_cell_indices, collect(hole_facet_ring)))) -cell_indices_map = map(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) > 0.05 ? indexin([ci], cell_indices)[1] : 0, 1:length(grid.cells)) +grid.facesets["hole"] = Set(filter(x->x.idx[1] ∉ hole_cell_indices, collect(hole_face_ring))); +cell_indices_map = map(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))>0.05 ? indexin([ci], cell_indices)[1] : 0, 1:length(grid.cells)) grid.cells = grid.cells[cell_indices] -for facetsetname in keys(grid.facetsets) - grid.facetsets[facetsetname] = Set(map(fi -> FacetIndex( cell_indices_map[fi.idx[1]] ,fi.idx[2]), collect(grid.facetsets[facetsetname]))) +for facesetname in keys(grid.facesets) + grid.facesets[facesetname] = Set(map(fi -> FaceIndex( cell_indices_map[fi.idx[1]] ,fi.idx[2]), collect(grid.facesets[facesetname]))) end; # We test against full development of the flow - so regenerate the grid #src @@ -178,10 +176,10 @@ close!(dh); # fluid on this portion of the boundary is fixed to be zero. ch = ConstraintHandler(dh); -nosplip_facet_names = ["top", "bottom", "hole"]; +nosplip_face_names = ["top", "bottom", "hole"]; # No hole for the test present #src -nosplip_facet_names = ["top", "bottom"] #hide -∂Ω_noslip = union(getfacetset.((grid, ), nosplip_facet_names)...); +nosplip_face_names = ["top", "bottom"] #hide +∂Ω_noslip = union(getfaceset.((grid, ), nosplip_face_names)...); noslip_bc = Dirichlet(:v, ∂Ω_noslip, (x, t) -> [0,0], [1,2]) add!(ch, noslip_bc); @@ -190,7 +188,7 @@ add!(ch, noslip_bc); # is already enough to obtain some simple vortex streets. By increasing the # velocity further we can obtain stronger vortices - which may need additional # refinement of the grid. -∂Ω_inflow = getfacetset(grid, "left"); +∂Ω_inflow = getfaceset(grid, "left"); vᵢₙ(t) = clamp(t, 0.0, 1.0)*1.0 #inflow velocity vᵢₙ(t) = clamp(t, 0.0, 1.0)*0.3 #hide @@ -202,7 +200,7 @@ add!(ch, inflow_bc); # cylinder when the weak form has been derived by setting the boundary integral # to zero. It is also called the do-nothing condition. Other outflow conditions # are also possible. -∂Ω_free = getfacetset(grid, "right"); +∂Ω_free = getfaceset(grid, "right"); close!(ch) update!(ch, 0.0); @@ -433,7 +431,7 @@ integrator = init( progress=true, progress_steps=1, saveat=Δt_save); -pvd = VTKFileCollection("vortex-street", grid); +pvd = paraview_collection("vortex-street.pvd"); integrator = TimeChoiceIterator(integrator, 0.0:Δt_save:T) for (u_uc,t) in integrator # We ignored the Dirichlet constraints in the solution vector up to now, @@ -442,11 +440,13 @@ for (u_uc,t) in integrator update!(ch, t) u = copy(u_uc) apply!(u, ch) - addstep!(pvd, t) do io - write_solution(io, dh, u) + vtk_grid("vortex-street-$t.vtu", dh) do vtk + vtk_point_data(vtk,dh,u) + vtk_save(vtk) + pvd[t] = vtk end end -close(pvd); +vtk_save(pvd); # Test the result for full proper development of the flow #src using Test #hide From 24d4f646577514ec8aa594629f3b935c7c64fe6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 10:17:13 +0200 Subject: [PATCH 125/143] whoopsie --- docs/src/literate-tutorials/ns_vs_diffeq.jl | 44 ++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/src/literate-tutorials/ns_vs_diffeq.jl b/docs/src/literate-tutorials/ns_vs_diffeq.jl index f397d03812..352af3ffcc 100644 --- a/docs/src/literate-tutorials/ns_vs_diffeq.jl +++ b/docs/src/literate-tutorials/ns_vs_diffeq.jl @@ -135,20 +135,22 @@ grid = generate_grid(Quadrilateral, (x_cells, y_cells), Vec{2}((0.0, 0.0)), Vec{ # Next we carve a hole $B_{0.05}(0.2,0.2)$ in the mesh by deleting the cells and update the boundary face sets. # This code will be replaced once a proper mesh interface is available. -cell_indices = filter(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))>0.05, 1:length(grid.cells)) -hole_cell_indices = filter(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))<=0.05, 1:length(grid.cells)); -hole_face_ring = Set{FaceIndex}() +hole_center = Vec((0.2, 0.2)) +hole_radius = 0.05 +cell_indices = filter(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) > hole_radius, 1:length(grid.cells)) +hole_cell_indices = filter(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) <= hole_radius, 1:length(grid.cells)); +hole_facet_ring = Set{FacetIndex}() for hci ∈ hole_cell_indices - push!(hole_face_ring, FaceIndex((hci+1, 4))) - push!(hole_face_ring, FaceIndex((hci-1, 2))) - push!(hole_face_ring, FaceIndex((hci-x_cells, 3))) - push!(hole_face_ring, FaceIndex((hci+x_cells, 1))) + push!(hole_facet_ring, FacetIndex((hci+1, 4))) + push!(hole_facet_ring, FacetIndex((hci-1, 2))) + push!(hole_facet_ring, FacetIndex((hci-x_cells, 3))) + push!(hole_facet_ring, FacetIndex((hci+x_cells, 1))) end -grid.facesets["hole"] = Set(filter(x->x.idx[1] ∉ hole_cell_indices, collect(hole_face_ring))); -cell_indices_map = map(ci->norm(mean(map(i->grid.nodes[i].x-[0.2,0.2], Ferrite.vertices(grid.cells[ci]))))>0.05 ? indexin([ci], cell_indices)[1] : 0, 1:length(grid.cells)) +addfacetset!(grid, "hole", Set(filter(x->x.idx[1] ∉ hole_cell_indices, collect(hole_facet_ring)))) +cell_indices_map = map(ci -> norm(mean(getcoordinates(grid, ci) .- (hole_center,))) > 0.05 ? indexin([ci], cell_indices)[1] : 0, 1:length(grid.cells)) grid.cells = grid.cells[cell_indices] -for facesetname in keys(grid.facesets) - grid.facesets[facesetname] = Set(map(fi -> FaceIndex( cell_indices_map[fi.idx[1]] ,fi.idx[2]), collect(grid.facesets[facesetname]))) +for facetsetname in keys(grid.facetsets) + grid.facetsets[facetsetname] = Set(map(fi -> FacetIndex( cell_indices_map[fi.idx[1]] ,fi.idx[2]), collect(grid.facetsets[facetsetname]))) end; # We test against full development of the flow - so regenerate the grid #src @@ -176,10 +178,10 @@ close!(dh); # fluid on this portion of the boundary is fixed to be zero. ch = ConstraintHandler(dh); -nosplip_face_names = ["top", "bottom", "hole"]; +nosplip_facet_names = ["top", "bottom", "hole"]; # No hole for the test present #src -nosplip_face_names = ["top", "bottom"] #hide -∂Ω_noslip = union(getfaceset.((grid, ), nosplip_face_names)...); +nosplip_facet_names = ["top", "bottom"] #hide +∂Ω_noslip = union(getfacetset.((grid, ), nosplip_facet_names)...); noslip_bc = Dirichlet(:v, ∂Ω_noslip, (x, t) -> [0,0], [1,2]) add!(ch, noslip_bc); @@ -188,7 +190,7 @@ add!(ch, noslip_bc); # is already enough to obtain some simple vortex streets. By increasing the # velocity further we can obtain stronger vortices - which may need additional # refinement of the grid. -∂Ω_inflow = getfaceset(grid, "left"); +∂Ω_inflow = getfacetset(grid, "left"); vᵢₙ(t) = clamp(t, 0.0, 1.0)*1.0 #inflow velocity vᵢₙ(t) = clamp(t, 0.0, 1.0)*0.3 #hide @@ -200,7 +202,7 @@ add!(ch, inflow_bc); # cylinder when the weak form has been derived by setting the boundary integral # to zero. It is also called the do-nothing condition. Other outflow conditions # are also possible. -∂Ω_free = getfaceset(grid, "right"); +∂Ω_free = getfacetset(grid, "right"); close!(ch) update!(ch, 0.0); @@ -431,7 +433,7 @@ integrator = init( progress=true, progress_steps=1, saveat=Δt_save); -pvd = paraview_collection("vortex-street.pvd"); +pvd = VTKFileCollection("vortex-street", grid); integrator = TimeChoiceIterator(integrator, 0.0:Δt_save:T) for (u_uc,t) in integrator # We ignored the Dirichlet constraints in the solution vector up to now, @@ -440,13 +442,11 @@ for (u_uc,t) in integrator update!(ch, t) u = copy(u_uc) apply!(u, ch) - vtk_grid("vortex-street-$t.vtu", dh) do vtk - vtk_point_data(vtk,dh,u) - vtk_save(vtk) - pvd[t] = vtk + addstep!(pvd, t) do io + write_solution(io, dh, u) end end -vtk_save(pvd); +close(pvd); # Test the result for full proper development of the flow #src using Test #hide From 92ac19f0b48b8f6f85e078003e597f70ef8349ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 12:28:13 +0200 Subject: [PATCH 126/143] Update src/Dofs/DofHandler.jl Co-authored-by: Knut Andreas Meyer --- src/Dofs/DofHandler.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index 50006b424c..d818c01551 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -368,7 +368,7 @@ function __close!(dh::DofHandler{dim}) where {dim} for fi in 1:numfields dh.vertexdicts[fi] = zeros(Int, nnodes) dh.edgedicts[fi] = Dict{Tuple{Int,Int}, Int}() - dh.facedicts[fi] = Dict{NTuple{dim,Int}, Int}() + dh.facedicts[fi] = Dict{NTuple{3,Int}, Int}() end # Set initial values From fb3df4ca94bd9ac8aaf63ef66d72e3b5911b0e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 13:28:19 +0200 Subject: [PATCH 127/143] make the pre-commit linter happy --- docs/src/literate-tutorials/adaptivity.jl | 6 +-- .../src/literate-tutorials/heat_adaptivity.jl | 6 +-- docs/src/literate-tutorials/l.msh | 42 +++++++++---------- src/Adaptivity/AMR.jl | 2 +- src/Adaptivity/BWG.jl | 22 +++++----- src/Adaptivity/constraints.jl | 1 - src/Adaptivity/ncgrid.jl | 2 +- src/Adaptivity/visualization.jl | 4 +- src/Grid/grid_generators.jl | 6 +-- test/test_p4est.jl | 18 ++++---- 10 files changed, 54 insertions(+), 55 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index a01f54f02b..103d62a12b 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -64,9 +64,9 @@ end function solve(grid) dim = 2 - order = 1 - ip = Lagrange{RefQuadrilateral, order}()^dim - qr = QuadratureRule{RefQuadrilateral}(2) + order = 1 + ip = Lagrange{RefQuadrilateral, order}()^dim + qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); dh = DofHandler(grid) diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index 6abf9a77ba..83c5ef3b86 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -24,7 +24,7 @@ function assemble_cell!(ke, fe, cellvalues, ue, coords) for i in 1:n_basefuncs Nᵢ = shape_value(cellvalues, q_point, i) ∇Nᵢ = shape_gradient(cellvalues, q_point, i) - fe[i] += analytical_rhs(x) * Nᵢ * dΩ + fe[i] += analytical_rhs(x) * Nᵢ * dΩ for j in 1:n_basefuncs ∇Nⱼ = shape_gradient(cellvalues, q_point, j) ke[i, j] += ∇Nⱼ ⋅ ∇Nᵢ * dΩ @@ -42,7 +42,7 @@ function assemble_global!(K, f, a, dh, cellvalues) assembler = start_assemble(K, f) ## Loop over all cells for cell in CellIterator(dh) - reinit!(cellvalues, cell) + reinit!(cellvalues, cell) @views ue = a[celldofs(cell)] ## Compute element contribution coords = getcoordinates(cell) @@ -55,7 +55,7 @@ end function solve(grid) dim = 2 - order = 1 + order = 1 ip = Lagrange{RefQuadrilateral, order}() qr = QuadratureRule{RefQuadrilateral}(2) cellvalues = CellValues(qr, ip); diff --git a/docs/src/literate-tutorials/l.msh b/docs/src/literate-tutorials/l.msh index a6f57b3494..f37a7b98ea 100644 --- a/docs/src/literate-tutorials/l.msh +++ b/docs/src/literate-tutorials/l.msh @@ -11,21 +11,21 @@ $PhysicalNames $EndPhysicalNames $Entities 6 7 2 0 -1 0 0 0 0 -2 1 0 0 0 -3 1 0.5 0 0 -4 0.5 0.5 0 0 -5 0.5 1 0 0 -6 0 1 0 0 -1 0 0 0 1 0 0 1 10 2 1 -2 -2 1 0 0 1 0.5 0 1 11 2 2 -3 -3 0.5 0.5 0 1 0.5 0 0 2 3 -4 -4 0.5 0.5 0 0.5 1 0 0 2 4 -5 -5 0 1 0 0.5 1 0 1 8 2 5 -6 -6 0 0 0 0 1 0 1 9 2 6 -1 -7 0 0 0 0.5 0.5 0 0 2 1 -4 -1 0 0 0 1 0.5 0 1 12 4 3 -7 1 2 -2 0 0 0 0.5 1 0 1 12 4 4 5 6 7 +1 0 0 0 0 +2 1 0 0 0 +3 1 0.5 0 0 +4 0.5 0.5 0 0 +5 0.5 1 0 0 +6 0 1 0 0 +1 0 0 0 1 0 0 1 10 2 1 -2 +2 1 0 0 1 0.5 0 1 11 2 2 -3 +3 0.5 0.5 0 1 0.5 0 0 2 3 -4 +4 0.5 0.5 0 0.5 1 0 0 2 4 -5 +5 0 1 0 0.5 1 0 1 8 2 5 -6 +6 0 0 0 0 1 0 1 9 2 6 -1 +7 0 0 0 0.5 0.5 0 0 2 1 -4 +1 0 0 0 1 0.5 0 1 12 4 3 -7 1 2 +2 0 0 0 0.5 1 0 1 12 4 4 5 6 7 $EndEntities $Nodes 12 6 1 6 @@ -57,15 +57,15 @@ $EndNodes $Elements 6 6 1 6 1 1 1 1 -1 1 2 +1 1 2 1 2 1 1 -2 2 3 +2 2 3 1 5 1 1 -3 5 6 +3 5 6 1 6 1 1 -4 6 1 +4 6 1 2 1 3 1 -5 3 4 1 2 +5 3 4 1 2 2 2 3 1 -6 4 5 6 1 +6 4 5 6 1 $EndElements diff --git a/src/Adaptivity/AMR.jl b/src/Adaptivity/AMR.jl index 5e599581d4..b5ce097ae0 100644 --- a/src/Adaptivity/AMR.jl +++ b/src/Adaptivity/AMR.jl @@ -8,7 +8,7 @@ include("BWG.jl") include("ncgrid.jl") include("constraints.jl") -export ForestBWG, +export ForestBWG, refine!, refine_all!, coarsen!, diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 1eda3101a8..363ab88f22 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -559,7 +559,7 @@ function transform_pointBWG(forest::ForestBWG{dim}, k::Integer, vertex::NTuple{d tree = forest.cells[k] cellnodes = getnodes(forest,collect(tree.nodes)) .|> get_node_coordinate vertex = vertex .* (2/(2^tree.b)) .- 1 - octant_physical_coordinates = sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(vertex),j),1:length(cellnodes)) + octant_physical_coordinates = sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(vertex),j),1:length(cellnodes)) return Vec{dim}(octant_physical_coordinates) end @@ -580,7 +580,7 @@ computes based on the rotation indicator `r` ∈ {0,...,3} and a given corner in See Table 3 and Theorem 2.2 [BWG2011](@citet). """ function rotation_permutation(f,f′,r,i) - return 𝒫[𝒬[ℛ[f,f′],r+1],i] + return 𝒫[𝒬[ℛ[f,f′],r+1],i] end #TODO: this function should wrap the LNodes Iterator of [IBWG2015](@citet) @@ -676,7 +676,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} end else for i ∈ 1:ncorners_face3D - rotated_ξ = rotation_permutation(f′,f,r,i) + rotated_ξ = rotation_permutation(f′,f,r,i) if haskey(nodeids, (k′,fnodes_neighbor[i])) nodeids[(k,fnodes[rotated_ξ])] = nodeids[(k′,fnodes_neighbor[i])] nodeowners[(k,fnodes[rotated_ξ])] = (k′,fnodes_neighbor[i]) @@ -1221,7 +1221,7 @@ end function face_contains_edge(f::NTuple{4,Tuple{T1,T1,T1}},e::Tuple{Tuple{T2,T2,T2},Tuple{T2,T2,T2}}) where {T1<:Integer,T2<:Integer} edge_center = center(e) lower_left = ntuple(i->minimum(getindex.(f,i)),3) - top_right = ntuple(i->maximum(getindex.(f,i)),3) + top_right = ntuple(i->maximum(getindex.(f,i)),3) if (lower_left[1] ≤ edge_center[1] ≤ top_right[1]) && (lower_left[2] ≤ edge_center[2] ≤ top_right[2]) && (lower_left[3] ≤ edge_center[3] ≤ top_right[3]) return true else @@ -1510,13 +1510,13 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2 # Coordinate axes of f a = ( f ≤ 2, # tangent - f > 2 # normal + f > 2 # normal ) a_sign = _two*((f - _one) & 1) - _one # Coordinate axes of f' b = ( f′ ≤ 2, # tangent - f′ > 2 # normal + f′ > 2 # normal ) # b_sign = _two*(f′ & 1) - _one @@ -1648,7 +1648,7 @@ transform_corner_remote(forest::ForestBWG,v::VertexIndex,oct::OctantBWG,inside) Algorithm 10 in [BWG2011](@citet) to transform edge into different octree coordinate system. This function looks at the octant from the octree coordinate system of the neighbor that can be found at (k,e) """ -function transform_edge_remote(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N,T2},inside::Bool) where {N,T1<:Integer,T2<:Integer} +function transform_edge_remote(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N,T2},inside::Bool) where {N,T1<:Integer,T2<:Integer} _four = T2(4) _one = T2(1) _two = T2(2) @@ -1667,7 +1667,7 @@ function transform_edge_remote(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N, a₀ += _one #add it again b = forest.cells[k].b l = oct.l; g = _two^b - _two^(b-l) - h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b + h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b s = compute_edge_orientation(forest,k,e) xyz = zeros(T2,3) xyz[𝐛[1]+_one] = s*g+(_one-(_two*s))*oct.xyz[a₀] @@ -1706,7 +1706,7 @@ function transform_edge(forest::ForestBWG,k::T1,e::T1,oct::OctantBWG{3,N,T2},ins a₀ += _one #add it again b = forest.cells[k].b l = oct.l; g = _two^b - _two^(b-l) - h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b + h⁻ = inside ? z : -_two^(b-l); h⁺ = inside ? g : _two^b s = compute_edge_orientation(forest,k′,e′) xyz = zeros(T2,3) xyz[𝐛[1]+_one] = s*g+(_one-(_two*s))*oct.xyz[a₀] @@ -1731,7 +1731,7 @@ function edge_neighbor(octant::OctantBWG{3,N,T}, e::T, b::T=_maxlevel[2]) where ox,oy,oz = octant.xyz 𝐚 = (e ÷ 4, e < 4 ? 1 : 0, - e < 8 ? 2 : 1) + e < 8 ? 2 : 1) xyz = zeros(T,3) xyz[𝐚[1]+_one] = octant.xyz[𝐚[1]+_one] xyz[𝐚[2]+_one] = octant.xyz[𝐚[2]+_one] + (_two*(e&_one)-_one)*h @@ -1807,7 +1807,7 @@ _edge_corners(edge::Int,i::Int) = 𝒰[edge,i] # finds face corner ξ′ in f′ for two associated faces f,f′ in {1,...,6} and their orientation r in {1,...,4}} _neighbor_corner(f::Int,f′::Int,r::Int,ξ::Int) = 𝒫[𝒬[ℛ[f,f′],r],ξ] -# map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup +# map given `face` and `ξ` to corner `c`. Need to provide dim for different lookup function _face_corners(dim::Int,face::Int,ξ::Int) if dim == 2 return 𝒱₂[face,ξ] diff --git a/src/Adaptivity/constraints.jl b/src/Adaptivity/constraints.jl index 5317c0cbb9..c86ed68e18 100644 --- a/src/Adaptivity/constraints.jl +++ b/src/Adaptivity/constraints.jl @@ -40,4 +40,3 @@ function _add_conformity_constraint(ch::ConstraintHandler, field_index::Int, int end end end - diff --git a/src/Adaptivity/ncgrid.jl b/src/Adaptivity/ncgrid.jl index 14304757e3..fc53633bdb 100644 --- a/src/Adaptivity/ncgrid.jl +++ b/src/Adaptivity/ncgrid.jl @@ -1,7 +1,7 @@ """ NonConformingGrid{dim, C<:AbstractCell, T<:Real, CIT} <: AbstractGrid} -A `NonConformingGrid` is a collection of `Cells` and `Node`s which covers the computational domain, together with Sets of cells, nodes, faces +A `NonConformingGrid` is a collection of `Cells` and `Node`s which covers the computational domain, together with Sets of cells, nodes, faces and assocaited information about the conformity. There are multiple helper structures to apply boundary conditions or define subdomains. They are gathered in the `cellsets`, `nodesets`, `facesets`, `edgesets` and `vertexsets`. diff --git a/src/Adaptivity/visualization.jl b/src/Adaptivity/visualization.jl index fa5fe4eb85..e1b4a53cfe 100644 --- a/src/Adaptivity/visualization.jl +++ b/src/Adaptivity/visualization.jl @@ -7,10 +7,10 @@ function visualize_grid(forest::ForestBWG{dim}) where dim #request vertices and faces in octree coordinate system _vertices = Ferrite.vertices(leaf,tree.b) # transform from octree coordinate system to -1,1 by first shifting to 0,2 and later shift by -1 - _vertices = broadcast.(x->x .* 2/(2^tree.b) .- 1, _vertices) + _vertices = broadcast.(x->x .* 2/(2^tree.b) .- 1, _vertices) octant_physical_coordinates = zeros(length(_vertices),dim) for (i,v) in enumerate(_vertices) - octant_physical_coordinates[i,:] .= sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(v),j),1:length(cellnodes)) + octant_physical_coordinates[i,:] .= sum(j-> cellnodes[j] * Ferrite.shape_value(Lagrange{Ferrite.RefHypercube{dim},1}(),Vec{dim}(v),j),1:length(cellnodes)) end GLMakie.scatter!(ax,octant_physical_coordinates,color=:black,markersize=25) center = sum(octant_physical_coordinates,dims=1) ./ 4 diff --git a/src/Grid/grid_generators.jl b/src/Grid/grid_generators.jl index 86c57449ff..e39cd1bc37 100644 --- a/src/Grid/grid_generators.jl +++ b/src/Grid/grid_generators.jl @@ -554,7 +554,7 @@ function generate_simple_disc_grid(::Type{Quadrilateral}, n; radius = 1.0) facesets = Dict( "boundary" => Set([FaceIndex(i,1) for i ∈ 1:n]) ∪ Set([FaceIndex(i,2) for i ∈ 1:n]), ) - + return Grid(elements, Node.(nodes); facesets=facesets) end @@ -565,7 +565,7 @@ function generate_simple_disc_grid(::Type{Hexahedron}, n; radius = 1.0, layers = nodepos_bottom = Vec((0.0,radius,0.0)) nodes = [rotate(nodepos_bottom, Vec{3}((0,0,1)), θ*i) for i ∈ 0:(2n-1)] push!(nodes, Vec((0.0,0.0,0.0))) - + # TODO generalize for n layers by looping over layers nodepos_layer = Vec((0.0,radius,height)) nodes_layer = [rotate(nodepos_layer, Vec{3}((0,0,1)), θ*i) for i ∈ 0:(2n-1)] @@ -579,6 +579,6 @@ function generate_simple_disc_grid(::Type{Hexahedron}, n; radius = 1.0, layers = "top" => Set([FaceIndex(i,5) for i ∈ 1:n]), "bottom" => Set([FaceIndex(i,6) for i ∈ 1:n]), ) - + return Grid(elements, Node.(nodes))#; facesets=facesets) end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index e905b6ad6a..87d93bdf4c 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -375,7 +375,7 @@ end grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) o = adaptive_grid.cells[1].leaves[1] - + # faces @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) @@ -447,7 +447,7 @@ end @test Ferrite.AMR.transform_edge(adaptive_grid, EdgeIndex(1,12), o, false) == Ferrite.AMR.OctantBWG(0,(8,8,0)) @test Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,12), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,0)) - # Rotate three dimensional case + # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) # This is our root mesh top view # x-----------x-----------x @@ -631,7 +631,7 @@ end Ferrite.AMR.balanceforest!(adaptive_grid) transfered_grid_ref = Ferrite.AMR.creategrid(adaptive_grid) - # Rotate three dimensional case + # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) # This is our root mesh top view # x-----------x-----------x @@ -665,7 +665,7 @@ end transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == length(transfered_grid_ref.cells) @test length(transfered_grid.cells) == 92 - + # edge balancing for new introduced connection that is not within topology table grid = generate_grid(Hexahedron, (2,1,1)); adaptive_grid = ForestBWG(grid,3) @@ -743,7 +743,7 @@ end ################################################# ############ structured 2D examples ############# ################################################# - + # 2D case with a single tree grid = generate_grid(Quadrilateral,(1,1)) adaptive_grid = ForestBWG(grid,3) @@ -780,7 +780,7 @@ end Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[5]) Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) - Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) + Ferrite.AMR.refine!(adaptive_grid.cells[9],adaptive_grid.cells[9].leaves[end]) transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.cells) == 45 @test length(transfered_grid.nodes) == 76 @@ -809,7 +809,7 @@ end @test length(transfered_grid.cells) == 16 @test length(transfered_grid.nodes) == 45 @test unique(transfered_grid.nodes) == transfered_grid.nodes - #rotate the case around + #rotate the case around grid = generate_grid(Hexahedron,(1,2,1)) adaptive_grid = ForestBWG(grid,3) Ferrite.AMR.refine_all!(adaptive_grid,1) @@ -833,7 +833,7 @@ end @test length(transfered_grid.nodes) == 125 # 5 per edge @test unique(transfered_grid.nodes) == transfered_grid.nodes - # Rotate three dimensional case + # Rotate three dimensional case grid = generate_grid(Hexahedron,(2,2,2)) # Rotate face topologically grid.cells[2] = Hexahedron((grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[4+2], grid.cells[2].nodes[4+3], grid.cells[2].nodes[4+4], grid.cells[2].nodes[4+1])) @@ -890,7 +890,7 @@ end # x-----x-----x-----------x transfered_grid = Ferrite.AMR.creategrid(adaptive_grid) @test length(transfered_grid.conformity_info) == 12 - + # Easy Interoctree grid = generate_grid(Hexahedron,(2,2,2)) adaptive_grid = ForestBWG(grid,3) From d788f967f171b211e65470170c91f9adf0ba6875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 13:47:01 +0200 Subject: [PATCH 128/143] fix DofHandler definition --- src/Dofs/DofHandler.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index d818c01551..849b189e94 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -116,7 +116,7 @@ struct DofHandler{dim,G<:AbstractGrid{dim}} <: AbstractDofHandler # add to the face since currently more dofs per face isn't supported. In # 2D a face (i.e. a line) is uniquely determined by 2 vertices, and in 3D a face (i.e. a # surface) is uniquely determined by 3 vertices. - facedicts::Vector{Dict{NTuple{dim,Int}, Int}} + facedicts::Vector{Dict{NTuple{3,Int}, Int}} end """ From d54ae18ba0e220d50cfa04d956ab4493889ae8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 13:59:07 +0200 Subject: [PATCH 129/143] add amr to topics overview --- docs/src/topics/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/src/topics/index.md b/docs/src/topics/index.md index 6e8a1d7642..e5ae4c1ded 100644 --- a/docs/src/topics/index.md +++ b/docs/src/topics/index.md @@ -11,6 +11,7 @@ Pages = [ "boundary_conditions.md", "constraints.md", "grid.md", - "export.md" + "export.md", + "amr.md" ] ``` From d9c4688dd041bfd9bfa5697f3c3993b5d6680ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 22 May 2024 14:04:03 +0200 Subject: [PATCH 130/143] change tuple in dofhandler construction --- src/Dofs/DofHandler.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Dofs/DofHandler.jl b/src/Dofs/DofHandler.jl index 849b189e94..8b6fa24314 100644 --- a/src/Dofs/DofHandler.jl +++ b/src/Dofs/DofHandler.jl @@ -144,7 +144,7 @@ close!(dh) function DofHandler(grid::G) where {dim, G <: AbstractGrid{dim}} ncells = getncells(grid) sdhs = SubDofHandler{DofHandler{dim, G}}[] - DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), ScalarWrapper(false), grid, ScalarWrapper(-1), [Int[]], Dict{Tuple{Int,Int}}[], Dict{NTuple{dim,Int}}[]) + DofHandler{dim, G}(sdhs, Symbol[], Int[], zeros(Int, ncells), zeros(Int, ncells), ScalarWrapper(false), grid, ScalarWrapper(-1), [Int[]], Dict{Tuple{Int,Int}}[], Dict{NTuple{3,Int}}[]) end function Base.show(io::IO, mime::MIME"text/plain", dh::DofHandler) From be5ac8a470932782f9b79b498b73cc2fd40774a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 23 May 2024 14:33:44 +0200 Subject: [PATCH 131/143] change to get_facet_facet_neighborhood --- src/Adaptivity/BWG.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 363ab88f22..62003ae8ee 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -597,7 +597,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} node_map_inv = dim < 3 ? node_map₂_inv : node_map₃_inv nodeids = Dict{Tuple{Int,NTuple{dim,Int32}},Int}() nodeowners = Dict{Tuple{Int,NTuple{dim,Int32}},Tuple{Int,NTuple{dim,Int32}}}() - facet_neighborhood = Ferrite.get_facet_facet_neighborhood(forest) + facet_neighborhood = Ferrite.Ferrite.get_facet_facet_neighborhood(forest) # Phase 1: Assign node owners intra-octree pivot_nodeid = 1 @@ -807,7 +807,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim facetable = dim == 2 ? 𝒱₂ : 𝒱₃ opposite_face = dim == 2 ? opposite_face_2 : opposite_face_3 hnodes = Dict{Int,Vector{Int}}() - facet_neighborhood = Ferrite.get_facet_facet_neighborhood(forest) + facet_neighborhood = Ferrite.Ferrite.get_facet_facet_neighborhood(forest) for (k,tree) in enumerate(forest.cells) rootfaces = faces(root(dim),tree.b) for (l,leaf) in enumerate(tree.leaves) @@ -969,7 +969,7 @@ function balanceforest!(forest::ForestBWG{dim}) where dim perm_corner_inv = dim == 2 ? node_map₂_inv : node_map₃_inv root_ = root(dim) nrefcells = 0 - facet_neighborhood = Ferrite.get_facet_facet_neighborhood(forest) + facet_neighborhood = Ferrite.Ferrite.get_facet_facet_neighborhood(forest) while nrefcells - getncells(forest) != 0 for k in 1:length(forest.cells) tree = forest.cells[k] @@ -1377,7 +1377,8 @@ function compute_face_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{dim,<:Any, n_perminv = (dim == 2 ? node_map₂_inv : node_map₃_inv) f_ferrite = f_perm[f] - k′, f′_ferrite = dim == 2 ? forest.topology.edge_edge_neighbor[k,f_ferrite][1] : forest.topology.face_face_neighbor[k,f_ferrite][1] + facet_neighbor_table = Ferrite.get_facet_facet_neighborhood(forest) + k′, f′_ferrite = facet_neighbor_table[k,f_ferrite][1] f′ = f_perminv[f′_ferrite] reffacenodes = reference_faces_bwg(Ferrite.RefHypercube{dim}) nodes_f = [forest.cells[k].nodes[n_perm[ni]] for ni in reffacenodes[f]] @@ -1440,7 +1441,8 @@ function transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim _two = T2(2) _perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) _perminv = (dim == 2 ? 𝒱₂_perm_inv : 𝒱₃_perm_inv) - k′, f′ = dim == 2 ? forest.topology.edge_edge_neighbor[k,_perm[f]][1] : forest.topology.face_face_neighbor[k,_perm[f]][1] + facet_neighbor_table = Ferrite.get_facet_facet_neighborhood(forest) + k′, f′ = facet_neighbor_table[k,_perm[f]][1] f′ = _perminv[f′] s′ = _one - (((f - _one) & _one) ⊻ ((f′ - _one) & _one)) s = zeros(T2,dim-1) From a0696b1d111e8e2315e7ee140f6ce3b67ecb4c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 27 May 2024 09:48:11 +0200 Subject: [PATCH 132/143] rename face to facet --- src/Adaptivity/BWG.jl | 28 ++++----- test/test_p4est.jl | 138 +++++++++++++++++++++--------------------- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 62003ae8ee..d5f6509386 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -1,5 +1,5 @@ # TODO we should remove the mixture of indices. Maybe with these: -# - struct FaceIndexBWG ... end +# - struct FacetIndexBWG ... end # - struct QuadrilateralBWG ... end # - struct HexahedronBWG ... end @@ -659,7 +659,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} continue end end - neighbor_candidate = transform_face(forest,k′,f′,leaf) + neighbor_candidate = transform_facet(forest,k′,f′,leaf) # Candidate must be the face opposite to f' f′candidate = ((f′ - 1) ⊻ 1) + 1 fnodes = face(leaf, f , tree.b) @@ -851,7 +851,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim if contains_facet(rf, pface) k′ = face_neighbor_[1][1] ri′ = _perminv[face_neighbor_[1][2]] - interoctree_neighbor = transform_face(forest, k′, ri′, neighbor_candidate) + interoctree_neighbor = transform_facet(forest, k′, ri′, neighbor_candidate) interoctree_neighbor_candidate_idx = findfirst(x->x==interoctree_neighbor,forest.cells[k′].leaves) if interoctree_neighbor_candidate_idx !== nothing r = compute_face_orientation(forest,k,pface_i) @@ -937,8 +937,8 @@ end function balance_face(forest,k′,f′,o,s) o.l == 1 && return # no balancing needed for pivot octant level == 1 - o′ = transform_face(forest,k′,f′,o) - s′ = transform_face(forest,k′,f′,s) + o′ = transform_facet(forest,k′,f′,o) + s′ = transform_facet(forest,k′,f′,s) neighbor_tree = forest.cells[k′] if s′ ∉ neighbor_tree.leaves && parent(s′, neighbor_tree.b) ∉ neighbor_tree.leaves if parent(parent(s′,neighbor_tree.b),neighbor_tree.b) ∈ neighbor_tree.leaves @@ -1417,8 +1417,8 @@ function compute_edge_orientation(forest::ForestBWG{<:Any,<:OctreeBWG{3,<:Any,T2 end """ - transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T1,T2} - transform_face_remote(forest::ForestBWG, f::FaceIndex, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T2} + transform_facet_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T1,T2} + transform_facet_remote(forest::ForestBWG, f::FacetIndex, o::OctantBWG{dim,N,T2}) -> OctantBWG{dim,N,T2} Interoctree coordinate transformation of an given octant `o` to the face-neighboring of octree `k` by virtually pushing `o`s coordinate system through `k`s face `f`. Implements Algorithm 8 of [BWG2011](@citet). @@ -1436,7 +1436,7 @@ Consider 4 octrees with a single leaf each and a maximum refinement level of 1 This function transforms octant 1 into the coordinate system of octant 2 by specifying `k=2` and `f=1`. While in the own octree coordinate system octant 1 is at `xyz=(0,0)`, the returned and transformed octant is located at `xyz=(-2,0)` """ -function transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) where {dim,N,T1<:Integer,T2<:Integer} +function transform_facet_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim,N,T2}) where {dim,N,T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) _perm = (dim == 2 ? 𝒱₂_perm : 𝒱₃_perm) @@ -1475,11 +1475,11 @@ function transform_face_remote(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{dim end end -transform_face_remote(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face_remote(forest,f[1],f[2],oct) +transform_facet_remote(forest::ForestBWG,f::FacetIndex,oct::OctantBWG) = transform_facet_remote(forest,f[1],f[2],oct) """ - transform_face(forest::ForestBWG, k', f', o::OctantBWG) -> OctantBWG - transform_face(forest::ForestBWG, f'::FaceIndex, o::OctantBWG) -> OctantBWG + transform_facet(forest::ForestBWG, k', f', o::OctantBWG) -> OctantBWG + transform_facet(forest::ForestBWG, f'::FacetIndex, o::OctantBWG) -> OctantBWG Interoctree coordinate transformation of an given octant `o` that lies outside of the pivot octree `k`, namely in neighbor octree `k'`. However, the coordinate of `o` is given in octree coordinates of `k`. Thus, this algorithm implements the transformation of the octree coordinates of `o` into the octree coordinates of `k'`. @@ -1500,7 +1500,7 @@ Consider 4 octrees with a single leaf each and a maximum refinement level of 1 This function transforms octant 1 into the coordinate system of octant 2 by specifying `k=1` and `f=2`. While from the perspective of octree coordinates `k=2` octant 1 is at `xyz=(-2,0)`, the returned and transformed octant is located at `xyz=(0,0)` """ -function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2}) where {T1<:Integer,T2<:Integer} +function transform_facet(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2}) where {T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) _perm = 𝒱₂_perm @@ -1540,7 +1540,7 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{2,<:Any,T2 return OctantBWG(o.l,(xyz[a[1] + _one],xyz[a[2] + _one])) end -function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{3,<:Any,T2}) where {T1<:Integer,T2<:Integer} +function transform_facet(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{3,<:Any,T2}) where {T1<:Integer,T2<:Integer} _one = one(T2) _two = T2(2) _perm = 𝒱₃_perm @@ -1595,7 +1595,7 @@ function transform_face(forest::ForestBWG, k::T1, f::T1, o::OctantBWG{3,<:Any,T2 # return OctantBWG(o.l,(xyz[a[1] + _one],xyz[a[2] + _one],xyz[a[3] + _one])) end -transform_face(forest::ForestBWG,f::FaceIndex,oct::OctantBWG) = transform_face(forest,f[1],f[2],oct) +transform_facet(forest::ForestBWG,f::FacetIndex,oct::OctantBWG) = transform_facet(forest,f[1],f[2],oct) """ transform_corner(forest,k,c',oct,inside::Bool) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 87d93bdf4c..24d7d8d36a 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -187,23 +187,23 @@ end @test cell isa Ferrite.AMR.OctreeBWG @test cell.leaves[1] == Ferrite.AMR.OctantBWG(2,0,1,cell.b) end - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,1), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(-8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,3), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(4,1), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(-8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(4,3), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) o = adaptive_grid.cells[1].leaves[1] - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(4,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(4,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 9 @@ -234,14 +234,14 @@ end @test cell isa Ferrite.AMR.OctreeBWG @test cell.leaves[1] == Ferrite.AMR.OctantBWG(2,0,1,cell.b) end - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,2), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(4,4), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,2), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(4,2), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(3,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,4), adaptive_grid.cells[3].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(4,4), adaptive_grid.cells[2].leaves[1]) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(2,2), adaptive_grid.cells[4].leaves[1]) == Ferrite.AMR.OctantBWG(0,(0,8)) #@test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(4,4), adaptive_grid.cells[1].leaves[1],false) == Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(1,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,8)) #@test Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.transform_corner_remote(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,-8)) @@ -249,14 +249,14 @@ end #@test Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(3,2), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(2,4), adaptive_grid.cells[1].leaves[1], false) == Ferrite.AMR.OctantBWG(0,(8,-8)) o = adaptive_grid.cells[1].leaves[1] - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(4,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(4,2), o) == Ferrite.AMR.OctantBWG(0,(8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(4,4), o) == Ferrite.AMR.OctantBWG(0,(0,8)) #simple first and second level refinement @@ -285,10 +285,10 @@ end end Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(1,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(1,(0,-4)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,-4)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(1,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(1,(0,-4)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(1,(4,-4)) grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 @@ -322,12 +322,12 @@ end @test length(adaptive_grid.cells[1].leaves) == 7 @test all(getproperty.(adaptive_grid.cells[1].leaves[1:3],:l) .== 1) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[2]) == Ferrite.AMR.OctantBWG(1,(0,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(2,(4,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[3]) == Ferrite.AMR.OctantBWG(1,(0,-4)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,-2)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[2]) == Ferrite.AMR.OctantBWG(1,(0,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[5]) == Ferrite.AMR.OctantBWG(2,(4,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(2,4), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[3]) == Ferrite.AMR.OctantBWG(1,(0,-4)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(3,3), adaptive_grid.cells[1].leaves[7]) == Ferrite.AMR.OctantBWG(2,(6,-2)) grid_new = Ferrite.AMR.creategrid(adaptive_grid) @test length(grid_new.nodes) == 19 @@ -377,31 +377,31 @@ end o = adaptive_grid.cells[1].leaves[1] # faces - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) - @test Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) - - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,1), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,1), o) - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,3), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,3), o) - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,5), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(1,5), o) - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,2), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,2), o) - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,4), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,4), o) - @test_throws BoundsError Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(8,6), o) - @test_throws BoundsError Ferrite.AMR.transform_face_remote(adaptive_grid, FaceIndex(8,6), o) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,2), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,4), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,6), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(-8,0,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,1), o) == Ferrite.AMR.OctantBWG(0,(8,0,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,-8,0)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,3), o) == Ferrite.AMR.OctantBWG(0,(0,8,0)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,-8)) + @test Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,5), o) == Ferrite.AMR.OctantBWG(0,(0,0,8)) + + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,1), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,1), o) + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,3), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,3), o) + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,5), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(1,5), o) + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,2), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,2), o) + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,4), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,4), o) + @test_throws BoundsError Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(8,6), o) + @test_throws BoundsError Ferrite.AMR.transform_facet_remote(adaptive_grid, FacetIndex(8,6), o) #corners @test_throws BoundsError Ferrite.AMR.transform_corner(adaptive_grid, VertexIndex(1,1), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,-8)) @@ -514,7 +514,7 @@ end Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[3]) @test adaptive_grid.cells[2].leaves[3+4] == Ferrite.AMR.OctantBWG(2,(0,4,2)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,2), adaptive_grid.cells[2].leaves[3+4]) == Ferrite.AMR.OctantBWG(2,(8,4,2)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,2), adaptive_grid.cells[2].leaves[3+4]) == Ferrite.AMR.OctantBWG(2,(8,4,2)) # (b) Rotate elements topologically grid.cells[1] = Hexahedron((grid.cells[1].nodes[2], grid.cells[1].nodes[3], grid.cells[1].nodes[4], grid.cells[1].nodes[1], grid.cells[1].nodes[6], grid.cells[1].nodes[7], grid.cells[1].nodes[8], grid.cells[1].nodes[5])) grid.cells[2] = Hexahedron((grid.cells[2].nodes[4], grid.cells[2].nodes[1], grid.cells[2].nodes[2], grid.cells[2].nodes[3], grid.cells[2].nodes[8], grid.cells[2].nodes[5], grid.cells[2].nodes[6], grid.cells[2].nodes[7])) @@ -523,7 +523,7 @@ end Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) Ferrite.AMR.refine!(adaptive_grid.cells[2],adaptive_grid.cells[2].leaves[1]) @test adaptive_grid.cells[2].leaves[6] == Ferrite.AMR.OctantBWG(2,(2,0,2)) - @test Ferrite.AMR.transform_face(adaptive_grid, FaceIndex(1,3), adaptive_grid.cells[2].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2,2)) + @test Ferrite.AMR.transform_facet(adaptive_grid, FacetIndex(1,3), adaptive_grid.cells[2].leaves[6]) == Ferrite.AMR.OctantBWG(2,(4,-2,2)) end @testset "ForestBWG AbstractGrid Interfacing" begin From f07da5f6322e9f377531e1f06e934de16f166a88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 27 May 2024 15:44:38 +0200 Subject: [PATCH 133/143] face_neighbor to facet_neighbor --- docs/src/devdocs/AMR.md | 6 +++--- src/Adaptivity/BWG.jl | 22 +++++++++++----------- test/test_p4est.jl | 24 ++++++++++++------------ test/test_p4est_example.jl | 2 +- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/docs/src/devdocs/AMR.md b/docs/src/devdocs/AMR.md index 4748a4f70f..049052cc9e 100644 --- a/docs/src/devdocs/AMR.md +++ b/docs/src/devdocs/AMR.md @@ -189,7 +189,7 @@ These operations are useful to collect unique entities within a single octree or ```@docs Ferrite.AMR.corner_neighbor Ferrite.AMR.edge_neighbor -Ferrite.AMR.face_neighbor +Ferrite.AMR.facet_neighbor Ferrite.AMR.possibleneighbors ``` @@ -202,7 +202,7 @@ Thereby, one needs to account for topological connections between the octrees as ```@docs Ferrite.AMR.transform_corner Ferrite.AMR.transform_edge -Ferrite.AMR.transform_face +Ferrite.AMR.transform_facet ``` Note that we flipped the input and to expected output logic a bit to the proposed algorithms of the paper. @@ -211,7 +211,7 @@ However, the original proposed versions are implemented as well in: ```@docs Ferrite.AMR.transform_corner_remote Ferrite.AMR.transform_edge_remote -Ferrite.AMR.transform_face_remote +Ferrite.AMR.transform_facet_remote ``` despite being never used in the code base so far. diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index d5f6509386..d1b2669297 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -820,7 +820,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim parentfaces = faces(parent_,tree.b) for (pface_i, pface) in enumerate(parentfaces) if iscenter(c,pface) #hanging node candidate - neighbor_candidate = face_neighbor(parent_, pface_i, tree.b) + neighbor_candidate = facet_neighbor(parent_, pface_i, tree.b) if inside(tree,neighbor_candidate) #intraoctree branch neighbor_candidate_idx = findfirst(x->x==neighbor_candidate,tree.leaves) if neighbor_candidate_idx !== nothing @@ -844,13 +844,13 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim end else #interoctree branch for (ri,rf) in enumerate(rootfaces) - face_neighbor_ = facet_neighborhood[k,_perm[ri]] - if length(face_neighbor_) == 0 + facet_neighbor_ = facet_neighborhood[k,_perm[ri]] + if length(facet_neighbor_) == 0 continue end if contains_facet(rf, pface) - k′ = face_neighbor_[1][1] - ri′ = _perminv[face_neighbor_[1][2]] + k′ = facet_neighbor_[1][1] + ri′ = _perminv[facet_neighbor_[1][2]] interoctree_neighbor = transform_facet(forest, k′, ri′, neighbor_candidate) interoctree_neighbor_candidate_idx = findfirst(x->x==interoctree_neighbor,forest.cells[k′].leaves) if interoctree_neighbor_candidate_idx !== nothing @@ -1142,7 +1142,7 @@ function possibleneighbors(o::OctantBWG{2},l,b;insidetree=true) neighbors = ntuple(8) do i if i > 4 j = i - 4 - face_neighbor(o,j,b) + facet_neighbor(o,j,b) else corner_neighbor(o,i,b) end @@ -1162,7 +1162,7 @@ function possibleneighbors(o::OctantBWG{3},l,b;insidetree=true) neighbors = ntuple(26) do i if 8 < i ≤ 14 j = i - 8 - face_neighbor(o,j,b) + facet_neighbor(o,j,b) elseif 14 < i ≤ 26 j = i - 14 edge_neighbor(o,j,b) @@ -1318,7 +1318,7 @@ function descendants(octant::OctantBWG{dim,N,T}, b::Integer=_maxlevel[dim-1]) wh end """ - face_neighbor(octant::OctantBWG{dim,N,T}, f::T, b::T=_maxlevel[2]) -> OctantBWG{3,N,T} + facet_neighbor(octant::OctantBWG{dim,N,T}, f::T, b::T=_maxlevel[2]) -> OctantBWG{dim,N,T} Intraoctree face neighbor for a given faceindex `f` (in p4est, i.e. z order convention) and specified maximum refinement level `b`. Implements Algorithm 5 of [BWG2011](@citet). @@ -1337,7 +1337,7 @@ Then, the computed face neighbor will be octant 2 with `xyz=(1,0)`. Note that the function is not sensitive in terms of leaving the octree boundaries. For the above example, a query for face index 1 (marked as `o`) will return an octant outside of the octree with `xyz=(-1,0)`. """ -function face_neighbor(octant::OctantBWG{3,N,T}, f::T, b::T=_maxlevel[2]) where {N,T<:Integer} +function facet_neighbor(octant::OctantBWG{3,N,T}, f::T, b::T=_maxlevel[2]) where {N,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y,z = octant.xyz @@ -1346,7 +1346,7 @@ function face_neighbor(octant::OctantBWG{3,N,T}, f::T, b::T=_maxlevel[2]) where z += ((f == T(5)) ? -h : ((f == T(6)) ? h : zero(T))) return OctantBWG(l,(x,y,z)) end -function face_neighbor(octant::OctantBWG{2,N,T}, f::T, b::T=_maxlevel[1]) where {N,T<:Integer} +function facet_neighbor(octant::OctantBWG{2,N,T}, f::T, b::T=_maxlevel[1]) where {N,T<:Integer} l = octant.l h = T(_compute_size(b,octant.l)) x,y = octant.xyz @@ -1354,7 +1354,7 @@ function face_neighbor(octant::OctantBWG{2,N,T}, f::T, b::T=_maxlevel[1]) where y += ((f == T(3)) ? -h : ((f == T(4)) ? h : zero(T))) return OctantBWG(l,(x,y)) end -face_neighbor(o::OctantBWG{dim,N,T1}, f::T2, b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = face_neighbor(o,T1(f),T1(b)) +facet_neighbor(o::OctantBWG{dim,N,T1}, f::T2, b::T3) where {dim,N,T1<:Integer,T2<:Integer,T3<:Integer} = facet_neighbor(o,T1(f),T1(b)) reference_faces_bwg(::Type{Ferrite.RefHypercube{2}}) = ((1,3) , (2,4), (1,2), (3,4)) reference_faces_bwg(::Type{Ferrite.RefHypercube{3}}) = ((1,3,5,7) , (2,4,6,8), (1,2,5,6), (3,4,7,8), (1,2,3,4), (5,6,7,8)) # p4est consistent ordering diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 24d7d8d36a..fcbf27f680 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -100,22 +100,22 @@ end @testset "OctantBWG Operations" begin o = Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,0)) - @test Ferrite.AMR.face_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)) - @test Ferrite.AMR.face_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(4,0,0)) - @test Ferrite.AMR.face_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,-2,0)) - @test Ferrite.AMR.face_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,2,0)) - @test Ferrite.AMR.face_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,-2)) - @test Ferrite.AMR.face_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,2)) + @test Ferrite.AMR.facet_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)) + @test Ferrite.AMR.facet_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(4,0,0)) + @test Ferrite.AMR.facet_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,-2,0)) + @test Ferrite.AMR.facet_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,2,0)) + @test Ferrite.AMR.facet_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,-2)) + @test Ferrite.AMR.facet_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,2)) @test Ferrite.AMR.descendants(o,2) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(2,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(3,1,1))) @test Ferrite.AMR.descendants(o,3) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(2,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(5,3,3))) o = Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,0)) - @test Ferrite.AMR.face_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(-2,0,0)) - @test Ferrite.AMR.face_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,0)) - @test Ferrite.AMR.face_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,-2,0)) - @test Ferrite.AMR.face_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,2,0)) - @test Ferrite.AMR.face_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,-2)) - @test Ferrite.AMR.face_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,2)) + @test Ferrite.AMR.facet_neighbor(o,1,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(-2,0,0)) + @test Ferrite.AMR.facet_neighbor(o,2,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(2,0,0)) + @test Ferrite.AMR.facet_neighbor(o,3,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,-2,0)) + @test Ferrite.AMR.facet_neighbor(o,4,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,2,0)) + @test Ferrite.AMR.facet_neighbor(o,5,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,-2)) + @test Ferrite.AMR.facet_neighbor(o,6,2) == Ferrite.AMR.Ferrite.AMR.OctantBWG(1,(0,0,2)) o = Ferrite.AMR.Ferrite.AMR.OctantBWG(0,(0,0,0)) @test Ferrite.AMR.descendants(o,2) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(0,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(2,(3,3,3))) @test Ferrite.AMR.descendants(o,3) == (Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(0,0,0)), Ferrite.AMR.Ferrite.AMR.OctantBWG(3,(7,7,7))) diff --git a/test/test_p4est_example.jl b/test/test_p4est_example.jl index 5f5332e1ac..7168ab9350 100644 --- a/test/test_p4est_example.jl +++ b/test/test_p4est_example.jl @@ -172,7 +172,7 @@ end geometry = ConvergenceTestHelper.get_geometry(interpolation) interpolation_geo = interpolation N = ConvergenceTestHelper.get_N(interpolation) - grid = generate_grid(geometry, ntuple(x->10, Ferrite.getdim(geometry))); + grid = generate_grid(geometry, ntuple(x->10, Ferrite.getrefdim(geometry))); adaptive_grid = ForestBWG(grid,7) # ... a suitable quadrature rule ... qr_order = ConvergenceTestHelper.get_quadrature_order(interpolation) From c12a154aee4b1afad15df750dbbc5ac4ab5cc123 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 10 Jun 2024 10:54:06 +0200 Subject: [PATCH 134/143] include simple maximum marking in elasticity example --- docs/src/literate-tutorials/adaptivity.jl | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 103d62a12b..1d698d6bf1 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,5 +1,5 @@ using Ferrite, FerriteGmsh, SparseArrays -#grid = togrid("l.msh"); +#grid = togrid("docs/src/literate-tutorials/l.msh"); grid = generate_grid(Quadrilateral,(2,2)) grid = ForestBWG(grid,25) Ferrite.refine_all!(grid,1) @@ -148,10 +148,14 @@ function solve_adaptive(initial_grid) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) * getdetJdV(cellvalues_tensorial,q_point) end - if error > 0.01 + push!(error_arr,error) + end + η = maximum(error_arr) + θ = 0.8 + for (cellid,cell_err) in enumerate(error_arr) + if cell_err > θ*η push!(cells_to_refine,cellid) end - push!(error_arr,error) end VTKFile("linear_elasticity-$i", dh) do vtk write_solution(vtk, dh, u) @@ -161,15 +165,10 @@ function solve_adaptive(initial_grid) end Ferrite.refine!(grid,cells_to_refine) - VTKFile("unbalanced.vtu", dh) do vtk - end - Ferrite.balanceforest!(grid) - VTKFile("balanced.vtu", dh) do vtk - end i += 1 - if isempty(cells_to_refine) + if isempty(cells_to_refine) || maximum(error_arr) < 0.01 finished = true end end From fb20338178c462790507ae8777f040b54420fde6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Mon, 10 Jun 2024 11:04:55 +0200 Subject: [PATCH 135/143] add pvd export --- docs/src/literate-tutorials/adaptivity.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 1d698d6bf1..4e0a1bb594 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -132,6 +132,8 @@ function solve_adaptive(initial_grid) finished = false i = 1 grid = initial_grid + transfered_grid = Ferrite.creategrid(grid) + pvd = VTKFileCollection("linear-elasticity.pvd",transfered_grid) while !finished transfered_grid = Ferrite.creategrid(grid) u,dh,ch,cv = solve(transfered_grid) @@ -157,7 +159,7 @@ function solve_adaptive(initial_grid) push!(cells_to_refine,cellid) end end - VTKFile("linear_elasticity-$i", dh) do vtk + addstep!(pvd,i,transfered_grid) do vtk write_solution(vtk, dh, u) write_projection(vtk, projector, σ_dof, "stress") write_cell_data(vtk, getindex.(collect(Iterators.flatten(σ_gp_sc)),1), "stress sc") @@ -172,6 +174,7 @@ function solve_adaptive(initial_grid) finished = true end end + close(pvd) end solve_adaptive(grid) From bc1db4138692a155fece91c846e81288700541fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 12 Jun 2024 09:40:56 +0200 Subject: [PATCH 136/143] change qr for error estimator in elasticity example --- docs/src/literate-tutorials/adaptivity.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index 4e0a1bb594..b7448b2b77 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -92,7 +92,7 @@ end function compute_fluxes(u,dh) ip = Lagrange{RefQuadrilateral, 1}()^2 ## Superconvergent points - qr = QuadratureRule{RefQuadrilateral}(1) + qr = QuadratureRule{RefQuadrilateral}(2) cellvalues_sc = CellValues(qr, ip); ## "Normal" quadrature points for the fluxes qr = QuadratureRule{RefQuadrilateral}(2) @@ -127,7 +127,7 @@ end function solve_adaptive(initial_grid) ip = Lagrange{RefQuadrilateral, 1}() - qr = QuadratureRule{RefQuadrilateral}(1) + qr = QuadratureRule{RefQuadrilateral}(2) cellvalues_tensorial = CellValues(qr, ip); finished = false i = 1 @@ -153,7 +153,7 @@ function solve_adaptive(initial_grid) push!(error_arr,error) end η = maximum(error_arr) - θ = 0.8 + θ = 0.5 for (cellid,cell_err) in enumerate(error_arr) if cell_err > θ*η push!(cells_to_refine,cellid) @@ -170,7 +170,7 @@ function solve_adaptive(initial_grid) Ferrite.balanceforest!(grid) i += 1 - if isempty(cells_to_refine) || maximum(error_arr) < 0.01 + if isempty(cells_to_refine) || maximum(error_arr) < 0.05 finished = true end end From c7468343710e5024c173d5dcea9836c6bf1ee861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Thu, 13 Jun 2024 12:37:58 +0200 Subject: [PATCH 137/143] resolve reported bug by adjusting the nrefcells logic to something that makes actual sense --- src/Adaptivity/BWG.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index d1b2669297..6402eeef59 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -971,6 +971,7 @@ function balanceforest!(forest::ForestBWG{dim}) where dim nrefcells = 0 facet_neighborhood = Ferrite.Ferrite.get_facet_facet_neighborhood(forest) while nrefcells - getncells(forest) != 0 + nrefcells = getncells(forest) for k in 1:length(forest.cells) tree = forest.cells[k] rootfaces = faces(root_,tree.b) @@ -978,7 +979,6 @@ function balanceforest!(forest::ForestBWG{dim}) where dim rootvertices = vertices(root_,tree.b) balanced = balancetree(tree) forest.cells[k] = balanced - nrefcells = getncells(forest) for (o_i, o) in enumerate(forest.cells[k].leaves) ss = possibleneighbors(o,o.l,tree.b,;insidetree=false) isinside = inside.(ss,(tree.b,)) From b7492cf56c6ee612af3306a86d439ead2dcd1689 Mon Sep 17 00:00:00 2001 From: termi-official Date: Mon, 17 Jun 2024 17:55:07 +0200 Subject: [PATCH 138/143] [skip ci] test coverage for failing MWEs in 3D --- test/test_p4est.jl | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index fcbf27f680..6e4662a127 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -507,6 +507,89 @@ end end end + # Single + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[8]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + # Unrefined grid has 5 ^ dim nodes and the refined element introduces 6 face center, 12 edge center and 1 volume center nodes + @test length(transfered_grid.nodes) == 5^3 + (6 + 12 + 1) + # 6 faces and 12 edges of the single refined element induces one hanging node each + @test length(transfered_grid.conformity_info) == 6 + 12 + + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + # Unrefined grid has 5 ^ dim nodes and the refined element introduces 6 face center, 12 edge center and 1 volume center nodes + @test length(transfered_grid.nodes) == 5^3 + (6 + 12 + 1) + # 6 faces and 12 edges of the single refined element induces one hanging node each - minus 3 faces and 3 edges on the outer boundary + @test length(transfered_grid.conformity_info) == 6 + 12- 2*3 + + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[8]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + # Unrefined grid has 5 ^ dim nodes and the refined element introduces 6 face center, 12 edge center and 1 volume center nodes + @test length(transfered_grid.nodes) == 5^3 + (6 + 12 + 1) + # 6 faces and 12 edges of the single refined element induces one hanging node each - minus 3 faces and 3 edges on the outer boundary + @test length(transfered_grid.conformity_info) == 6 + 12 - 2*3 + + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + # Unrefined grid has 5 ^ dim nodes and the refined element introduces 6 face center, 12 edge center and 1 volume center nodes + @test length(transfered_grid.nodes) == 5^3 + (6 + 12 + 1) + # 6 faces and 12 edges of the single refined element induces one hanging node each + @test length(transfered_grid.conformity_info) == 6 + 12 + + # Combined + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + @test length(transfered_grid.nodes) == 5^3 + 2*(6 + 12 + 1) + @test length(transfered_grid.conformity_info) == 2*(6 + 12 + 1) - 2*3 + + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) + @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 + + # Combined + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[1]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) + @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 - 2*3 + + # Combined and rotated + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[6]) + Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[3]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) + @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 - 2*3 + # Reproducer test for Fig.3 BWG 11 grid = generate_grid(Hexahedron,(2,1,1)) # (a) From d50665db4288b09000ea48c73b00339bb9adff29 Mon Sep 17 00:00:00 2001 From: termi-official Date: Tue, 18 Jun 2024 12:55:19 +0200 Subject: [PATCH 139/143] Derp --- test/test_p4est.jl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 6e4662a127..664ad15dbc 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -555,7 +555,7 @@ end transfered_grid = Ferrite.creategrid(adaptive_grid) @test unique(transfered_grid.nodes) == transfered_grid.nodes @test length(transfered_grid.nodes) == 5^3 + 2*(6 + 12 + 1) - @test length(transfered_grid.conformity_info) == 2*(6 + 12 + 1) - 2*3 + @test length(transfered_grid.conformity_info) == 2*(6 + 12) - 2*3 adaptive_grid = ForestBWG(grid,3) Ferrite.AMR.refine_all!(adaptive_grid,1) @@ -563,8 +563,8 @@ end Ferrite.AMR.refine!(adaptive_grid.cells[8],adaptive_grid.cells[8].leaves[1]) transfered_grid = Ferrite.creategrid(adaptive_grid) @test unique(transfered_grid.nodes) == transfered_grid.nodes - @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) - @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 + @test length(transfered_grid.nodes) == 5^3 + 2*(6 + 12 + 1) + @test length(transfered_grid.conformity_info) == 2*(6 + 12) - 2*3 # Combined adaptive_grid = ForestBWG(grid,3) @@ -576,7 +576,7 @@ end transfered_grid = Ferrite.creategrid(adaptive_grid) @test unique(transfered_grid.nodes) == transfered_grid.nodes @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) - @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 - 2*3 + @test length(transfered_grid.conformity_info) == 4*(6 + 12) - 2*3 - 2*3 # Combined and rotated adaptive_grid = ForestBWG(grid,3) @@ -588,7 +588,10 @@ end transfered_grid = Ferrite.creategrid(adaptive_grid) @test unique(transfered_grid.nodes) == transfered_grid.nodes @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) - @test length(transfered_grid.conformity_info) == 4*(6 + 12 + 1) - 2*3 - 2*3 + # 4*(6 + 12) potential hanging nodes + # - 2 shared through common edge + # - 2* (2*3) outer boundary face and edge nodes + @test length(transfered_grid.conformity_info) == 4*(6 + 12) - 2 - 2*3 - 2*3 # Reproducer test for Fig.3 BWG 11 grid = generate_grid(Hexahedron,(2,1,1)) From 8461ed67537fdbcd14886f792b307239708c3c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 19 Jun 2024 07:35:13 +0200 Subject: [PATCH 140/143] typo in hanging nodes; doesn't resolve bug --- src/Adaptivity/BWG.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index 6402eeef59..ee1d798ac6 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -831,7 +831,7 @@ function hangingnodes(forest::ForestBWG{dim}, nodeids, nodeowners) where dim vs = vertices(leaf,tree.b) for ξ ∈ 1:ncorners_face3D c′ = facetable[pface_i, ξ] - if c′ ∉ (c̃,c) + if c′ ∉ (c̃,ci) neighbor_candidate_edges = edges(neighbor_candidate,tree.b) ne = findfirst(x->iscenter(vs[c′],x),neighbor_candidate_edges) if ne !== nothing From 30a3b363803b0f2f954cab34006c4d7c18832a44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 19 Jun 2024 09:46:03 +0200 Subject: [PATCH 141/143] failing example; something fishy in the conformity info --- docs/src/literate-tutorials/adaptivity.jl | 66 ++++++++++++++--------- 1 file changed, 41 insertions(+), 25 deletions(-) diff --git a/docs/src/literate-tutorials/adaptivity.jl b/docs/src/literate-tutorials/adaptivity.jl index b7448b2b77..fae90aeb5e 100644 --- a/docs/src/literate-tutorials/adaptivity.jl +++ b/docs/src/literate-tutorials/adaptivity.jl @@ -1,16 +1,28 @@ using Ferrite, FerriteGmsh, SparseArrays -#grid = togrid("docs/src/literate-tutorials/l.msh"); -grid = generate_grid(Quadrilateral,(2,2)) -grid = ForestBWG(grid,25) -Ferrite.refine_all!(grid,1) -Ferrite.refine_all!(grid,2) +#cells = [Hexahedron((1, 2, 5, 4, 10, 11, 14, 13)),Hexahedron((4, 5, 8, 7, 13, 14, 17, 16)), Hexahedron((3, 4, 7, 6, 12, 13, 16, 15)), Hexahedron((12, 13, 16, 15, 21, 22, 25, 24)), Hexahedron((13, 14, 17, 16, 22, 23, 26, 25)), Hexahedron((10, 11, 14, 13, 19, 20, 23, 22)), Hexahedron((9, 10, 13, 12, 18, 19, 22, 21))] +### beefy L cells = [Hexahedron((4, 5, 8, 7, 13, 14, 17, 16)), Hexahedron((3, 4, 7, 6, 12, 13, 16, 15)), Hexahedron((12, 13, 16, 15, 21, 22, 25, 24)), Hexahedron((13, 14, 17, 16, 22, 23, 26, 25)), Hexahedron((10, 11, 14, 13, 19, 20, 23, 22)), Hexahedron((9, 10, 13, 12, 18, 19, 22, 21))] +### cells = [Hexahedron((3, 4, 7, 6, 12, 13, 16, 15)), Hexahedron((12, 13, 16, 15, 21, 22, 25, 24)), Hexahedron((9, 10, 13, 12, 18, 19, 22, 21))] +#nodes = Node{3, Float64}[Node{3, Float64}(Vec{3}((0.0, -1.0, -1.0))), Node{3, Float64}(Vec{3}((1.0, -1.0, -1.0))), Node{3, Float64}(Vec{3}((-1.0, 0.0, -1.0))), Node{3, Float64}(Vec{3}((0.0, 0.0, -1.0))), Node{3, Float64}(Vec{3}((1.0, 0.0, -1.0))), Node{3, Float64}(Vec{3}((-1.0, 1.0, -1.0))), Node{3, Float64}(Vec{3}((0.0, 1.0, -1.0))), Node{3, Float64}(Vec{3}((1.0, 1.0, -1.0))), Node{3, Float64}(Vec{3}((-1.0, -1.0, 0.0))), Node{3, Float64}(Vec{3}((0.0, -1.0, 0.0))), Node{3, Float64}(Vec{3}((1.0, -1.0, 0.0))), Node{3, Float64}(Vec{3}((-1.0, 0.0, 0.0))), Node{3, Float64}(Vec{3}((0.0, 0.0, 0.0))), Node{3, Float64}(Vec{3}((1.0, 0.0, 0.0))), Node{3, Float64}(Vec{3}((-1.0, 1.0, 0.0))), Node{3, Float64}(Vec{3}((0.0, 1.0, 0.0))), Node{3, Float64}(Vec{3}((1.0, 1.0, 0.0))), Node{3, Float64}(Vec{3}((-1.0, -1.0, 1.0))), Node{3, Float64}(Vec{3}((0.0, -1.0, 1.0))), Node{3, Float64}(Vec{3}((1.0, -1.0, 1.0))), Node{3, Float64}(Vec{3}((-1.0, 0.0, 1.0))), Node{3, Float64}(Vec{3}((0.0, 0.0, 1.0))), Node{3, Float64}(Vec{3}((1.0, 0.0, 1.0))), Node{3, Float64}(Vec{3}((-1.0, 1.0, 1.0))), Node{3, Float64}(Vec{3}((0.0, 1.0, 1.0))), Node{3, Float64}(Vec{3}((1.0, 1.0, 1.0)))] +#grid = Grid(cells,nodes) +#addfacetset!(grid,"front",x->x[2]≈-1) +#addfacetset!(grid,"back",x->x[2]≈1) +#addfacetset!(grid,"left",x->x[3]≈-1) +#addfacetset!(grid,"right",x->x[3]≈1) +#addfacetset!(grid,"top",x->x[1]≈-1) +#addfacetset!(grid,"bottom",x->x[1]≈1) +#addfacetset!(grid,"pull",x->x[1]≈0 && x[2] <= 0.5 && x[3] <= 0.5) +grid = generate_grid(Hexahedron,(2,1,1)) +grid = ForestBWG(grid,20) +#Ferrite.refine_all!(grid,1) +Ferrite.refine!(grid,[1,2]) +Ferrite.balanceforest!(grid) struct Elasticity G::Float64 K::Float64 end -function material_routine(material::Elasticity, ε::SymmetricTensor{2}) +function material_routine(material::Elasticity, ε::SymmetricTensor{dim}) where dim (; G, K) = material stress(ε) = 2G * dev(ε) + K * tr(ε) * one(ε) ∂σ∂ε, σ = gradient(stress, ε, :all) @@ -63,10 +75,10 @@ function assemble_global!(K, f, a, dh, cellvalues, material) end function solve(grid) - dim = 2 + dim = 3 order = 1 - ip = Lagrange{RefQuadrilateral, order}()^dim - qr = QuadratureRule{RefQuadrilateral}(2) + ip = Lagrange{RefHexahedron, order}()^dim + qr = QuadratureRule{RefHexahedron}(2) cellvalues = CellValues(qr, ip); dh = DofHandler(grid) @@ -75,8 +87,12 @@ function solve(grid) ch = ConstraintHandler(dh) add!(ch, Ferrite.ConformityConstraint(:u)) - add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> Vec{2}((0.0,0.0)), [1,2])) - add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 0.01, 2)) + add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> Vec{3}((0.0,0.0,0.0)), [1,2,3])) + add!(ch, Dirichlet(:u, getfacetset(grid, "back"), (x, t) -> Vec{3}((0.0,0.0,0.0)), [1,2,3])) + add!(ch, Dirichlet(:u, getfacetset(grid, "front"), (x, t) -> Vec{3}((0.0,0.0,0.0)), [1,2,3])) + add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> Vec{3}((0.0,0.0,0.0)), [1,2,3])) + add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> Vec{3}((0.0,0.0,0.0)), [1,2,3])) + add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> Vec{3}((-0.1,0.0,0.0)), [1,2,3])) close!(ch); K = create_sparsity_pattern(dh,ch) @@ -89,19 +105,18 @@ function solve(grid) return u,dh,ch,cellvalues end -function compute_fluxes(u,dh) - ip = Lagrange{RefQuadrilateral, 1}()^2 - ## Superconvergent points - qr = QuadratureRule{RefQuadrilateral}(2) +function compute_fluxes(u,dh::DofHandler{dim}) where dim + ip = Lagrange{RefHexahedron, 1}()^dim + qr = QuadratureRule{RefHexahedron}(2) cellvalues_sc = CellValues(qr, ip); ## "Normal" quadrature points for the fluxes - qr = QuadratureRule{RefQuadrilateral}(2) + qr = QuadratureRule{RefHexahedron}(2) cellvalues = CellValues(qr, ip); ## Buffers - σ_gp_sc = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() - σ_gp_sc_loc = Vector{SymmetricTensor{2,2,Float64,3}}() - σ_gp = Vector{Vector{SymmetricTensor{2,2,Float64,3}}}() - σ_gp_loc = Vector{SymmetricTensor{2,2,Float64,3}}() + σ_gp_sc = Vector{Vector{SymmetricTensor{2,dim,Float64,dim*2}}}() + σ_gp_sc_loc = Vector{SymmetricTensor{2,dim,Float64,dim*2}}() + σ_gp = Vector{Vector{SymmetricTensor{2,dim,Float64,dim*2}}}() + σ_gp_loc = Vector{SymmetricTensor{2,dim,Float64,dim*2}}() for (cellid,cell) in enumerate(CellIterator(dh)) reinit!(cellvalues, cell) reinit!(cellvalues_sc, cell) @@ -126,8 +141,8 @@ function compute_fluxes(u,dh) end function solve_adaptive(initial_grid) - ip = Lagrange{RefQuadrilateral, 1}() - qr = QuadratureRule{RefQuadrilateral}(2) + ip = Lagrange{RefHexahedron, 1}() + qr = QuadratureRule{RefHexahedron}(2) cellvalues_tensorial = CellValues(qr, ip); finished = false i = 1 @@ -138,8 +153,8 @@ function solve_adaptive(initial_grid) transfered_grid = Ferrite.creategrid(grid) u,dh,ch,cv = solve(transfered_grid) σ_gp, σ_gp_sc = compute_fluxes(u,dh) - projector = L2Projector(Lagrange{RefQuadrilateral, 1}()^2, transfered_grid) - σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) + projector = L2Projector(Lagrange{RefHexahedron, 1}()^3, transfered_grid) + σ_dof = project(projector, σ_gp, QuadratureRule{RefHexahedron}(2)) cells_to_refine = Int[] error_arr = Float64[] for (cellid,cell) in enumerate(CellIterator(projector.dh)) @@ -148,7 +163,7 @@ function solve_adaptive(initial_grid) error = 0.0 for q_point in 1:getnquadpoints(cellvalues_tensorial) σ_dof_at_sc = function_value(cellvalues_tensorial, q_point, σe) - error += norm((σ_gp_sc[cellid][1] - σ_dof_at_sc )) * getdetJdV(cellvalues_tensorial,q_point) + error += norm((σ_gp_sc[cellid][q_point] - σ_dof_at_sc )) * getdetJdV(cellvalues_tensorial,q_point) end push!(error_arr,error) end @@ -173,6 +188,7 @@ function solve_adaptive(initial_grid) if isempty(cells_to_refine) || maximum(error_arr) < 0.05 finished = true end + break end close(pvd) end From 9b38d6206a88f1de3ff4be63e096ac966027395a Mon Sep 17 00:00:00 2001 From: termi-official Date: Wed, 19 Jun 2024 15:39:27 +0200 Subject: [PATCH 142/143] Fix shared edge node detection. --- src/Adaptivity/AMR.jl | 1 + src/Adaptivity/BWG.jl | 53 ++++++++++++++++++++----------------------- test/test_p4est.jl | 27 +++++++++++++++++++--- 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/src/Adaptivity/AMR.jl b/src/Adaptivity/AMR.jl index b5ce097ae0..ab5eaee2f4 100644 --- a/src/Adaptivity/AMR.jl +++ b/src/Adaptivity/AMR.jl @@ -1,6 +1,7 @@ module AMR using .. Ferrite +import Ferrite: @debug using SparseArrays using OrderedCollections diff --git a/src/Adaptivity/BWG.jl b/src/Adaptivity/BWG.jl index ee1d798ac6..5bea4c4c0e 100644 --- a/src/Adaptivity/BWG.jl +++ b/src/Adaptivity/BWG.jl @@ -583,6 +583,9 @@ function rotation_permutation(f,f′,r,i) return 𝒫[𝒬[ℛ[f,f′],r+1],i] end +p4est_opposite_face_index(f) = ((f - 1) ⊻ 0b1) + 1 +p4est_opposite_edge_index(e) = ((e - 1) ⊻ 0b11) + 1 + #TODO: this function should wrap the LNodes Iterator of [IBWG2015](@citet) """ creategrid(forest::ForestBWG) -> NonConformingGrid @@ -637,7 +640,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} # Face neighbors @debug println("Updating face neighbors for octree $k") for (f,fc) in enumerate(_faces) # f in p4est notation - f_axis_index, f_axis_sign = divrem(f-1,2) + # Skip boundary edges facet_neighbor_ = facet_neighborhood[k,_perm[f]] if length(facet_neighbor_) == 0 continue @@ -645,31 +648,26 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} @debug @assert length(facet_neighbor_) == 1 k′, f′_ferrite = facet_neighbor_[1] f′ = _perminv[f′_ferrite] + @debug println(" Neighboring tree: $k′, face $f′_ferrite (Ferrite)/$f′ (p4est)") if k > k′ # Owner tree′ = forest.cells[k′] for leaf in tree.leaves - if f_axis_sign == 1 # positive face - if leaf.xyz[f_axis_index + 1] < 2^tree.b-2^(tree.b-leaf.l) - @debug println(" Rejecting $leaf") - continue - end - else # negative face - if leaf.xyz[f_axis_index + 1] > 0 - @debug println(" Rejecting $leaf") - continue - end + fnodes = face(leaf, f , tree.b) + if !contains_facet(fc, fnodes) + @debug println(" Rejecting leaf $leaf because its facet $fnodes is not on the octant boundary") + continue end neighbor_candidate = transform_facet(forest,k′,f′,leaf) # Candidate must be the face opposite to f' - f′candidate = ((f′ - 1) ⊻ 1) + 1 - fnodes = face(leaf, f , tree.b) + f′candidate = p4est_opposite_face_index(f′) fnodes_neighbor = face(neighbor_candidate, f′candidate, tree′.b) r = compute_face_orientation(forest,k,f) - @debug println(" Matching $fnodes (local) to $fnodes_neighbor (neighbor)") + @debug println(" Trying to match $fnodes (local) to $fnodes_neighbor (neighbor $neighbor_candidate)") if dim == 2 for i ∈ 1:ncorners_face2D i′ = rotation_permutation(r,i) if haskey(nodeids, (k′,fnodes_neighbor[i′])) + @debug println(" Updating $((k,fnodes[i])) $(nodeids[(k,fnodes[i])]) -> $(nodeids[(k′,fnodes_neighbor[i′])])") nodeids[(k,fnodes[i])] = nodeids[(k′,fnodes_neighbor[i′])] nodeowners[(k,fnodes[i])] = (k′,fnodes_neighbor[i′]) end @@ -678,6 +676,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} for i ∈ 1:ncorners_face3D rotated_ξ = rotation_permutation(f′,f,r,i) if haskey(nodeids, (k′,fnodes_neighbor[i])) + @debug println(" Updating $((k,fnodes[i])) $(nodeids[(k,fnodes[rotated_ξ])]) -> $(nodeids[(k′,fnodes_neighbor[i])])") nodeids[(k,fnodes[rotated_ξ])] = nodeids[(k′,fnodes_neighbor[i])] nodeowners[(k,fnodes[rotated_ξ])] = (k′,fnodes_neighbor[i]) end @@ -691,7 +690,7 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} # edge neighbors @debug println("Updating edge neighbors for octree $k") for (e,ec) in enumerate(edges(root(dim),tree.b)) # e in p4est notation - e_axis_index, e_axis_sign = divrem(e-1,4) #first axis 0 (x), 1 (y), 2(z), second positive or negative direction + # Skip boundary edges edge_neighbor_ = forest.topology.edge_edge_neighbor[k,edge_perm[e]] if length(edge_neighbor_) == 0 continue @@ -699,31 +698,27 @@ function creategrid(forest::ForestBWG{dim,C,T}) where {dim,C,T} @debug @assert length(edge_neighbor_) == 1 k′, e′_ferrite = edge_neighbor_[1] e′ = edge_perm_inv[e′_ferrite] + @debug println(" Neighboring tree: $k′, edge $e′_ferrite (Ferrite)/$e′ (p4est)") if k > k′ # Owner tree′ = forest.cells[k′] for leaf in tree.leaves - #debugging checks; should be inbounds anyway due to iteration - if e_axis_sign == 1 # positive edge - if leaf.xyz[e_axis_index + 1] < 2^tree.b-2^(tree.b-leaf.l) - @debug println(" Rejecting $leaf") - continue - end - else # negative edge - if leaf.xyz[e_axis_index + 1] > 0 - @debug println(" Rejecting $leaf") - continue - end + # First we skip edges which are not on the current edge of the root element + enodes = edge(leaf, e , tree.b) + if !contains_edge(ec, enodes) + @debug println(" Rejecting leaf $leaf because its edge $enodes is not on the octant boundary") + continue end neighbor_candidate = transform_edge(forest,k′,e′,leaf, false) # Candidate must be the edge opposite to e' - e′candidate = ((e′ - 1) ⊻ 1) + 1 - enodes = edge(leaf, e , tree.b) + e′candidate = p4est_opposite_edge_index(e′) + enodes_neighbor = edge(neighbor_candidate, e′candidate, tree′.b) r = compute_edge_orientation(forest,k,e) - @debug println(" Matching $enodes (local) to $enodes_neighbor (neighbor)") + @debug println(" Trying to match $enodes (local) to $enodes_neighbor (neighbor $neighbor_candidate)") for i ∈ 1:ncorners_edge i′ = rotation_permutation(r,i) if haskey(nodeids, (k′,enodes_neighbor[i′])) + @debug println(" Updating $((k,enodes[i])) $(nodeids[(k,enodes[i])]) -> $(nodeids[(k′,enodes_neighbor[i′])])") nodeids[(k,enodes[i])] = nodeids[(k′,enodes_neighbor[i′])] nodeowners[(k,enodes[i])] = (k′,enodes_neighbor[i′]) end diff --git a/test/test_p4est.jl b/test/test_p4est.jl index 664ad15dbc..71f99757ee 100644 --- a/test/test_p4est.jl +++ b/test/test_p4est.jl @@ -448,7 +448,6 @@ end @test Ferrite.AMR.transform_edge_remote(adaptive_grid, EdgeIndex(1,12), o, false) == Ferrite.AMR.OctantBWG(0,(-8,-8,0)) # Rotate three dimensional case - grid = generate_grid(Hexahedron,(2,2,2)) # This is our root mesh top view # x-----------x-----------x # |6 3 5|8 4 7| @@ -467,7 +466,8 @@ end # | | | # |5 3 6|5 3 6| # x-----------x-----------x - # Rotate face topologically + grid = generate_grid(Hexahedron,(2,2,2)) + # Rotate face topologically as decscribed in the ascii picture above grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) grid.cells[7] = Hexahedron((grid.cells[7].nodes[2], grid.cells[7].nodes[3], grid.cells[7].nodes[4], grid.cells[7].nodes[1], grid.cells[7].nodes[4+2], grid.cells[7].nodes[4+3], grid.cells[7].nodes[4+4], grid.cells[7].nodes[4+1])) adaptive_grid = ForestBWG(grid,3) @@ -578,6 +578,24 @@ end @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) @test length(transfered_grid.conformity_info) == 4*(6 + 12) - 2*3 - 2*3 + # Combined and not rotated + adaptive_grid = ForestBWG(grid,3) + Ferrite.AMR.refine_all!(adaptive_grid,1) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[8]) + Ferrite.AMR.refine!(adaptive_grid.cells[1],adaptive_grid.cells[1].leaves[1]) + Ferrite.AMR.refine!(adaptive_grid.cells[6],adaptive_grid.cells[6].leaves[6]) + Ferrite.AMR.refine!(adaptive_grid.cells[6],adaptive_grid.cells[6].leaves[3]) + transfered_grid = Ferrite.creategrid(adaptive_grid) + @test unique(transfered_grid.nodes) == transfered_grid.nodes + # +5^3 on the coarse grid + # +4 refined elements a 6 face nodes, 12 edge nodes and 1 volume nodes + # -1 shared node between tree 1 and 6 + @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) - 1 + # 4*(6 + 12) potential hanging nodes + # - 2 shared through common edge + # - 2* (2*3) outer boundary face and edge nodes + @test length(transfered_grid.conformity_info) == 4*(6 + 12) - 2 - 2*3 - 2*3 + # Combined and rotated adaptive_grid = ForestBWG(grid,3) Ferrite.AMR.refine_all!(adaptive_grid,1) @@ -587,7 +605,10 @@ end Ferrite.AMR.refine!(adaptive_grid.cells[7],adaptive_grid.cells[7].leaves[3]) transfered_grid = Ferrite.creategrid(adaptive_grid) @test unique(transfered_grid.nodes) == transfered_grid.nodes - @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) + # +5^3 on the coarse grid + # +4 refined elements a 6 face nodes, 12 edge nodes and 1 volume nodes + # -1 shared node between tree 1 and 7 + @test length(transfered_grid.nodes) == 5^3 + 4*(6 + 12 + 1) - 1 # 4*(6 + 12) potential hanging nodes # - 2 shared through common edge # - 2* (2*3) outer boundary face and edge nodes From b913af46b2f9257c2ba2ba5f18f1392a288ab8ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20K=C3=B6hler?= Date: Wed, 19 Jun 2024 15:51:20 +0200 Subject: [PATCH 143/143] heat_adaptivity 3D version for debugging --- .../src/literate-tutorials/heat_adaptivity.jl | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/docs/src/literate-tutorials/heat_adaptivity.jl b/docs/src/literate-tutorials/heat_adaptivity.jl index 83c5ef3b86..955c1c2ee6 100644 --- a/docs/src/literate-tutorials/heat_adaptivity.jl +++ b/docs/src/literate-tutorials/heat_adaptivity.jl @@ -1,10 +1,10 @@ using Ferrite, FerriteGmsh, SparseArrays -grid = generate_grid(Quadrilateral, (8,8)); +grid = generate_grid(Hexahedron, (4,4,4)); function random_deformation_field(x) if any(x .≈ -1.0) || any(x .≈ 1.0) return x else - Vec{2}(x .+ (rand(2).-0.5)*0.15) + Vec{3}(x .+ (rand(3).-0.5)*0.15) end end transform_coordinates!(grid, random_deformation_field) @@ -54,10 +54,10 @@ function assemble_global!(K, f, a, dh, cellvalues) end function solve(grid) - dim = 2 + dim = 3 order = 1 - ip = Lagrange{RefQuadrilateral, order}() - qr = QuadratureRule{RefQuadrilateral}(2) + ip = Lagrange{RefHexahedron, order}() + qr = QuadratureRule{RefHexahedron}(2) cellvalues = CellValues(qr, ip); dh = DofHandler(grid) @@ -65,11 +65,11 @@ function solve(grid) close!(dh); ch = ConstraintHandler(dh) - add!(ch, ConformityConstraint(:u)) add!(ch, Dirichlet(:u, getfacetset(grid, "top"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfacetset(grid, "right"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfacetset(grid, "left"), (x, t) -> 0.0)) add!(ch, Dirichlet(:u, getfacetset(grid, "bottom"), (x, t) -> 0.0)) + add!(ch, ConformityConstraint(:u)) close!(ch); K = create_sparsity_pattern(dh,ch) @@ -83,18 +83,18 @@ function solve(grid) end function compute_fluxes(u,dh) - ip = Lagrange{RefQuadrilateral, 1}() + ip = Lagrange{RefHexahedron, 1}() ## Normal quadrature points - qr = QuadratureRule{RefQuadrilateral}(2) + qr = QuadratureRule{RefHexahedron}(2) cellvalues = CellValues(qr, ip); ## Superconvergent point - qr_sc = QuadratureRule{RefQuadrilateral}(1) + qr_sc = QuadratureRule{RefHexahedron}(1) cellvalues_sc = CellValues(qr_sc, ip); ## Buffers - σ_gp = Vector{Vector{Vec{2,Float64}}}() - σ_gp_loc = Vector{Vec{2,Float64}}() - σ_gp_sc = Vector{Vector{Vec{2,Float64}}}() - σ_gp_sc_loc = Vector{Vec{2,Float64}}() + σ_gp = Vector{Vector{Vec{3,Float64}}}() + σ_gp_loc = Vector{Vec{3,Float64}}() + σ_gp_sc = Vector{Vector{Vec{3,Float64}}}() + σ_gp_sc_loc = Vector{Vec{3,Float64}}() for (cellid,cell) in enumerate(CellIterator(dh)) @views ue = u[celldofs(cell)] @@ -118,8 +118,8 @@ function compute_fluxes(u,dh) end function solve_adaptive(initial_grid) - ip = Lagrange{RefQuadrilateral, 1}()^2 - qr_sc = QuadratureRule{RefQuadrilateral}(1) + ip = Lagrange{RefHexahedron, 1}()^3 + qr_sc = QuadratureRule{RefHexahedron}(1) cellvalues_flux = CellValues(qr_sc, ip); finished = false i = 1 @@ -130,8 +130,8 @@ function solve_adaptive(initial_grid) transfered_grid = Ferrite.creategrid(grid) u,dh,ch,cv = solve(transfered_grid) σ_gp, σ_gp_sc = compute_fluxes(u,dh) - projector = L2Projector(Lagrange{RefQuadrilateral, 1}(), transfered_grid) - σ_dof = project(projector, σ_gp, QuadratureRule{RefQuadrilateral}(2)) + projector = L2Projector(Lagrange{RefHexahedron, 1}(), transfered_grid) + σ_dof = project(projector, σ_gp, QuadratureRule{RefHexahedron}(2)) cells_to_refine = Int[] error_arr = Float64[] for (cellid,cell) in enumerate(CellIterator(projector.dh))