@@ -158,6 +158,45 @@ function get_periodic_coupling_info(
158158end
159159
160160
161+ # compact variant of lazy_interpolate! specialized on ON_FACES interpolations
162+ function interpolate_on_boundaryfaces (
163+ source:: FEVector{Tv, TvG, TiG} ,
164+ give_opposite,
165+ start_cell:: Int = 1 , # TODO we interpolate on the "b_from" side: a proper start cell should be given
166+ eps = 1.0e-13 ,
167+ kwargs... ,
168+ ) where {Tv, TvG, TiG}
169+
170+ # wrap point evaluation into function that is put into normal interpolate!
171+ xgrid = source[1 ]. FES. xgrid
172+ xdim:: Int = size (xgrid[Coordinates], 1 )
173+ PE = PointEvaluator ([(1 , Identity)], source)
174+ xref = zeros (TvG, xdim)
175+ x_source = zeros (TvG, xdim)
176+ CF:: ExtendableGrids.CellFinder{TvG, TiG} = ExtendableGrids. CellFinder (xgrid)
177+ last_cell = zeros (Int, 1 )
178+ last_cell[1 ] = start_cell
179+
180+ function __setstartcell (new)
181+ return last_cell[1 ] = Int (new)
182+ end
183+
184+ function __eval_point (result, qpinfo)
185+ give_opposite (x_source, qpinfo. x)
186+
187+ cell = ExtendableGrids. gFindLocal! (xref, CF, x_source; icellstart = last_cell[1 ], eps)
188+ if cell == 0
189+ @error " boundary coordinate $(qpinfo. x) opposite to $x_source could not be found in the grid"
190+ else
191+ evaluate_bary! (result, PE, xref, cell)
192+ last_cell[1 ] = cell
193+ end
194+ return nothing
195+ end
196+
197+ return __eval_point, __setstartcell
198+ end
199+
161200"""
162201 get_periodic_coupling_matrix(
163202 FES::FESpace,
@@ -178,7 +217,7 @@ Input:
178217 - b_to: boundary region of the grid with dofs to replace the dofs in b_from
179218 - give_opposite! Function in (y,x)
180219 - mask: (optional) vector of masking components
181- . sparsity_tol: threshold for treating an interpolated value as zero
220+ - sparsity_tol: threshold for treating an interpolated value as zero
182221
183222give_opposite!(y,x) has to be defined in a way that for each x ∈ b_from the resulting y is in the opposite boundary.
184223For each x in the grid, the resulting y has to be in the grid, too: incorporate some mirroring of the coordinates.
@@ -193,54 +232,17 @@ Note that A is transposed for efficient col-wise storage.
193232
194233"""
195234function get_periodic_coupling_matrix (
196- FES:: FESpace ,
197- xgrid:: ExtendableGrid ,
235+ FES:: FESpace{Tv} ,
236+ xgrid:: ExtendableGrid{TvG, TiG} ,
198237 b_from,
199238 b_to,
200239 give_opposite!:: Function ;
201240 mask = :auto ,
202241 sparsity_tol = 1.0e-12
203- )
242+ ) where {Tv, TvG, TiG}
204243
205244 @info " Computing periodic coupling matrix. This may take a while."
206245
207- # compact variant of lazy_interpolate! specialized on ON_FACES interpolations
208- function interpolate_on_boundaryfaces (
209- target:: FEVectorBlock{T1, Tv, Ti} ,
210- source,
211- give_opposite,
212- boundary_faces,
213- start_cell = 1 , # TODO we interpolate on the "b_from" side: a proper start cell should be given
214- eps = 1.0e-13 ,
215- kwargs...
216- ) where {T1, Tv, Ti}
217-
218- # wrap point evaluation into function that is put into normal interpolate!
219- xgrid = source[1 ]. FES. xgrid
220- xdim:: Int = size (xgrid[Coordinates], 1 )
221- PE = PointEvaluator ([(1 , Identity)], source)
222- xref = zeros (Tv, xdim)
223- x_source = zeros (Tv, xdim)
224- CF:: ExtendableGrids.CellFinder{Tv, Ti} = ExtendableGrids. CellFinder (xgrid)
225- last_cell = start_cell
226-
227- function eval_point (result, qpinfo)
228- x = qpinfo. x
229- give_opposite (x_source, x)
230-
231- cell = ExtendableGrids. gFindLocal! (xref, CF, x_source; icellstart = last_cell, eps)
232- if cell == 0
233- @error " boundary coordinate $x opposite to $x_source could not be found in the grid"
234- else
235- evaluate_bary! (result, PE, xref, cell)
236- last_cell = cell
237- end
238- return nothing
239- end
240-
241- return interpolate! (target, ON_FACES, eval_point, items = boundary_faces, kwargs... )
242- end
243-
244246 # total number of grid boundary faces
245247 boundary_nodes = xgrid[BFaceNodes]
246248 n_boundary_faces = size (boundary_nodes, 2 )
@@ -267,10 +269,13 @@ function get_periodic_coupling_matrix(
267269 face_numbers_of_bfaces = xgrid[BFaceFaces]
268270
269271 # find all faces in b_to
270- faces_in_b_to = Int[]
272+ faces_in_b_to = TiG[]
273+ faces_in_b_from = TiG[]
271274 for (i, region) in enumerate (boundary_regions)
272275 if region == b_to
273276 push! (faces_in_b_to, face_numbers_of_bfaces[i])
277+ elseif region == b_from
278+ push! (faces_in_b_from, face_numbers_of_bfaces[i])
274279 end
275280 end
276281
@@ -285,6 +290,66 @@ function get_periodic_coupling_matrix(
285290 @assert length (mask) == ncomponents " component mask has to match number of components"
286291 end
287292
293+ # do the intervals a=[a1,a2] and b=[b1,b2] overlap?
294+ safety = 1.0e-12 # we would be sad if we miss an overlap due to rounding errors
295+ do_intervals_overlap (a, b) = a[1 ] ≤ b[2 ] + safety && b[1 ] ≤ a[2 ] + safety
296+
297+ # do the boxes 𝑓 and 𝑔 overlap?
298+ # we provide the Vector of the coordinate intervals
299+ function do_boxes_overlap (box_f:: AbstractVector , box_g:: AbstractVector )
300+ for i in eachindex (box_f)
301+ if ! do_intervals_overlap (box_f[i], box_g[i])
302+ return false
303+ end
304+ end
305+
306+ # all coordinates overlap
307+ return true
308+ end
309+
310+ dummy = zeros (TvG, size (xgrid[Coordinates], 1 ))
311+
312+ # flip a face to the other side using the give_opposite! function
313+ # Warning: this overwrites the face
314+ function transfer_face! (face:: AbstractMatrix )
315+ for i in axes (face, 2 )
316+ @views coord = face[:, i]
317+ give_opposite! (dummy, coord)
318+ @views face[:, i] .= dummy
319+ end
320+ return
321+ end
322+
323+ eval_point, set_start = interpolate_on_boundaryfaces (fe_vector, give_opposite!)
324+
325+ # precompute approximate search region for each boundary face in b_from
326+ search_areas = Dict {TiG, Vector{TiG}} ()
327+ coords = xgrid[Coordinates]
328+ facenodes = xgrid[FaceNodes]
329+ box_from = zeros (TvG, 2 )
330+ for face_from in faces_in_b_from
331+ coords_from = coords[:, facenodes[:, face_from]]
332+
333+ # transfer the coords_from to the other side
334+ transfer_face! (coords_from)
335+
336+ # get the extrama in each component ( = bounding box of the face)
337+ @views box_from = extrema (coords_from, dims = (2 ))[:]
338+
339+ for face_to in faces_in_b_to
340+ @views coords_to = coords[:, facenodes[:, face_to]]
341+ @views box_to = extrema (coords_to, dims = (2 ))[:]
342+
343+ if do_boxes_overlap (box_from, box_to)
344+ if ! haskey (search_areas, face_from)
345+ search_areas[face_from] = []
346+ end
347+ push! (search_areas[face_from], face_to)
348+ end
349+ end
350+ end
351+
352+ # loop over boundary face indices: we need this index for dofs_on_boundary
288353 for i_boundary_face in 1 : n_boundary_faces
289354
290355 # for each boundary face: check if in b_from
@@ -304,11 +369,10 @@ function get_periodic_coupling_matrix(
304369 fe_vector. entries[local_dof] = 1.0
305370
306371 # interpolate on the opposite boundary using x_trafo = give_opposite
307- interpolate_on_boundaryfaces (
372+ interpolate! (
308373 fe_vector_target[1 ],
309- fe_vector,
310- give_opposite!,
311- faces_in_b_to
374+ ON_FACES, eval_point,
375+ items = search_areas[face_numbers_of_bfaces[i_boundary_face]],
312376 )
313377
314378 # deactivate entry
0 commit comments