Neko 1.99.5
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
ale_routines_cpu.f90
Go to the documentation of this file.
1! Copyright (c) 2025-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!
33! Routines for expensive ALE calculations on CPU
35 use num_types, only : rp
36 use field, only : field_t
37 use coefs, only : coef_t
38 use math, only : cfill, glimax, rzero
40 use time_state, only : time_state_t
42 use utils, only : neko_error
43 use comm, only : neko_comm
45 use mesh, only : mesh_t
46 use gather_scatter, only : gs_op_min
47 use mpi_f08, only : mpi_wtime, mpi_barrier
48 use logger, only : neko_log, log_size
50 implicit none
51 private
52
54 public :: update_ale_mesh_cpu
56
57contains
58
60 subroutine compute_cheap_dist_cpu(d, coef, msh, zone_indices)
61 real(kind=rp), intent(inout), target :: d(:)
62 type(coef_t), intent(in) :: coef
63 type(mesh_t), intent(in) :: msh
64 type(zero_dirichlet_t) :: bc_wall
65 integer, intent(in) :: zone_indices(:)
66 real(kind=rp), pointer :: d4(:, :, :, :)
67 integer :: i, j, k, e, n
68 integer :: ipass, nchange, max_pass
69 integer :: ii, jj, kk, i0, i1, j0, j1, k0, k1
70 integer :: lx, ly, lz, nel, z_idx
71 integer :: m, idx
72 real(kind=rp) :: dtmp, x1, y1, z1, x2, y2, z2
73 integer :: change_vec(1)
74 logical :: done
75
76 lx = coef%dof%Xh%lx
77 ly = coef%dof%Xh%ly
78 lz = coef%dof%Xh%lz
79 nel = msh%nelv
80 n = size(d)
81 d4(1:lx, 1:ly, 1:lz, 1:nel) => d
82 max_pass = 10000
83
84 !d = huge(0.0_rp)
85 call cfill(d, huge(0.0_rp), n)
86
87 if (size(zone_indices) .gt. 0) then
88 call bc_wall%init_from_components(coef)
89 do k = 1, size(zone_indices)
90 z_idx = zone_indices(k)
91 call bc_wall%mark_zone(msh%labeled_zones(z_idx))
92 end do
93 call bc_wall%finalize()
94 m = bc_wall%msk(0)
95 do i = 1, m
96 idx = bc_wall%msk(i)
97 d(idx) = 0.0_rp
98 end do
99 call bc_wall%free()
100 end if
101
102 ipass = 1
103 done = .false.
104 do while ((ipass .le. max_pass) .and. .not. done)
105 nchange = 0
106 do e = 1, nel
107 do k = 1, lz
108 do j = 1, ly
109 do i = 1, lx
110 x1 = coef%dof%x(i, j, k, e)
111 y1 = coef%dof%y(i, j, k, e)
112 z1 = coef%dof%z(i, j, k, e)
113 i0 = max(1, i - 1)
114 i1 = min(lx, i + 1)
115 j0 = max(1, j - 1)
116 j1 = min(ly, j + 1)
117 k0 = max(1, k - 1)
118 k1 = min(lz, k + 1)
119 do kk = k0, k1
120 do jj = j0, j1
121 do ii = i0, i1
122 if ((ii .eq. i) .and. (jj .eq. j) .and. &
123 (kk .eq. k)) cycle
124 x2 = coef%dof%x(ii, jj, kk, e)
125 y2 = coef%dof%y(ii, jj, kk, e)
126 z2 = coef%dof%z(ii, jj, kk, e)
127 dtmp = d4(ii, jj, kk, e) + &
128 sqrt((x1 - x2)**2 + &
129 (y1 - y2)**2 + (z1 - z2)**2)
130 if (dtmp .lt. d4(i, j, k, e)) then
131 d4(i, j, k, e) = dtmp
132 nchange = nchange + 1
133 end if
134 end do
135 end do
136 end do
137 end do
138 end do
139 end do
140 end do
141 call coef%gs_h%gs_op_vector(d, n, gs_op_min)
142 change_vec(1) = nchange
143 if (glimax(change_vec, 1) .eq. 0) done = .true.
144 ipass = ipass + 1
145 end do
146 end subroutine compute_cheap_dist_cpu
147
151 subroutine compute_cheap_dist_v2_cpu(dist_field, coef, msh, zone_indices)
152 type(field_t), intent(inout) :: dist_field
153 type(coef_t), intent(in) :: coef
154 type(mesh_t), intent(in) :: msh
155 integer, intent(in) :: zone_indices(:)
156 type(zero_dirichlet_t) :: bc_wall
157 integer :: i, j, k, e, n
158 integer :: ipass, nchange, max_pass
159 integer :: ii, jj, kk, i0, i1, j0, j1, k0, k1
160 integer :: lx, ly, lz, nel, z_idx
161 integer :: m, idx, iter, local_iters
162 real(kind=rp) :: dtmp, x1, y1, z1, x2, y2, z2
163 integer :: change_vec(1)
164 logical :: done, changed_local, element_changed_ever
165 character(len=LOG_SIZE) :: log_buf
166
167 lx = coef%dof%Xh%lx
168 ly = coef%dof%Xh%ly
169 lz = coef%dof%Xh%lz
170 nel = msh%nelv
171 n = coef%dof%size()
172 max_pass = 10000
173
174 ! Limit for worst case scenario such that all nodes can propagate
175 ! their values across the element before triggering an MPI call.
176 local_iters = lx + ly + lz
177
178 call cfill(dist_field%x, huge(0.0_rp), n)
179
180 if (size(zone_indices) .gt. 0) then
181 call bc_wall%init_from_components(coef)
182 do k = 1, size(zone_indices)
183 z_idx = zone_indices(k)
184 call bc_wall%mark_zone(msh%labeled_zones(z_idx))
185 end do
186 call bc_wall%finalize()
187 m = bc_wall%msk(0)
188 do i = 1, m
189 idx = bc_wall%msk(i)
190 dist_field%x(idx, 1, 1, 1) = 0.0_rp
191 end do
192 call bc_wall%free()
193 end if
194
195 ipass = 1
196 done = .false.
197 do while ((ipass .le. max_pass) .and. .not. done)
198 nchange = 0
199
200 do e = 1, nel
201 iter = 1
202 element_changed_ever = .false.
203 changed_local = .true.
204 do while (changed_local .and. (iter .le. local_iters))
205
206 changed_local = .false.
207 do k = 1, lz
208 do j = 1, ly
209 do i = 1, lx
210 x1 = coef%dof%x(i, j, k, e)
211 y1 = coef%dof%y(i, j, k, e)
212 z1 = coef%dof%z(i, j, k, e)
213 i0 = max(1, i - 1)
214 i1 = min(lx, i + 1)
215 j0 = max(1, j - 1)
216 j1 = min(ly, j + 1)
217 k0 = max(1, k - 1)
218 k1 = min(lz, k + 1)
219 do kk = k0, k1
220 do jj = j0, j1
221 do ii = i0, i1
222 if ((ii .eq. i) .and. (jj .eq. j) .and. &
223 (kk .eq. k)) cycle
224
225 x2 = coef%dof%x(ii, jj, kk, e)
226 y2 = coef%dof%y(ii, jj, kk, e)
227 z2 = coef%dof%z(ii, jj, kk, e)
228
229 dtmp = dist_field%x(ii, jj, kk, e) + &
230 sqrt((x1 - x2)**2 + &
231 (y1 - y2)**2 + (z1 - z2)**2)
232
233 if (dtmp .lt. dist_field%x(i, j, k, e)) then
234 dist_field%x(i, j, k, e) = dtmp
235 changed_local = .true.
236 end if
237
238 end do
239 end do
240 end do
241 end do
242 end do
243 end do
244 if (changed_local) element_changed_ever = .true.
245 iter = iter + 1
246 end do
247
248 if (element_changed_ever) nchange = nchange + 1
249 end do
250
251 call coef%gs_h%gs_op_vector(dist_field%x, n, gs_op_min)
252 change_vec(1) = nchange
253
254 if (glimax(change_vec, 1) .eq. 0) done = .true.
255 ipass = ipass + 1
256 end do
257
258 if (done) then
259 write(log_buf, '(A, I0, A)') " converged in: ", ipass, " passes"
260 call neko_log%message(log_buf)
261 else
262 write(log_buf, '(A, I0, A)') " reached max passes: ", ipass, &
263 " without convergence"
264 call neko_log%message(log_buf)
265 end if
266 end subroutine compute_cheap_dist_v2_cpu
267
269 subroutine add_kinematics_to_mesh_velocity_cpu(wx, wy, wz, &
270 x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, inital_pivot_loc)
271 type(field_t), intent(inout) :: wx, wy, wz
272 type(field_t), intent(in) :: x_ref, y_ref, z_ref
273 type(field_t), intent(in) :: phi
274 type(coef_t), intent(in) :: coef
275 type(body_kinematics_t), intent(in) :: kinematics
276 real(kind=rp), intent(in) :: inital_pivot_loc(3)
277 real(kind=rp), intent(in) :: rot_mat(3,3)
278 integer :: i, n
279 real(kind=rp) :: rx, ry, rz
280 real(kind=rp) :: v_tan_x, v_tan_y, v_tan_z
281 real(kind=rp) :: dx_ref, dy_ref, dz_ref
282 real(kind=rp) :: rx_target, ry_target, rz_target
283 real(kind=rp) :: p_val
284 n = phi%dof%size()
285 associate(x => coef%dof%x, y => coef%dof%y, z => coef%dof%z)
286 do concurrent(i = 1:n)
287 ! for points on the wall (phi=1) we do this to avoid any
288 ! numeric error due to computation of rotation matrix.
289 ! It ensures, the walls are always where they need to be!
290 if ( abs(phi%x(i, 1, 1, 1) - 1.0_rp) .lt. 1e-6_rp ) then
291
292 rx = x(i, 1, 1, 1) - kinematics%center(1)
293 ry = y(i, 1, 1, 1) - kinematics%center(2)
294 rz = z(i, 1, 1, 1) - kinematics%center(3)
295 v_tan_x = kinematics%vel_ang(2) * rz - kinematics%vel_ang(3) * ry
296 v_tan_y = kinematics%vel_ang(3) * rx - kinematics%vel_ang(1) * rz
297 v_tan_z = kinematics%vel_ang(1) * ry - kinematics%vel_ang(2) * rx
298 wx%x(i, 1, 1, 1) = wx%x(i, 1, 1, 1) + &
299 (kinematics%vel_trans(1) + v_tan_x) * phi%x(i, 1, 1, 1)
300 wy%x(i, 1, 1, 1) = wy%x(i, 1, 1, 1) + &
301 (kinematics%vel_trans(2) + v_tan_y) * phi%x(i, 1, 1, 1)
302 wz%x(i, 1, 1, 1) = wz%x(i, 1, 1, 1) + &
303 (kinematics%vel_trans(3) + v_tan_z) * phi%x(i, 1, 1, 1)
304 else
305 ! For other points we do this to avoid the time-dependnent
306 ! drift in some special cases, which happens due to the nature of
307 ! the ODE that we integrate above!
308 dx_ref = x_ref%x(i, 1, 1, 1) - inital_pivot_loc(1)
309 dy_ref = y_ref%x(i, 1, 1, 1) - inital_pivot_loc(2)
310 dz_ref = z_ref%x(i, 1, 1, 1) - inital_pivot_loc(3)
311
312 ! Rotate to find the "ghost" target vector
313 ! Apply the rotation matrix R(t) to the reference vector
314 rx_target = rot_mat(1,1)*dx_ref + rot_mat(1,2)*dy_ref + &
315 rot_mat(1,3)*dz_ref
316 ry_target = rot_mat(2,1)*dx_ref + rot_mat(2,2)*dy_ref + &
317 rot_mat(2,3)*dz_ref
318 rz_target = rot_mat(3,1)*dx_ref + rot_mat(3,2)*dy_ref + &
319 rot_mat(3,3)*dz_ref
320
321 ! Calculate tangential velocity at the ghost target
322 ! v_tan = Omega \corss R_target
323 v_tan_x = kinematics%vel_ang(2) * rz_target - &
324 kinematics%vel_ang(3) * ry_target
325 v_tan_y = kinematics%vel_ang(3) * rx_target - &
326 kinematics%vel_ang(1) * rz_target
327 v_tan_z = kinematics%vel_ang(1) * ry_target - &
328 kinematics%vel_ang(2) * rx_target
329
330 p_val = phi%x(i, 1, 1, 1)
331
332 ! Total Mesh Velocity
333 wx%x(i, 1, 1, 1) = wx%x(i, 1, 1, 1) + &
334 (kinematics%vel_trans(1) + v_tan_x) * p_val
335 wy%x(i, 1, 1, 1) = wy%x(i, 1, 1, 1) + &
336 (kinematics%vel_trans(2) + v_tan_y) * p_val
337 wz%x(i, 1, 1, 1) = wz%x(i, 1, 1, 1) + &
338 (kinematics%vel_trans(3) + v_tan_z) * p_val
339
340 end if
341 end do
342 end associate
344
346 subroutine update_ale_mesh_cpu(c_Xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, &
347 wm_z_lag, time, nadv, scheme_type)
348 type(coef_t), intent(inout) :: c_xh
349 type(field_t), intent(in) :: wm_x, wm_y, wm_z
350 type(field_series_t), intent(in) :: wm_x_lag, wm_y_lag, wm_z_lag
351 type(time_state_t), intent(in) :: time
352 type(ab_time_scheme_t) :: ab_scheme_obj
353 integer, intent(in) :: nadv
354 integer :: i, j, n
355 character(len=*), intent(in) :: scheme_type
356 real(kind=rp) :: ab_coeffs(4), dt_history(10)
357
358 call rzero(ab_coeffs, 4)
359 if (trim(scheme_type) .eq. 'ab') then
360 dt_history(1) = time%dt
361 dt_history(2) = time%dtlag(1)
362 dt_history(3) = time%dtlag(2)
363 call ab_scheme_obj%compute_coeffs(ab_coeffs, dt_history, nadv)
364 else
365 call neko_error("ALE: Unknown mesh time-integration scheme")
366 end if
367
368 n = c_xh%dof%size()
369
370 do concurrent(i = 1:n)
371 c_xh%dof%x(i, 1, 1, 1) = c_xh%dof%x(i, 1, 1, 1) + &
372 time%dt * ab_coeffs(1) * wm_x%x(i, 1, 1, 1)
373 c_xh%dof%y(i, 1, 1, 1) = c_xh%dof%y(i, 1, 1, 1) + &
374 time%dt * ab_coeffs(1) * wm_y%x(i, 1, 1, 1)
375 c_xh%dof%z(i, 1, 1, 1) = c_xh%dof%z(i, 1, 1, 1) + &
376 time%dt * ab_coeffs(1) * wm_z%x(i, 1, 1, 1)
377
378 do j = 2, nadv
379 c_xh%dof%x(i, 1, 1, 1) = c_xh%dof%x(i, 1, 1, 1) + &
380 time%dt * ab_coeffs(j) * wm_x_lag%lf(j - 1)%x(i, 1, 1, 1)
381 c_xh%dof%y(i, 1, 1, 1) = c_xh%dof%y(i, 1, 1, 1) + &
382 time%dt * ab_coeffs(j) * wm_y_lag%lf(j - 1)%x(i, 1, 1, 1)
383 c_xh%dof%z(i, 1, 1, 1) = c_xh%dof%z(i, 1, 1, 1) + &
384 time%dt * ab_coeffs(j) * wm_z_lag%lf(j - 1)%x(i, 1, 1, 1)
385 end do
386
387 end do
388 end subroutine update_ale_mesh_cpu
389
390end module ale_routines_cpu
Adam-Bashforth scheme for time integration.
Defines data structures and algorithms for configuring, calculating, and time-integrating the rigid-b...
subroutine, public add_kinematics_to_mesh_velocity_cpu(wx, wy, wz, x_ref, y_ref, z_ref, phi, coef, kinematics, rot_mat, inital_pivot_loc)
Adds kinematics to mesh velocity (CPU)
subroutine compute_cheap_dist_cpu(d, coef, msh, zone_indices)
Implementation of cheap_dist in Nek5000 (CPU)
subroutine, public compute_cheap_dist_v2_cpu(dist_field, coef, msh, zone_indices)
Compute cheap_dist field by passing distance information throughout an entire local element before do...
subroutine, public update_ale_mesh_cpu(c_xh, wm_x, wm_y, wm_z, wm_x_lag, wm_y_lag, wm_z_lag, time, nadv, scheme_type)
Updates mesh position by integrating mesh velocity in time using AB (CPU)
Coefficients.
Definition coef.f90:34
Definition comm.F90:1
type(mpi_comm), public neko_comm
MPI communicator.
Definition comm.F90:45
Contains the field_serties_t type.
Defines a field.
Definition field.f90:34
Gather-scatter.
Logging routines.
Definition log.f90:34
type(log_t), public neko_log
Global log stream.
Definition log.f90:80
integer, parameter, public log_size
Definition log.f90:46
Definition math.f90:60
subroutine, public cfill(a, c, n)
Set all elements to a constant c .
Definition math.f90:597
subroutine, public rzero(a, n)
Zero a real vector.
Definition math.f90:235
integer function, public glimax(a, n)
Max of an integer vector of length n.
Definition math.f90:669
Defines a mesh.
Definition mesh.f90:34
integer, parameter, public rp
Global precision used in computations.
Definition num_types.f90:12
Module with things related to the simulation time.
Utilities.
Definition utils.f90:35
Defines a zero-valued Dirichlet boundary condition.
Explicit Adam-Bashforth scheme for time integration.
Calculated Kinematics for a body at current time.
Coefficients defined on a given (mesh, ) tuple. Arrays use indices (i,j,k,e): element e,...
Definition coef.f90:62
Stores a series (sequence) of fields, logically connected to a base field, and arranged according to ...
A struct that contains all info about the time, expand as needed.
Zero-valued Dirichlet boundary condition. Used for no-slip walls, but also for various auxillary cond...
#define max(a, b)
Definition tensor.cu:40