Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
lpt_wall_collision_cpu.f90
Go to the documentation of this file.
1! Copyright (c) 2026, The Neko Authors
2! All rights reserved.
3!
4! Redistribution and use in source and binary forms, with or without
5! modification, are permitted provided that the following conditions
6! are met:
7!
8! * Redistributions of source code must retain the above copyright
9! notice, this list of conditions and the following disclaimer.
10!
11! * Redistributions in binary form must reproduce the above
12! copyright notice, this list of conditions and the following
13! disclaimer in the documentation and/or other materials provided
14! with the distribution.
15!
16! * Neither the name of the authors nor the names of its
17! contributors may be used to endorse or promote products derived
18! from this software without specific prior written permission.
19!
20! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21! "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22! LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23! FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24! COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25! INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26! BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27! LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28! CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29! LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30! ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31! POSSIBILITY OF SUCH DAMAGE.
32!
35 use num_types, only : rp
36 use mesh, only : mesh_t
37 use dofmap, only : dofmap_t
38 use coefs, only : coef_t
39 use vector, only : vector_t
40 implicit none
41 private
42
44
45contains
46
54 subroutine lpt_handle_elastic_wall_collisions_cpu(msh, dm_Xh, coef, &
55 wall_facet_mask, el_list, x_old, y_old, z_old, x, y, z, d, u, v, w, &
56 u_lag, v_lag, w_lag, u_laglag, v_laglag, w_laglag, acc_xlag, &
57 acc_ylag, acc_zlag, acc_xlaglag, acc_ylaglag, acc_zlaglag, u_old, &
58 v_old, w_old, acc_x, acc_y, acc_z, lag_len, n)
59 type(mesh_t), intent(in) :: msh
60 type(dofmap_t), target, intent(in) :: dm_xh
61 type(coef_t), intent(in) :: coef
62 logical, intent(in) :: wall_facet_mask(:, :)
63 integer, intent(in) :: el_list(:)
64 type(vector_t), intent(in) :: x_old, y_old, z_old
65 type(vector_t), intent(inout) :: x, y, z
66 type(vector_t), intent(in) :: d
67 type(vector_t), intent(inout) :: u, v, w
68 type(vector_t), intent(inout) :: u_lag, v_lag, w_lag
69 type(vector_t), intent(inout) :: u_laglag, v_laglag, w_laglag
70 type(vector_t), intent(inout) :: acc_xlag, acc_ylag, acc_zlag
71 type(vector_t), intent(inout) :: acc_xlaglag, acc_ylaglag
72 type(vector_t), intent(inout) :: acc_zlaglag
73 type(vector_t), intent(inout) :: u_old, v_old, w_old
74 type(vector_t), intent(inout) :: acc_x, acc_y, acc_z
75 integer, intent(in) :: lag_len
76 integer, intent(in) :: n
77 integer :: i
78 integer :: facet
79 integer :: candidate
80 integer :: el
81 integer :: el_mesh
82 integer :: hit_count
83 integer :: hit_idx
84 integer :: hit_facets(6)
85 real(kind=rp) :: normal(3)
86 real(kind=rp) :: wall_point(3)
87 real(kind=rp) :: radius
88 real(kind=rp), pointer, dimension(:,:,:,:) :: dm_x, dm_y, dm_z
89 integer :: lx
90 integer :: ly
91 integer :: lz
92
93 ! Once per call rather than at setup: wall_facet_normal() below is only
94 ! reached from here, and this routine has no init to hang the check on
95 call coef%require_facets('lpt wall collisions')
96
97 dm_x => dm_xh%x
98 dm_y => dm_xh%y
99 dm_z => dm_xh%z
100 lx = dm_xh%Xh%lx
101 ly = dm_xh%Xh%ly
102 lz = dm_xh%Xh%lz
103
104 do i = 1, n
105 el = el_list(i)
106 if (el .lt. 0) cycle
107 el_mesh = el + 1
108 if (el_mesh .gt. msh%nelv) cycle
109
110 radius = 0.5_rp * d%x(i)
111
112 hit_count = 0
113 do candidate = 1, 2 * msh%gdim
114 if (wall_facet_is_hit(wall_facet_mask, dm_x, dm_y, dm_z, &
115 lx, ly, lz, coef, &
116 x_old%x(i), y_old%x(i), z_old%x(i), x%x(i), y%x(i), &
117 z%x(i), radius, el_mesh, candidate, msh%gdim)) then
118 hit_count = hit_count + 1
119 hit_facets(hit_count) = candidate
120 end if
121 end do
122 if (hit_count .eq. 0) cycle
123
124 do hit_idx = 1, hit_count
125 facet = hit_facets(hit_idx)
126 call wall_facet_normal(coef, lx, ly, lz, el_mesh, facet, normal)
127 if (norm2(normal) .le. epsilon(1.0_rp)) cycle
128
129 call wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el_mesh, &
130 facet, wall_point)
131 call reflect_position(x%x(i), y%x(i), z%x(i), wall_point, &
132 normal, radius)
133 call reflect_vector_components(u%x(i), v%x(i), w%x(i), normal)
134 call reflect_vector_components(u_old%x(i), v_old%x(i), &
135 w_old%x(i), normal)
136 call reflect_vector_components(acc_x%x(i), acc_y%x(i), &
137 acc_z%x(i), normal)
138 if (lag_len .ge. 1) then
139 call reflect_vector_components(u_lag%x(i), v_lag%x(i), &
140 w_lag%x(i), normal)
141 call reflect_vector_components(acc_xlag%x(i), acc_ylag%x(i), &
142 acc_zlag%x(i), normal)
143 end if
144 if (lag_len .ge. 2) then
145 call reflect_vector_components(u_laglag%x(i), v_laglag%x(i), &
146 w_laglag%x(i), normal)
147 call reflect_vector_components(acc_xlaglag%x(i), &
148 acc_ylaglag%x(i), acc_zlaglag%x(i), normal)
149 end if
150 end do
151 end do
153
155 logical function wall_facet_is_hit(wall_facet_mask, dm_x, dm_y, dm_z, &
156 lx, ly, lz, coef, x_old, y_old, z_old, x, y, z, radius, el, facet, &
157 gdim) result(is_hit)
158 logical, intent(in) :: wall_facet_mask(:, :)
159 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_x
160 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_y
161 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_z
162 integer, intent(in) :: lx
163 integer, intent(in) :: ly
164 integer, intent(in) :: lz
165 type(coef_t), intent(in) :: coef
166 real(kind=rp), intent(in) :: x_old
167 real(kind=rp), intent(in) :: y_old
168 real(kind=rp), intent(in) :: z_old
169 real(kind=rp), intent(in) :: x
170 real(kind=rp), intent(in) :: y
171 real(kind=rp), intent(in) :: z
172 real(kind=rp), intent(in) :: radius
173 integer, intent(in) :: el
174 integer, intent(in) :: facet
175 integer, intent(in) :: gdim
176 real(kind=rp), parameter :: tol = 1.0e-8_rp
177 real(kind=rp) :: normal(3)
178 real(kind=rp) :: wall_point(3)
179 real(kind=rp) :: dist_old
180 real(kind=rp) :: dist_new
181 real(kind=rp) :: penetration
182
183 is_hit = .false.
184
185 if (facet .lt. 1 .or. facet .gt. 2 * gdim) return
186 if (.not. wall_facet_mask(facet, el)) return
187
188 call wall_facet_normal(coef, lx, ly, lz, el, facet, normal)
189 if (norm2(normal) .le. epsilon(1.0_rp)) return
190 call wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el, facet, wall_point)
191 dist_old = signed_plane_distance(x_old, y_old, z_old, wall_point, normal)
192 dist_new = signed_plane_distance(x, y, z, wall_point, normal)
193 penetration = dist_new + radius
194
195 if (penetration .le. tol) return
196 if (dist_new .le. dist_old + tol) return
197
198 is_hit = .true.
199 end function wall_facet_is_hit
200
203 subroutine wall_facet_normal(coef, lx, ly, lz, el, facet, normal)
204 type(coef_t), intent(in) :: coef
205 integer, intent(in) :: lx
206 integer, intent(in) :: ly
207 integer, intent(in) :: lz
208 integer, intent(in) :: el
209 integer, intent(in) :: facet
210 real(kind=rp), intent(out) :: normal(3)
211 integer :: ic
212 integer :: jc
213 integer :: kc
214
215 ic = max(1, (lx + 1) / 2)
216 jc = max(1, (ly + 1) / 2)
217 kc = max(1, (lz + 1) / 2)
218
219 select case (facet)
220 case (1, 2)
221 normal = coef%get_normal(1, jc, kc, el, facet)
222 case (3, 4)
223 normal = coef%get_normal(ic, 1, kc, el, facet)
224 case (5, 6)
225 normal = coef%get_normal(ic, jc, 1, el, facet)
226 case default
227 normal = 0.0_rp
228 end select
229 end subroutine wall_facet_normal
230
233 subroutine wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el, facet, &
234 wall_point)
235 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_x, dm_y, dm_z
236 integer, intent(in) :: lx, ly, lz
237 integer, intent(in) :: el
238 integer, intent(in) :: facet
239 real(kind=rp), intent(out) :: wall_point(3)
240 integer :: ic
241 integer :: jc
242 integer :: kc
243
244 ic = max(1, (lx + 1) / 2)
245 jc = max(1, (ly + 1) / 2)
246 kc = max(1, (lz + 1) / 2)
247
248 select case (facet)
249 case (1)
250 wall_point = [dm_x(1, jc, kc, el), dm_y(1, jc, kc, el), &
251 dm_z(1, jc, kc, el)]
252 case (2)
253 wall_point = [dm_x(lx, jc, kc, el), &
254 dm_y(lx, jc, kc, el), &
255 dm_z(lx, jc, kc, el)]
256 case (3)
257 wall_point = [dm_x(ic, 1, kc, el), dm_y(ic, 1, kc, el), &
258 dm_z(ic, 1, kc, el)]
259 case (4)
260 wall_point = [dm_x(ic, ly, kc, el), &
261 dm_y(ic, ly, kc, el), &
262 dm_z(ic, ly, kc, el)]
263 case (5)
264 wall_point = [dm_x(ic, jc, 1, el), dm_y(ic, jc, 1, el), &
265 dm_z(ic, jc, 1, el)]
266 case (6)
267 wall_point = [dm_x(ic, jc, lz, el), &
268 dm_y(ic, jc, lz, el), &
269 dm_z(ic, jc, lz, el)]
270 case default
271 wall_point = 0.0_rp
272 end select
273 end subroutine wall_facet_center
274
283 subroutine reflect_position(x, y, z, wall_point, normal, radius)
284 real(kind=rp), intent(inout) :: x, y, z
285 real(kind=rp), intent(in) :: wall_point(3)
286 real(kind=rp), intent(in) :: normal(3)
287 real(kind=rp), intent(in) :: radius
288 real(kind=rp) :: nhat(3)
289 real(kind=rp) :: nmag
290 real(kind=rp) :: signed_contact_distance
291
292 nmag = norm2(normal)
293 if (nmag .le. epsilon(1.0_rp)) return
294
295 nhat = normal / nmag
296 signed_contact_distance = dot_product([x, y, z] - wall_point, nhat) + &
297 radius
298 if (signed_contact_distance .le. 0.0_rp) return
299
300 x = x - 2.0_rp * signed_contact_distance * nhat(1)
301 y = y - 2.0_rp * signed_contact_distance * nhat(2)
302 z = z - 2.0_rp * signed_contact_distance * nhat(3)
303 end subroutine reflect_position
304
308 pure real(kind=rp) function signed_plane_distance(x, y, z, wall_point, &
309 normal)
310 real(kind=rp), intent(in) :: x, y, z
311 real(kind=rp), intent(in) :: wall_point(3)
312 real(kind=rp), intent(in) :: normal(3)
313 real(kind=rp) :: nmag
314
315 nmag = norm2(normal)
316 if (nmag .le. epsilon(1.0_rp)) then
317 signed_plane_distance = -huge(1.0_rp)
318 return
319 end if
320
321 signed_plane_distance = dot_product([x, y, z] - wall_point, normal / nmag)
322 end function signed_plane_distance
323
327 pure subroutine reflect_vector(vec, normal)
328 real(kind=rp), intent(inout) :: vec(3)
329 real(kind=rp), intent(in) :: normal(3)
330 real(kind=rp) :: nhat(3)
331 real(kind=rp) :: nmag
332 real(kind=rp) :: vn
333
334 nmag = norm2(normal)
335 if (nmag .le. epsilon(1.0_rp)) return
336
337 nhat = normal / nmag
338 vn = dot_product(vec, nhat)
339 vec = vec - 2.0_rp * vn * nhat
340 end subroutine reflect_vector
341
347 pure subroutine reflect_vector_components(x, y, z, normal)
348 real(kind=rp), intent(inout) :: x
349 real(kind=rp), intent(inout) :: y
350 real(kind=rp), intent(inout) :: z
351 real(kind=rp), intent(in) :: normal(3)
352 real(kind=rp) :: vec(3)
353
354 vec = [x, y, z]
355 call reflect_vector(vec, normal)
356 x = vec(1)
357 y = vec(2)
358 z = vec(3)
359 end subroutine reflect_vector_components
360
361end module lpt_wall_collision_cpu
Coefficients.
Definition coef.f90:34
Defines a mapping of the degrees of freedom.
Definition dofmap.f90:35
CPU implementation for LPT wall-collision handling.
logical function wall_facet_is_hit(wall_facet_mask, dm_x, dm_y, dm_z, lx, ly, lz, coef, x_old, y_old, z_old, x, y, z, radius, el, facet, gdim)
Return true if the particle surface reaches a given wall facet.
subroutine reflect_position(x, y, z, wall_point, normal, radius)
Reflect the particle center across the contact plane located one radius inward from the wall plane.
pure real(kind=rp) function signed_plane_distance(x, y, z, wall_point, normal)
Return signed distance from a point to a wall plane.
pure subroutine reflect_vector(vec, normal)
Reflect a vector across a plane with the supplied normal.
subroutine wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el, facet, wall_point)
Return the facet-center position that matches the reflection normal.
subroutine wall_facet_normal(coef, lx, ly, lz, el, facet, normal)
Use the face-center SEM normal as the reflection normal.
pure subroutine reflect_vector_components(x, y, z, normal)
Reflect vector components across a plane with the supplied normal.
subroutine, public lpt_handle_elastic_wall_collisions_cpu(msh, dm_xh, coef, wall_facet_mask, el_list, x_old, y_old, z_old, x, y, z, d, u, v, w, u_lag, v_lag, w_lag, u_laglag, v_laglag, w_laglag, acc_xlag, acc_ylag, acc_zlag, acc_xlaglag, acc_ylaglag, acc_zlaglag, u_old, v_old, w_old, acc_x, acc_y, acc_z, lag_len, n)
Reflect inertial particles that hit configured wall facets on the CPU.
Defines a mesh.
Definition mesh.f90:34
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:14
Defines a vector.
Definition vector.f90:34
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:93
#define max(a, b)
Definition tensor.cu:40