Neko 1.99.6
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 dm_x => dm_xh%x
94 dm_y => dm_xh%y
95 dm_z => dm_xh%z
96 lx = dm_xh%Xh%lx
97 ly = dm_xh%Xh%ly
98 lz = dm_xh%Xh%lz
99
100 do i = 1, n
101 el = el_list(i)
102 if (el .lt. 0) cycle
103 el_mesh = el + 1
104 if (el_mesh .gt. msh%nelv) cycle
105
106 radius = 0.5_rp * d%x(i)
107
108 hit_count = 0
109 do candidate = 1, 2 * msh%gdim
110 if (wall_facet_is_hit(wall_facet_mask, dm_x, dm_y, dm_z, &
111 lx, ly, lz, coef, &
112 x_old%x(i), y_old%x(i), z_old%x(i), x%x(i), y%x(i), &
113 z%x(i), radius, el_mesh, candidate, msh%gdim)) then
114 hit_count = hit_count + 1
115 hit_facets(hit_count) = candidate
116 end if
117 end do
118 if (hit_count .eq. 0) cycle
119
120 do hit_idx = 1, hit_count
121 facet = hit_facets(hit_idx)
122 call wall_facet_normal(coef, lx, ly, lz, el_mesh, facet, normal)
123 if (norm2(normal) .le. epsilon(1.0_rp)) cycle
124
125 call wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el_mesh, &
126 facet, wall_point)
127 call reflect_position(x%x(i), y%x(i), z%x(i), wall_point, &
128 normal, radius)
129 call reflect_vector_components(u%x(i), v%x(i), w%x(i), normal)
130 call reflect_vector_components(u_old%x(i), v_old%x(i), &
131 w_old%x(i), normal)
132 call reflect_vector_components(acc_x%x(i), acc_y%x(i), &
133 acc_z%x(i), normal)
134 if (lag_len .ge. 1) then
135 call reflect_vector_components(u_lag%x(i), v_lag%x(i), &
136 w_lag%x(i), normal)
137 call reflect_vector_components(acc_xlag%x(i), acc_ylag%x(i), &
138 acc_zlag%x(i), normal)
139 end if
140 if (lag_len .ge. 2) then
141 call reflect_vector_components(u_laglag%x(i), v_laglag%x(i), &
142 w_laglag%x(i), normal)
143 call reflect_vector_components(acc_xlaglag%x(i), &
144 acc_ylaglag%x(i), acc_zlaglag%x(i), normal)
145 end if
146 end do
147 end do
149
151 logical function wall_facet_is_hit(wall_facet_mask, dm_x, dm_y, dm_z, &
152 lx, ly, lz, coef, x_old, y_old, z_old, x, y, z, radius, el, facet, &
153 gdim) result(is_hit)
154 logical, intent(in) :: wall_facet_mask(:, :)
155 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_x
156 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_y
157 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_z
158 integer, intent(in) :: lx
159 integer, intent(in) :: ly
160 integer, intent(in) :: lz
161 type(coef_t), intent(in) :: coef
162 real(kind=rp), intent(in) :: x_old
163 real(kind=rp), intent(in) :: y_old
164 real(kind=rp), intent(in) :: z_old
165 real(kind=rp), intent(in) :: x
166 real(kind=rp), intent(in) :: y
167 real(kind=rp), intent(in) :: z
168 real(kind=rp), intent(in) :: radius
169 integer, intent(in) :: el
170 integer, intent(in) :: facet
171 integer, intent(in) :: gdim
172 real(kind=rp), parameter :: tol = 1.0e-8_rp
173 real(kind=rp) :: normal(3)
174 real(kind=rp) :: wall_point(3)
175 real(kind=rp) :: dist_old
176 real(kind=rp) :: dist_new
177 real(kind=rp) :: penetration
178
179 is_hit = .false.
180
181 if (facet .lt. 1 .or. facet .gt. 2 * gdim) return
182 if (.not. wall_facet_mask(facet, el)) return
183
184 call wall_facet_normal(coef, lx, ly, lz, el, facet, normal)
185 if (norm2(normal) .le. epsilon(1.0_rp)) return
186 call wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el, facet, wall_point)
187 dist_old = signed_plane_distance(x_old, y_old, z_old, wall_point, normal)
188 dist_new = signed_plane_distance(x, y, z, wall_point, normal)
189 penetration = dist_new + radius
190
191 if (penetration .le. tol) return
192 if (dist_new .le. dist_old + tol) return
193
194 is_hit = .true.
195 end function wall_facet_is_hit
196
199 subroutine wall_facet_normal(coef, lx, ly, lz, el, facet, normal)
200 type(coef_t), intent(in) :: coef
201 integer, intent(in) :: lx
202 integer, intent(in) :: ly
203 integer, intent(in) :: lz
204 integer, intent(in) :: el
205 integer, intent(in) :: facet
206 real(kind=rp), intent(out) :: normal(3)
207 integer :: ic
208 integer :: jc
209 integer :: kc
210
211 ic = max(1, (lx + 1) / 2)
212 jc = max(1, (ly + 1) / 2)
213 kc = max(1, (lz + 1) / 2)
214
215 select case (facet)
216 case (1, 2)
217 normal = coef%get_normal(1, jc, kc, el, facet)
218 case (3, 4)
219 normal = coef%get_normal(ic, 1, kc, el, facet)
220 case (5, 6)
221 normal = coef%get_normal(ic, jc, 1, el, facet)
222 case default
223 normal = 0.0_rp
224 end select
225 end subroutine wall_facet_normal
226
229 subroutine wall_facet_center(dm_x, dm_y, dm_z, lx, ly, lz, el, facet, &
230 wall_point)
231 real(kind=rp), pointer, dimension(:,:,:,:), intent(in) :: dm_x, dm_y, dm_z
232 integer, intent(in) :: lx, ly, lz
233 integer, intent(in) :: el
234 integer, intent(in) :: facet
235 real(kind=rp), intent(out) :: wall_point(3)
236 integer :: ic
237 integer :: jc
238 integer :: kc
239
240 ic = max(1, (lx + 1) / 2)
241 jc = max(1, (ly + 1) / 2)
242 kc = max(1, (lz + 1) / 2)
243
244 select case (facet)
245 case (1)
246 wall_point = [dm_x(1, jc, kc, el), dm_y(1, jc, kc, el), &
247 dm_z(1, jc, kc, el)]
248 case (2)
249 wall_point = [dm_x(lx, jc, kc, el), &
250 dm_y(lx, jc, kc, el), &
251 dm_z(lx, jc, kc, el)]
252 case (3)
253 wall_point = [dm_x(ic, 1, kc, el), dm_y(ic, 1, kc, el), &
254 dm_z(ic, 1, kc, el)]
255 case (4)
256 wall_point = [dm_x(ic, ly, kc, el), &
257 dm_y(ic, ly, kc, el), &
258 dm_z(ic, ly, kc, el)]
259 case (5)
260 wall_point = [dm_x(ic, jc, 1, el), dm_y(ic, jc, 1, el), &
261 dm_z(ic, jc, 1, el)]
262 case (6)
263 wall_point = [dm_x(ic, jc, lz, el), &
264 dm_y(ic, jc, lz, el), &
265 dm_z(ic, jc, lz, el)]
266 case default
267 wall_point = 0.0_rp
268 end select
269 end subroutine wall_facet_center
270
279 subroutine reflect_position(x, y, z, wall_point, normal, radius)
280 real(kind=rp), intent(inout) :: x, y, z
281 real(kind=rp), intent(in) :: wall_point(3)
282 real(kind=rp), intent(in) :: normal(3)
283 real(kind=rp), intent(in) :: radius
284 real(kind=rp) :: nhat(3)
285 real(kind=rp) :: nmag
286 real(kind=rp) :: signed_contact_distance
287
288 nmag = norm2(normal)
289 if (nmag .le. epsilon(1.0_rp)) return
290
291 nhat = normal / nmag
292 signed_contact_distance = dot_product([x, y, z] - wall_point, nhat) + &
293 radius
294 if (signed_contact_distance .le. 0.0_rp) return
295
296 x = x - 2.0_rp * signed_contact_distance * nhat(1)
297 y = y - 2.0_rp * signed_contact_distance * nhat(2)
298 z = z - 2.0_rp * signed_contact_distance * nhat(3)
299 end subroutine reflect_position
300
304 pure real(kind=rp) function signed_plane_distance(x, y, z, wall_point, &
305 normal)
306 real(kind=rp), intent(in) :: x, y, z
307 real(kind=rp), intent(in) :: wall_point(3)
308 real(kind=rp), intent(in) :: normal(3)
309 real(kind=rp) :: nmag
310
311 nmag = norm2(normal)
312 if (nmag .le. epsilon(1.0_rp)) then
313 signed_plane_distance = -huge(1.0_rp)
314 return
315 end if
316
317 signed_plane_distance = dot_product([x, y, z] - wall_point, normal / nmag)
318 end function signed_plane_distance
319
323 pure subroutine reflect_vector(vec, normal)
324 real(kind=rp), intent(inout) :: vec(3)
325 real(kind=rp), intent(in) :: normal(3)
326 real(kind=rp) :: nhat(3)
327 real(kind=rp) :: nmag
328 real(kind=rp) :: vn
329
330 nmag = norm2(normal)
331 if (nmag .le. epsilon(1.0_rp)) return
332
333 nhat = normal / nmag
334 vn = dot_product(vec, nhat)
335 vec = vec - 2.0_rp * vn * nhat
336 end subroutine reflect_vector
337
343 pure subroutine reflect_vector_components(x, y, z, normal)
344 real(kind=rp), intent(inout) :: x
345 real(kind=rp), intent(inout) :: y
346 real(kind=rp), intent(inout) :: z
347 real(kind=rp), intent(in) :: normal(3)
348 real(kind=rp) :: vec(3)
349
350 vec = [x, y, z]
351 call reflect_vector(vec, normal)
352 x = vec(1)
353 y = vec(2)
354 z = vec(3)
355 end subroutine reflect_vector_components
356
357end 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:12
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:63
#define max(a, b)
Definition tensor.cu:40