BlockTree and Petrov Trees
A BlockTree pairs a test tree and a trial tree, built from separate point sets. It is the tree shape for Petrov-Galerkin problems, where the test and trial spaces differ. When test and trial are the same space, build a single TwoNTree instead (the Galerkin case); see Builder Workflow.
using H2Trees
mx = meshsphere(1.0, 0.1)
my = meshsphere(2.0, 0.1)
translate!(my, [6.0, 0.0, 0.0])
tree = buildtree(
vertices(mx),
vertices(my);
builder=BlockTreeBuilder(;
test=TwoNTreeBuilder(; minhalfsize=0.1), trial=TwoNTreeBuilder(; minhalfsize=0.1)
),
)BlockTree(test=TwoNTree{3,Float64}(nodes=74, leaves=57, levels=2:5, root=1), trial=TwoNTree{3,Float64}(nodes=421, leaves=329, levels=1:5, root=1))H2Trees.testtree(tree) / H2Trees.trialtree(tree) give the two sides back. Translations run from the trial tree (aggregation side) to the test tree (disaggregation side).
Why both sides must agree on level scale
test and trial must share minhalfsize and the builder root id: BlockTreeBuilder validates this and throws an ArgumentError otherwise. This does not mean both sides have the same geometric root box: their centers and root halfsizes may differ because the two point sets may occupy different domains. The requirement is that comparable levels use the same box size scale for near/far and translation purposes.
The two point sets are rarely the same physical size, though. To reconcile this without changing minhalfsize, the smaller side is started at a deeper minlevel: its root box is subdivided down until its halfsize matches what the larger side reaches at level 1. Both trees then line up level-for-level from that point on, even though one has more levels overall.
println("test starts at level ", H2Trees.level(H2Trees.testtree(tree), H2Trees.root(H2Trees.testtree(tree))))
println("trial starts at level ", H2Trees.level(H2Trees.trialtree(tree), H2Trees.root(H2Trees.trialtree(tree))))test starts at level 2
trial starts at level 1If you pass an explicit minlevel yourself, it must match the value this level-scale resolution would have picked: a mismatch is an ArgumentError, not a silent override.
using PlotlyJS
traces = [wireframe(skeleton(mx, 1)), wireframe(skeleton(my, 1))]
for node in H2Trees.LevelIterator(H2Trees.testtree(tree), 4)
push!(
traces,
H2Trees.tracecube(H2Trees.testtree(tree), node; mode="lines", line_color=:pink),
)
end
for node in H2Trees.LevelIterator(H2Trees.trialtree(tree), 4)
push!(
traces,
H2Trees.tracecube(H2Trees.trialtree(tree), node; mode="lines", line_color=:blue),
)
end
p = PlotlyJS.plot(
traces,
Layout(;
scene=attr(;
xaxis=attr(; visible=false),
yaxis=attr(; visible=false),
zaxis=attr(; visible=false),
),
showlegend=false,
),
)Test (pink) and trial (blue) boxes at the same level, from independently sized inputs.
See Constructing Petrov Plans for building translation plans on a BlockTree, and Admissibility Diagnostics for validating the result.