Path: blob/main/src/visualization/utilities_p4est_t8code.jl
2055 views
# By default, Julia/LLVM does not use fused multiply-add operations (FMAs).1# Since these FMAs can increase the performance of many numerical algorithms,2# we need to opt-in explicitly.3# See https://ranocha.de/blog/Optimizing_EC_Trixi for further details.4@muladd begin5#! format: noindent67#############################################################################8# 2D910# The P4estMesh and T8codeMesh can make use of the efficient search functionality11# based on the tree structure to speed up the process of finding the12# elements in which the curve points are located.13# We hard-code `Float64` here since some parts will be accessed from a Julia14# function called as callback from C. Thus, we cannot use multiple dispatch15# easily and use concrete types instead. See the comment on16# `SearchPointsInP4estT8codeMesh2DHelper` below.17# TODO: Implement the efficient search functionality also for the 2D T8codeMesh.18function unstructured_2d_to_1d_curve(u, mesh::P4estMesh{2, 2, Float64},19equations, dg::DGSEM, cache,20curve, slice,21point, nvisnodes,22solution_variables)23# TODO: Currently, the efficient search functionality is only implemented24# for linear meshes. If the mesh is not linear, we fall back to the naive25# but general approach.26if length(mesh.nodes) != 227return unstructured_2d_to_1d_curve_general(u, mesh, equations,28dg, cache,29curve, slice,30point, nvisnodes,31solution_variables)32end33# From here on, we know that we only have to deal with a linear mesh.3435# If no curve is defined, create an axis curve.36original_nodes = cache.elements.node_coordinates37if curve === nothing38curve = axis_curve(view(original_nodes, 1, :, :, :),39view(original_nodes, 2, :, :, :),40slice, point, nvisnodes)41end4243# Set up data structure.44@assert size(curve, 1) == 245n_points_curve = size(curve, 2)4647# Get the number of variables after applying the transformation48# to solution variables.49u_node = get_node_vars(u, equations, dg, 1, 1, 1)50var_node = solution_variables(u_node, equations)51n_variables = length(var_node)52data_on_curve = Array{eltype(var_node)}(undef, n_points_curve, n_variables)5354# Iterate over every point on the curve and determine the solution value at55# the given point.56# We can use the efficient search functionality of p4est/t8code to speed up57# the process. However, the logic is only implemented for linear meshes so far.5859# Retrieve the element in which each point on the curve is located as well as60# the local coordinates of the point in the element.61data = search_points_in_p4est_t8code_mesh_2d(mesh, curve)6263# We use the DG interpolation to get the solution value at the point.64# Thus, we first setup some data for interpolation.65nodes = dg.basis.nodes66baryweights = barycentric_weights(nodes)67# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,68# row vectors. We allocate memory here to improve performance.69vandermonde_x = polynomial_interpolation_matrix(nodes, zero(eltype(curve)))70vandermonde_y = similar(vandermonde_x)7172n_nodes = length(nodes)73temp_data = Array{eltype(data_on_curve)}(undef,74n_nodes,75n_variables)76unstructured_data = Array{eltype(data_on_curve)}(undef,77n_nodes, n_nodes,78n_variables)7980for idx_point in eachindex(data)81query = data[idx_point]82element = query.index83@assert query.found84# The normalization in p4est/t8code is [0, 1] but we need [-1, 1] for DGSEM.85normalized_coordinates = 2 * SVector(query.x, query.y) .- 18687# Interpolate to a single point in each element.88# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,89# row vectors.90polynomial_interpolation_matrix!(vandermonde_x, nodes,91normalized_coordinates[1], baryweights)92polynomial_interpolation_matrix!(vandermonde_y, nodes,93normalized_coordinates[2], baryweights)9495# First, we transform the conserved variables `u` to the solution variables96# before interpolation.97for j in eachnode(dg), i in eachnode(dg)98u_node = get_node_vars(u, equations, dg, i, j, element)99val = solution_variables(u_node, equations)100for v in eachindex(val)101unstructured_data[i, j, v] = val[v]102end103end104105# Next, we interpolate the solution variables to the located point.106for v in 1:n_variables107for i in 1:n_nodes108res_i = zero(eltype(temp_data))109for n in 1:n_nodes110res_i = res_i + vandermonde_y[n] * unstructured_data[i, n, v]111end112temp_data[i, v] = res_i113end114res_v = zero(eltype(data_on_curve))115for n in 1:n_nodes116res_v = res_v + vandermonde_x[n] * temp_data[n, v]117end118data_on_curve[idx_point, v] = res_v119end120end121122mesh_vertices_x = nothing123return calc_arc_length(curve), data_on_curve, mesh_vertices_x124end125126# This struct collects a point on the curve and the corresponding element index127# We hard-code `Float64` here since these structs will be accessed from a Julia128# function called as callback from C. Thus, we cannot use multiple dispatch129# easily and use concrete types instead.130struct SearchPointsInP4estT8codeMesh2DHelper131x::Float64132y::Float64133index::Int64134found::Bool135end136137#############################################################################138# Code specialized to the P4estMesh139function search_in_p4est_2d_quadrant_fn(p4est_ptr::Ptr{p4est_t},140which_tree::p4est_topidx_t,141quadrant_ptr::Ptr{p4est_quadrant_t},142local_num::p4est_locidx_t,143null_pointer::Ptr{Cvoid})::Cint144145# Continue the search146return Cint(1)147end148149function search_in_p4est_2d_point_fn(p4est_ptr::Ptr{p4est_t},150which_tree::p4est_topidx_t,151quadrant_ptr::Ptr{p4est_quadrant_t},152local_num::p4est_locidx_t,153query_ptr::Ptr{Cvoid})::Cint154# Currently, we do not store the `mapping` but only the resulting155# `tree_node_coordinates` in the mesh. Thus, we need to use them156# to convert the reference coordinates of the `element` to physical157# coordinates (assuming polydeg == 1) and then check whether the158# coordinates of the points are inside the `element`.159160# Get the references coordinates of the element.161# Note: This assumes that we are in 2D.162quadrant = PointerWrapper(quadrant_ptr)163164level = quadrant.level[]165p4est_root_length = 1 << P4EST_MAXLEVEL166p4est_quad_length = 1 << (P4EST_MAXLEVEL - level)167168x0 = quadrant.x[] / p4est_root_length169y0 = quadrant.y[] / p4est_root_length170171x1 = x0 + p4est_quad_length / p4est_root_length172y1 = y0 + p4est_quad_length / p4est_root_length173174r00 = SVector(x0, y0)175r10 = SVector(x1, y0)176r01 = SVector(x0, y1)177178# Get the bounding physical coordinates of the tree and use them to179# compute the affine transformation.180# Note: This assumes additionally that the polynomial degree is 1.181p4est = PointerWrapper(p4est_ptr)182user_data = Ptr{Float64}(pointer(p4est.user_pointer))183number_of_trees = p4est.connectivity.num_trees[]184tree_node_coordinates = PtrArray(user_data,185(2, 2, 2, number_of_trees))186a0, A = get_affine_transformation(r00, r10, r01,187tree_node_coordinates,188which_tree)189190# Load the query data191query = unsafe_load(Ptr{SearchPointsInP4estT8codeMesh2DHelper}(query_ptr))192193# Do nothing if the point has already been found elsewhere.194if query.found195return Cint(0)196end197198# If the point has not already been found, we check whether it is inside199# the parallelogram defined by the element.200point = SVector(query.x, query.y)201202# Compute coefficients to express the `point` in the basis of the203# parallelogram.204coefficients = A \ (point - a0)205206# Check if the point is inside the parallelogram.207tolerance = 1.0e-13208is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&209-tolerance <= coefficients[2] <= 1 + tolerance210211if is_inside212if local_num >= 0213# If we are in a valid element (leaf of the tree), we store214# the element id and the coefficients of the point in the215# query data structure.216index = local_num + 1217new_query = SearchPointsInP4estT8codeMesh2DHelper(coefficients[1],218coefficients[2],219index,220true)221unsafe_store!(Ptr{SearchPointsInP4estT8codeMesh2DHelper}(query_ptr),222new_query)223end224225return Cint(1)226else227return Cint(0)228end229end230231@inline function get_affine_transformation(r00, r10, r01,232tree_node_coordinates,233which_tree)234# Get the bounding physical coordinates of the tree235t00 = SVector(tree_node_coordinates[1, 1, 1, which_tree + 1],236tree_node_coordinates[2, 1, 1, which_tree + 1])237t10 = SVector(tree_node_coordinates[1, 2, 1, which_tree + 1],238tree_node_coordinates[2, 2, 1, which_tree + 1])239t01 = SVector(tree_node_coordinates[1, 1, 2, which_tree + 1],240tree_node_coordinates[2, 1, 2, which_tree + 1])241242# Transform the reference coordinates to physical coordinates.243# Note: This requires the same assumptions as above (linear hex mesh in 2D)244p00 = t00 +245r00[1] * (t10 - t00) +246r00[2] * (t01 - t00)247p10 = t00 +248r10[1] * (t10 - t00) +249r10[2] * (t01 - t00)250p01 = t00 +251r01[1] * (t10 - t00) +252r01[2] * (t01 - t00)253254# Get the base point a0 and the basis vectors a1, a2 spanning the255# parallelogram in physical coordinates.256# Note: This requires the same assumptions as above.257a0 = p00258a1 = p10 - p00259a2 = p01 - p00260261# Get the transformation matrix A to compute262# the coefficients of the point in the basis of the parallelogram.263A = SMatrix{2, 2}(a1[1], a2[1],264a1[2], a2[2])265266return a0, A267end268269function search_points_in_p4est_t8code_mesh_2d(mesh::P4estMesh,270curve::Array{Float64, 2})271quadrant_fn = @cfunction(search_in_p4est_2d_quadrant_fn,272Cint,273(Ptr{p4est_t}, p4est_topidx_t,274Ptr{p4est_quadrant_t}, p4est_locidx_t,275Ptr{Cvoid}))276point_fn = @cfunction(search_in_p4est_2d_point_fn,277Cint,278(Ptr{p4est_t}, p4est_topidx_t,279Ptr{p4est_quadrant_t}, p4est_locidx_t,280Ptr{Cvoid}))281282data = Vector{SearchPointsInP4estT8codeMesh2DHelper}(undef, size(curve, 2))283for i in eachindex(data)284data[i] = SearchPointsInP4estT8codeMesh2DHelper(curve[1, i],285curve[2, i],286typemin(Int64),287false)288end289queries = sc_array_new_data(pointer(data),290sizeof(eltype(data)),291length(data))292293call_post = 0294mesh.p4est.user_pointer = pointer(mesh.tree_node_coordinates)295p4est_search_local(pointer(mesh.p4est), call_post, quadrant_fn, point_fn,296queries)297298return data299end300301#############################################################################302# 3D303304# The P4estMesh and T8codeMesh can make use of the efficient search functionality305# based on the tree structure to speed up the process of finding the306# elements in which the curve points are located.307# We hard-code `Float64` here since some parts will be accessed from a Julia308# function called as callback from C. Thus, we cannot use multiple dispatch309# easily and use concrete types instead. See the comment on310# `SearchPointsInP4estT8codeMesh3DHelper` below.311function unstructured_3d_to_1d_curve(u,312mesh::Union{P4estMesh{3, 3, Float64},313T8codeMesh{3, Float64}},314equations, dg::DGSEM, cache,315curve, solution_variables)316# TODO: Currently, the efficient search functionality is only implemented317# for linear meshes. If the mesh is not linear, we fall back to the naive318# but general approach.319if length(mesh.nodes) != 2320return unstructured_3d_to_1d_curve_general(u, equations, dg, cache,321curve, solution_variables)322end323# From here on, we know that we only have to deal with a linear mesh.324325# Set up data structure.326@assert size(curve, 1) == 3327n_points_curve = size(curve, 2)328329# Get the number of variables after applying the transformation to solution variables.330u_node = get_node_vars(u, equations, dg, 1, 1, 1, 1)331var_node = solution_variables(u_node, equations)332n_variables = length(var_node)333data_on_curve = Array{eltype(var_node)}(undef, n_points_curve, n_variables)334335# Iterate over every point on the curve and determine the solution value at336# the given point.337# We can use the efficient search functionality of p4est/t8code to speed up338# the process. However, the logic is only implemented for linear meshes so far.339340# Retrieve the element in which each point on the curve is located as well as341# the local coordinates of the point in the element.342data = search_points_in_p4est_t8code_mesh_3d(mesh, curve)343344# We use the DG interpolation to get the solution value at the point.345# Thus, we first setup some data for interpolation.346nodes = dg.basis.nodes347baryweights = barycentric_weights(nodes)348# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,349# row vectors. We allocate memory here to improve performance.350vandermonde_x = polynomial_interpolation_matrix(nodes, zero(eltype(curve)))351vandermonde_y = similar(vandermonde_x)352vandermonde_z = similar(vandermonde_x)353354n_nodes = length(nodes)355temp_data = Array{eltype(data_on_curve)}(undef,356n_nodes, n_nodes + 1,357n_variables)358unstructured_data = Array{eltype(data_on_curve)}(undef,359n_nodes, n_nodes, n_nodes,360n_variables)361362for idx_point in eachindex(data)363query = data[idx_point]364element = query.index365@assert query.found366# The normalization in p4est/t8code is [0, 1] but we need [-1, 1] for DGSEM.367normalized_coordinates = 2 * SVector(query.x, query.y, query.z) .- 1368369# Interpolate to a single point in each element.370# These Vandermonde matrices are really 1×n_nodes matrices, i.e.,371# row vectors.372polynomial_interpolation_matrix!(vandermonde_x, nodes,373normalized_coordinates[1], baryweights)374polynomial_interpolation_matrix!(vandermonde_y, nodes,375normalized_coordinates[2], baryweights)376polynomial_interpolation_matrix!(vandermonde_z, nodes,377normalized_coordinates[3], baryweights)378379# First, we transform the conserved variables `u` to the solution variables380# before interpolation.381for k in eachnode(dg), j in eachnode(dg), i in eachnode(dg)382u_node = get_node_vars(u, equations, dg, i, j, k, element)383val = solution_variables(u_node, equations)384for v in eachindex(val)385unstructured_data[i, j, k, v] = val[v]386end387end388389# Next, we interpolate the solution variables to the located point.390for v in 1:n_variables391for i in 1:n_nodes392for ii in 1:n_nodes393res_ii = zero(eltype(temp_data))394for n in 1:n_nodes395res_ii = res_ii +396vandermonde_z[n] *397unstructured_data[i, ii, n, v]398end399temp_data[i, ii, v] = res_ii400end401res_i = zero(eltype(temp_data))402for n in 1:n_nodes403res_i = res_i + vandermonde_y[n] * temp_data[i, n, v]404end405temp_data[i, n_nodes + 1, v] = res_i406end407res_v = zero(eltype(temp_data))408for n in 1:n_nodes409res_v = res_v + vandermonde_x[n] * temp_data[n, n_nodes + 1, v]410end411data_on_curve[idx_point, v] = res_v412end413end414415mesh_vertices_x = nothing416return calc_arc_length(curve), data_on_curve, mesh_vertices_x417end418419# This struct collects a point on the curve and the corresponding element index420# We hard-code `Float64` here since these structs will be accessed from a Julia421# function called as callback from C. Thus, we cannot use multiple dispatch422# easily and use concrete types instead.423struct SearchPointsInP4estT8codeMesh3DHelper424x::Float64425y::Float64426z::Float64427index::Int64428found::Bool429end430431#############################################################################432# Code specialized to the P4estMesh433function search_in_p4est_3d_quadrant_fn(p4est_ptr::Ptr{p8est_t},434which_tree::p4est_topidx_t,435quadrant_ptr::Ptr{p8est_quadrant_t},436local_num::p4est_locidx_t,437null_pointer::Ptr{Cvoid})::Cint438439# Continue the search440return Cint(1)441end442443function search_in_p4est_3d_point_fn(p4est_ptr::Ptr{p8est_t},444which_tree::p4est_topidx_t,445quadrant_ptr::Ptr{p8est_quadrant_t},446local_num::p4est_locidx_t,447query_ptr::Ptr{Cvoid})::Cint448# Currently, we do not store the `mapping` but only the resulting449# `tree_node_coordinates` in the mesh. Thus, we need to use them450# to convert the reference coordinates of the `element` to physical451# coordinates (assuming polydeg == 1) and then check whether the452# coordinates of the points are inside the `element`.453454# Get the references coordinates of the element.455# Note: This assumes that we are in 3D.456quadrant = PointerWrapper(quadrant_ptr)457458level = quadrant.level[]459p4est_root_length = 1 << P4EST_MAXLEVEL460p4est_quad_length = 1 << (P4EST_MAXLEVEL - level)461462x0 = quadrant.x[] / p4est_root_length463y0 = quadrant.y[] / p4est_root_length464z0 = quadrant.z[] / p4est_root_length465466x1 = x0 + p4est_quad_length / p4est_root_length467y1 = y0 + p4est_quad_length / p4est_root_length468z1 = z0 + p4est_quad_length / p4est_root_length469470r000 = SVector(x0, y0, z0)471r100 = SVector(x1, y0, z0)472r010 = SVector(x0, y1, z0)473r001 = SVector(x0, y0, z1)474475# Get the bounding physical coordinates of the tree and use them to476# compute the affine transformation.477# Note: This assumes additionally that the polynomial degree is 1.478p4est = PointerWrapper(p4est_ptr)479user_data = Ptr{Ptr{Float64}}(pointer(p4est.user_pointer))480number_of_trees = p4est.connectivity.num_trees[]481tree_node_coordinates = PtrArray(unsafe_load(user_data, 1),482(3, 2, 2, 2, number_of_trees))483a0, A = get_affine_transformation(r000, r100, r010, r001,484tree_node_coordinates,485which_tree)486487# Load the query data488query = unsafe_load(Ptr{SearchPointsInP4estT8codeMesh3DHelper}(query_ptr))489490# Do nothing if the point has already been found elsewhere.491if query.found492return Cint(0)493end494495# If the point has not already been found, we check whether it is inside496# the parallelepiped defined by the element.497point = SVector(query.x, query.y, query.z)498499# Compute coefficients to express the `point` in the basis of the500# parallelepiped.501coefficients = A \ (point - a0)502503# Check if the point is inside the parallelepiped.504tolerance = 1.0e-13505is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&506-tolerance <= coefficients[2] <= 1 + tolerance &&507-tolerance <= coefficients[3] <= 1 + tolerance508509if is_inside510if local_num >= 0511# If we are in a valid element (leaf of the tree), we store512# the element id and the coefficients of the point in the513# query data structure.514index = local_num + 1515new_query = SearchPointsInP4estT8codeMesh3DHelper(coefficients[1],516coefficients[2],517coefficients[3],518index,519true)520unsafe_store!(Ptr{SearchPointsInP4estT8codeMesh3DHelper}(query_ptr),521new_query)522end523524return Cint(1)525else526return Cint(0)527end528end529530@inline function get_affine_transformation(r000, r100, r010, r001,531tree_node_coordinates,532which_tree)533# Get the bounding physical coordinates of the tree534t000 = SVector(tree_node_coordinates[1, 1, 1, 1, which_tree + 1],535tree_node_coordinates[2, 1, 1, 1, which_tree + 1],536tree_node_coordinates[3, 1, 1, 1, which_tree + 1])537t100 = SVector(tree_node_coordinates[1, 2, 1, 1, which_tree + 1],538tree_node_coordinates[2, 2, 1, 1, which_tree + 1],539tree_node_coordinates[3, 2, 1, 1, which_tree + 1])540t010 = SVector(tree_node_coordinates[1, 1, 2, 1, which_tree + 1],541tree_node_coordinates[2, 1, 2, 1, which_tree + 1],542tree_node_coordinates[3, 1, 2, 1, which_tree + 1])543t001 = SVector(tree_node_coordinates[1, 1, 1, 2, which_tree + 1],544tree_node_coordinates[2, 1, 1, 2, which_tree + 1],545tree_node_coordinates[3, 1, 1, 2, which_tree + 1])546547# Transform the reference coordinates to physical coordinates.548# Note: This requires the same assumptions as above (linear hex mesh in 3D)549p000 = t000 +550r000[1] * (t100 - t000) +551r000[2] * (t010 - t000) +552r000[3] * (t001 - t000)553p100 = t000 +554r100[1] * (t100 - t000) +555r100[2] * (t010 - t000) +556r100[3] * (t001 - t000)557p010 = t000 +558r010[1] * (t100 - t000) +559r010[2] * (t010 - t000) +560r010[3] * (t001 - t000)561p001 = t000 +562r001[1] * (t100 - t000) +563r001[2] * (t010 - t000) +564r001[3] * (t001 - t000)565566# Get the base point a0 and the basis vectors a1, a2, a3 spanning the567# parallelepiped in physical coordinates.568# Note: This requires the same assumptions as above.569a0 = p000570a1 = p100 - p000571a2 = p010 - p000572a3 = p001 - p000573574# Get the transformation matrix A to compute575# the coefficients of the point in the basis of the parallelepiped.576A = SMatrix{3, 3}(a1[1], a2[1], a3[1],577a1[2], a2[2], a3[2],578a1[3], a2[3], a3[3])579580return a0, A581end582583function search_points_in_p4est_t8code_mesh_3d(mesh::P4estMesh,584curve::Array{Float64, 2})585quadrant_fn = @cfunction(search_in_p4est_3d_quadrant_fn,586Cint,587(Ptr{p8est_t}, p4est_topidx_t,588Ptr{p8est_quadrant_t}, p4est_locidx_t,589Ptr{Cvoid}))590point_fn = @cfunction(search_in_p4est_3d_point_fn,591Cint,592(Ptr{p8est_t}, p4est_topidx_t,593Ptr{p8est_quadrant_t}, p4est_locidx_t,594Ptr{Cvoid}))595596data = Vector{SearchPointsInP4estT8codeMesh3DHelper}(undef, size(curve, 2))597for i in 1:size(curve, 2)598data[i] = SearchPointsInP4estT8codeMesh3DHelper(curve[1, i],599curve[2, i],600curve[3, i],601typemin(Int64),602false)603end604queries = sc_array_new_data(pointer(data),605sizeof(eltype(data)),606length(data))607608temp_vertex = zeros(Float64, 3)609GC.@preserve temp_vertex begin610user_data = [pointer(mesh.tree_node_coordinates),611pointer(temp_vertex)]612613GC.@preserve user_data begin614call_post = 0615mesh.p4est.user_pointer = pointer(user_data)616p8est_search_local(pointer(mesh.p4est), call_post, quadrant_fn, point_fn,617queries)618end619end620621return data622end623624#############################################################################625# Code specialized to the T8codeMesh626627function search_points_in_t8code_mesh_3d_callback_element(forest::t8_forest_t,628ltreeid::t8_locidx_t,629element::Ptr{t8_element_t},630is_leaf::Cint,631leaf_elements::Ptr{t8_element_array_t},632tree_leaf_index::t8_locidx_t)::Cint633# Continue the search634return Cint(1)635end636637function search_points_in_t8code_mesh_3d_callback_query(forest::t8_forest_t,638ltreeid::t8_locidx_t,639element::Ptr{t8_element_t},640is_leaf::Cint,641leaf_elements::Ptr{t8_element_array_t},642tree_leaf_index::t8_locidx_t,643queries_ptr::Ptr{sc_array_t},644query_indices_ptr::Ptr{sc_array_t},645query_matches::Ptr{Cint},646num_active_queries::Csize_t)::Cvoid647# It would be nice if we could just use648# t8_forest_element_points_inside(forest, ltreeid, element, coords,649# num_active_queries, query_matches, tolerance)650# However, this does not work since the coordinates of the points651# on the curve are given in physical coordinates but t8code only652# knows reference coordinates (in [0, 1]).653# Currently, we do not store the `mapping` but only the resulting654# `tree_node_coordinates` in the mesh. Thus, we need to use them655# to convert the reference coordinates of the `element` to physical656# coordinates (assuming polydeg == 1) and then check whether the657# coordinates of the points are inside the `element`.658659# Get the references coordinates of the element.660# Note: This assumes that we are in 3D and that the element is a hexahedron.661user_data = Ptr{Ptr{Float64}}(t8_forest_get_user_data(forest))662vertex = PtrArray(unsafe_load(user_data, 2), (3,))663eclass_scheme = t8_forest_get_eclass_scheme(forest, T8_ECLASS_HEX)664t8_element_vertex_reference_coords(eclass_scheme, element, 0, vertex)665r000 = SVector(vertex[1], vertex[2], vertex[3])666t8_element_vertex_reference_coords(eclass_scheme, element, 1, vertex)667r100 = SVector(vertex[1], vertex[2], vertex[3])668t8_element_vertex_reference_coords(eclass_scheme, element, 2, vertex)669r010 = SVector(vertex[1], vertex[2], vertex[3])670t8_element_vertex_reference_coords(eclass_scheme, element, 4, vertex)671r001 = SVector(vertex[1], vertex[2], vertex[3])672673# Get the bounding physical coordinates of the tree.674# Note: This assumes additionally that the polynomial degree is 1.675number_of_trees = t8_forest_get_num_global_trees(forest)676tree_node_coordinates = PtrArray(unsafe_load(user_data, 1),677(3, 2, 2, 2, number_of_trees))678a0, A = get_affine_transformation(r000, r100, r010, r001,679tree_node_coordinates,680ltreeid)681invA = inv(A)682683# Loop over all points that need to be found684queries = PointerWrapper(queries_ptr)685query_indices = PointerWrapper(query_indices_ptr)686for i in 1:num_active_queries687# t8code uses 0-based indexing, we use 1-based indexing in Julia.688query_index = unsafe_load_sc(Csize_t, query_indices, i) + 1689query = unsafe_load_sc(SearchPointsInP4estT8codeMesh3DHelper, queries,690query_index)691692# Do nothing if the point has already been found elsewhere.693if query.found694unsafe_store!(query_matches, 1, i)695continue696end697698# If the point has not already been found, we check whether it is inside699# the parallelepiped defined by the element.700point = SVector(query.x, query.y, query.z)701702# Compute coefficients to express the `point` in the basis of the703# parallelepiped.704coefficients = invA * (point - a0)705706# Check if the point is inside the parallelepiped.707tolerance = 1.0e-13708is_inside = -tolerance <= coefficients[1] <= 1 + tolerance &&709-tolerance <= coefficients[2] <= 1 + tolerance &&710-tolerance <= coefficients[3] <= 1 + tolerance711712if is_inside713unsafe_store!(query_matches, 1, i)714715if is_leaf == 1716# If we are in a valid element (leaf of the tree), we store717# the element id and the coefficients of the point in the718# query data structure.719index = t8_forest_get_tree_element_offset(forest, ltreeid) +720tree_leaf_index + 1721new_query = SearchPointsInP4estT8codeMesh3DHelper(coefficients[1],722coefficients[2],723coefficients[3],724index,725true)726unsafe_store_sc!(queries, new_query, query_index)727end728else729unsafe_store!(query_matches, 0, i)730end731end732733return nothing734end735736function search_points_in_p4est_t8code_mesh_3d(mesh::T8codeMesh,737curve::Array{Float64, 2})738element_fn = @cfunction(search_points_in_t8code_mesh_3d_callback_element,739Cint,740(t8_forest_t, t8_locidx_t, Ptr{t8_element_t},741Cint, Ptr{t8_element_array_t}, t8_locidx_t))742query_fn = @cfunction(search_points_in_t8code_mesh_3d_callback_query,743Cvoid,744(t8_forest_t, t8_locidx_t, Ptr{t8_element_t},745Cint, Ptr{t8_element_array_t}, t8_locidx_t,746Ptr{sc_array_t}, Ptr{sc_array_t},747Ptr{Cint}, Csize_t))748749data = Vector{SearchPointsInP4estT8codeMesh3DHelper}(undef, size(curve, 2))750for i in eachindex(data)751data[i] = SearchPointsInP4estT8codeMesh3DHelper(curve[1, i],752curve[2, i],753curve[3, i],754typemin(Int64),755false)756end757queries = sc_array_new_data(pointer(data),758sizeof(eltype(data)),759length(data))760761temp_vertex = zeros(Float64, 3)762GC.@preserve temp_vertex begin763user_data = [pointer(mesh.tree_node_coordinates),764pointer(temp_vertex)]765766GC.@preserve user_data begin767t8_forest_set_user_data(pointer(mesh.forest), pointer(user_data))768t8_forest_search(pointer(mesh.forest), element_fn, query_fn, queries)769end770end771772return data773end774end # @muladd775776777